package spinal.lib.bus.regif

import spinal.lib._

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

final case class DocCHeader(name : String,
                            override val prefix: String = "",
                            regType : String = "u32", //unsigned int
                            withshiftmask: Boolean = true,
                            reuseDefDecOldWay: Boolean = false) extends BusIfDoc {
  override val suffix: String = "h"

  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 C-Header [AUTOGENERATE by SpinalHDL]
        | */
        |
        |#ifndef ${guardName}
        |#define ${guardName}
        |
        |${normalRegSlices.map(_.define(maxnamelen, maxshiftlen)).mkString("\n")}
        |
        |${if(!reuseDefDecOldWay) reuseDeclareFunc(reuseGroupsById) else reuseDeclare(reuseGroupsById)}
        |
        |${normalRegSlices.map(_.union).mkString("\n")}
        |
        |${reuseStruct(reuseGroupsById)}
        |#endif /* ${guardName} */
        |""".stripMargin
  }

  @deprecated(message = "use new reuseDeclareFunc instead", since = "2026.12.30")
  def reuseDeclare(lst: Map[String, Map[Int, List[RegSlice]]]) = {

    def base(name: String, t: RegSlice, max: Int) = {
      val alignName = s"%-${max}s".format(t.reuseTag.instName)
      val defineName = s"${name}_base_${alignName}".toUpperCase()
      s"#define ${defineName}  0x${t.reuseTag.baseAddr.hexString()}"
    }

    lst.map{ t =>
      val partName = t._1
//      val decPart: List[RegSlice]  = t._2.head._2
      val decPart: List[RegSlice]  = t._2.toList.sortBy(_._1).head._2
      val heads : List[RegSlice] = t._2.map(_._2.head).toList.sortBy(_.reuseTag.id)

      val instNameMaxLens = heads.map(_.reuseTag.instName.size).max
      val maxnamelen = decPart.map(_.getName().size).max + prefix.length
      val maxshiftlen = decPart.map(t => t.getName().size + t.fdNameLens).max + prefix.length

      s"""
        |/* part '${partName}' declare --> */
        |${heads.map(t => base(partName, t, instNameMaxLens)).mkString("\n")}
        |/* part '${partName}' defines */
        |${decPart.map(_.baseDefine(maxnamelen, maxshiftlen)).mkString("\n")}
        |/* <--- part '${partName}' declare */
        |""".stripMargin
    }.mkString("")
  }

  def reuseDeclareFunc(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  0x${grp.head.addr.hexString()}
           |#define ${grpName.toUpperCase()}${id}_SIZE  0x${gap.hexString()}
           |#define ${grpName.toUpperCase()}${id}_NUM   0x${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 reuseStruct(lst: Map[String, Map[Int, List[RegSlice]]]) = {
    lst.map { t =>
      val partName = t._1
      val decPart: List[RegSlice] = t._2.toList.sortBy(_._2.head.addr).head._2
      s"""/*part '${partName}' start --> */
         |${decPart.map(_.union).mkString("\n")}
         |/*<-- part '${partName}' end*/
         |""".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}0x${reg.getAddr().hexString(16)}${fddefine(maxshiftlen)}""".stripMargin
    }

    def baseDefine(maxreglen: Int, maxshiftlen: Int) = {
      val _tab = " " * (maxreglen - deDupRegName.size)
      s"""#define ${preFixRegName}(base)  ${_tab}base + 0x${(reg.getAddr() - reg.reuseTag.baseAddr).hexString(8)}${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)) + 0x${inAddr.hexString(8)}${fddefine(maxshiftlen)}""".stripMargin
    }

    def union: String = {
      s"""/**
         |  * @union       ${preFixRegName.toLowerCase()}_t
         |  * @address     0x${reg.getAddr().hexString(16)}
         |  * @brief       ${reg.getDoc().replace("\n", "\\n")}
         |  */
         |typedef union {
         |    ${regType} val;
         |    struct {
         |        ${fdUnion(" " * 8)}
         |    } reg;
         |} ${preFixRegName.toLowerCase()}_t;""".stripMargin
    }

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

    def fdUnion(tab: String): String = {
      var i, j = -1
      val fields = reg.getFields()
      val maxlen = fdNameLens
      fields.map(fd => {
        val name = fd.getAccessType match {
          case AccessType.NA => i += 1; s"reserved_${i}"
          case AccessType.ROV => j += 1; s"rov_${j}"
          case _ => fd.getName()
        }
        val _tab = " " * (maxlen - name.size)
        val reset = fd.getAccessType match {
          case AccessType.NA => ""
          case _ => ", reset: 0x" + fd.getResetValue().hexString(fd.getWidth())
        }
        f"""$regType ${name.toLowerCase()}${_tab} : ${fd.getWidth()}%2d; /* ${fd.getAccessType()}${reset}, ${fd.getDoc().replace("\n", "\\n")} */""".stripMargin
      }).mkString("\n" + tab)
    }

    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}0x${mask.hexString(32)} /* ${fd.getName()}, ${fd.getAccessType()}, ${fd.getWidth()} bit */""".stripMargin
        }
      }
    }
  }
}