package spinal.lib.bus.regif

import spinal.lib._
import spinal.lib.bus.regif.AccessType._

import scala.math

final case class DocSVHeader(name : String,
                             override val prefix: String = "",
                             withshiftmask: Boolean = true) extends BusIfDoc {
  override val suffix: String = "svh"

  def guardName : String = s"${name.toUpperCase()}_REGIF_H"

  def body(): String = {
    val maxnamelen = bi.slices.map(_.getName().size).max + prefix.length
    val maxshiftlen = bi.slices.map(t => t.getName().size + t.fdNameLens).max + prefix.length
    val normalRegSlices = bi.regSlicesNotReuse
    val reuseGroupsById = bi.reuseGroupsById
    s"""|/*
        | * ${header}
        | * Reg Interface SV-Header [AUTOGENERATE by SpinalHDL]
        | */
        |
        |`ifndef ${guardName}
        |`define ${guardName}
        |
        |${normalRegSlices.map(_.define(maxnamelen, maxshiftlen)).mkString("\n")}
        |
        |${reuseDeclare(reuseGroupsById)}
        |
        |`endif /* ${guardName} */
        |""".stripMargin
  }

  def reuseDeclare(lst: Map[String, Map[Int, List[RegSlice]]]) = {
    lst.map { t =>
      val grpName = t._1
      val grps: List[List[RegSlice]] = t._2.toList.sortBy(_._1).map(_._2)
      val basePart = bi.groupConsecutiveBlocks(grps).zipWithIndex.map{ case((grp, num, gap), i) =>
        val id = if(i == 0) "" else s"_${grp.head.reuseTag.instName.toUpperCase()}"
        s"""`define ${grpName.toUpperCase()}${id}_BASE  'h${grp.head.addr.hexString()}
           |`define ${grpName.toUpperCase()}${id}_SIZE  'h${gap.hexString()}
           |`define ${grpName.toUpperCase()}${id}_NUM   'h${num}""".stripMargin
      }.mkString("\n")

      val decPart = grps.head
      val maxnamelen = decPart.map(_.getName().size).max + prefix.length
      val maxshiftlen = decPart.map(t => t.getName().size + t.fdNameLens).max + prefix.length
      s"""${basePart}
         |${decPart.map(t => t.baseDefFunc(maxnamelen, maxshiftlen, t.addr - decPart.head.addr)).mkString("\n")}
         |""".stripMargin
    }.mkString("\n")
  }

  def nameDedupliaction(repeat: String, word: String) = word.toUpperCase().replaceAll(repeat.toUpperCase() + "_", "")

  implicit class RegSliceCheadExtend(reg: RegSlice) {
    val deDupRegName = nameDedupliaction(prefix, reg.getName())
    val preFixRegName = (if(prefix.isEmpty) deDupRegName else s"${prefix}_${deDupRegName}").toUpperCase()

    def define(maxreglen: Int, maxshiftlen: Int): String = {
      val _tab = " " * (maxreglen - deDupRegName.size)
      s"""`define ${preFixRegName} ${_tab}'h${reg.getAddr().hexString(16)}${fddefine(maxshiftlen)}""".stripMargin
    }

    def baseDefFunc(maxreglen: Int, maxshiftlen: Int, inAddr: BigInt): String = {
      val _tab = " " * (maxreglen - deDupRegName.size)
      s"""`define ${preFixRegName}(base, size, i)  ${_tab}((base) + ((size) * (i)) + 'h${inAddr.hexString(8)}${fddefine(maxshiftlen)}""".stripMargin
    }

    def fdNameLens = math.max("reserved_0".size, reg.getFields().map(_.getName.size).max)

    def fddefine(maxlen: Int): String = {
      val nmaxlen = maxlen - preFixRegName.size
      if (withshiftmask) {
        val t = reg.getFields().map(t => t.define(preFixRegName, nmaxlen, prefix)).filterNot(_.isEmpty).mkString("\n")
        if (t.isEmpty) "" else "\n" + t
      } else ""
    }
  }

  implicit class FieldCHeadExtend(fd: Field) {
    def define(pre: String, tabn: Int = 0, duplicate: String = ""): String = {
      //add Define  XXX_SHIFT   XXX_MASK  for SW bit operation
      def lsb: Int = fd.getSection().min

      def msb: Int = fd.getSection().max

      def mask = BigInt((1 << fd.getSection().size) - 1) << lsb

      val newfdname = nameDedupliaction(duplicate, fd.getName())
      val _tab = " " * (tabn - newfdname.size)
      fd.getAccessType() match {
        case `NA` | `ROV` => ""
        case `W1S` | `W1C` | `W1T` | `W1P` | `W1CRS` | `W1SRC` | `W1SHS` | `W1CHS` | `W1I` => s"""`define ${pre}_${newfdname}_SHIFT ${_tab}${lsb} /* ${fd.getName()}, ${fd.getAccessType()}, 1 bit */""".stripMargin
        case `W0S` | `W0C` | `W0T` | `W0P` | `W0CRS` | `W0SRC` => s"""`define ${pre}_${newfdname}_SHIFT ${_tab}${lsb} /* ${fd.getName()}, ${fd.getAccessType()}, 1 bit */""".stripMargin
        case _ => {
          if (fd.getSection().size == bi.busDataWidth) "" else if (fd.getName() == "_bm_") "" else
            s"""`define ${pre}_${newfdname}_SHIFT ${_tab}${lsb}
               |`define ${pre}_${newfdname}_MASK  ${_tab}'h${mask.hexString(32)} /* ${fd.getName()}, ${fd.getAccessType()}, ${fd.getWidth()} bit */""".stripMargin
        }
      }
    }
  }
}