package wacc

import scala.collection.mutable
import cats.implicits._
import cats.data.Chain
import cats.effect.IO

object semantics {
  import renamer.{Scope, prepareGlobalScope, renameFunction}
  import typeChecker.checkFuncDecl

  private def checkFunc(
      funcDecl: ast.FuncDecl,
      scope: Scope
  ): IO[(microWacc.FuncDecl, Chain[Error])] = {
    for {
      renamerErrors <- renameFunction(funcDecl, scope)
      (microWaccFunc, typeErrors) = checkFuncDecl(funcDecl)
    } yield (microWaccFunc, renamerErrors ++ typeErrors)
  }

  def check(partialProg: ast.PartialProgram): IO[(microWacc.Program, Chain[Error])] = {
    given scope: Scope = Scope(mutable.Map.empty, Map.empty)

    for {
      (main, chunks, globalErrors) <- prepareGlobalScope(partialProg)
      toRename = (main +: chunks).toList
      res <- toRename
        .zip(scope.subscopes(toRename.size))
        .parTraverse(checkFunc)
      (typedChunks, errors) = res.foldLeft((Chain.empty[microWacc.FuncDecl], Chain.empty[Error])) {
        case ((acc, err), (funcDecl, errors)) =>
          (acc :+ funcDecl, err ++ errors)
      }
      (typedMain, funcs) = typedChunks.uncons match {
        case Some((head, tail)) => (head.body, tail)
        case None               => (Chain.empty, Chain.empty)
      }
    } yield (microWacc.Program(funcs, typedMain)(main.pos), globalErrors ++ errors)
  }

}
