package wacc

import scala.collection.mutable
import cats.data.Chain

private class LabelGenerator {
  import assemblyIR._
  import microWacc.{CallTarget, Ident, Builtin}
  import asmGenerator.escaped

  private val strings = mutable.HashMap[String, String]()
  private var labelVal = -1

  /** Get an arbitrary label. */
  def getLabel(): String = {
    labelVal += 1
    s".L$labelVal"
  }

  private def getLabel(target: CallTarget | RuntimeError): String = target match {
    case Ident(v, guid)    => s"wacc_${v}_$guid"
    case Builtin(name)     => s"_$name"
    case err: RuntimeError => s".L.${err.name}"
  }

  /** Get a named label def for a function or error. */
  def getLabelDef(target: CallTarget | RuntimeError): LabelDef =
    LabelDef(getLabel(target))

  /** Get a named label for a function or error. */
  def getLabelArg(target: CallTarget | RuntimeError): LabelArg =
    LabelArg(getLabel(target))

  /** Get an arbitrary label for a string. */
  def getLabelArg(str: String): LabelArg =
    LabelArg(strings.getOrElseUpdate(str, s".L.str${strings.size}"))

  /** Get a named label for a string. */
  def getLabelArg(src: String, name: String): LabelArg =
    LabelArg(strings.getOrElseUpdate(src, s".L.$name.str${strings.size}"))

  /** Generate the assembly labels for constants that were labelled using the LabelGenerator. */
  def generateConstants: Chain[AsmLine] =
    strings.foldLeft(Chain.empty) { case (acc, (str, label)) =>
      acc ++ Chain(
        LabelDef(label),
        Directive.Asciz(str.escaped)
      )
    }
}
