//| mvnDeps:
//| - com.typesafe:config:1.4.3
package build

import mill._, mill.api._, scalalib._, publish._
import build.project.SpinalVersion

trait SpinalModule extends SbtModule with CrossSbtModule { outer =>
  def scalatestVersion = "3.2.14"
  def scalacOptions = super.scalacOptions() ++ Seq("-unchecked", "-target:jvm-1.8")
  def javacOptions = super.javacOptions() ++ Seq("-source", "1.8", "-target", "1.8")

  val MvnDeps = Seq(
    mvn"org.scala-lang:scala-library:${scalaVersion}",
    mvn"org.scalactic:scalactic::3.2.10",
    mvn"net.java.dev.jna:jna:5.12.1",
    mvn"net.java.dev.jna:jna-platform:5.12.1",
    mvn"org.slf4j:slf4j-api:2.0.5",
    mvn"org.scala-lang.modules::scala-xml:1.3.0"
  )

  object test extends CrossSbtTests with TestModule.ScalaTest {
    def mvnDeps = Seq(mvn"org.scalatest::scalatest::${scalatestVersion}")
  }
  def testOnly(args: String*) = test.testOnly(args*)

  def moduleInfo = SpinalModuleInfo(crossScalaVersion)
  def moduleRefs = moduleInfo._1
  def modulePluginOptions = moduleInfo._2()
}

type SpinalModuleInfoType = Tuple2[Map[String, PublishModule], () => T[Seq[String]]]
object SpinalModuleInfo {
  def apply(crossScalaVersion: String) = (moduleDeps(crossScalaVersion), () => scalacOptions(crossScalaVersion))

  def moduleDeps(crossScalaVersion: String): Map[String, PublishModule] = Map(
    "core"        -> core(crossScalaVersion),
    "lib"         -> lib(crossScalaVersion),
    "sim"         -> sim(crossScalaVersion),
    "idslpayload" -> idslpayload(crossScalaVersion),
    "idslplugin"  -> idslplugin(crossScalaVersion),
    "tester"      -> tester(crossScalaVersion)
  )

  def scalacOptions(crossScalaVersion: String) = idslplugin(crossScalaVersion).pluginOptions
}

trait SpinalPublishModule extends PublishModule {
  def publishVersion = SpinalVersion.all
  def artifactName = "spinalhdl-" + super.artifactName()

  def pomSettings = PomSettings(
    description = "SpinalHDL",
    organization = "com.github.spinalhdl",
    url = "https://github.com/SpinalHDL/SpinalHDL",
    licenses = Seq(License.`LGPL-3.0-or-later`),
    versionControl = VersionControl.github("SpinalHDL", "SpinalHDL"),
    developers = Seq(
      Developer("Dolu1990", "SpinalHDL", "https://github.com/SpinalHDL")
    )
  )
}

object idslpayload extends Cross[IdslPayload](SpinalVersion.compilers)
trait IdslPayload extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.idslpayload")
  override def artifactName = "spinalhdl-idsl-payload"
  def mvnDeps = super.mvnDeps() ++ Seq(mvn"org.scala-lang:scala-reflect:${scalaVersion}")
}

object idslplugin extends Cross[IdslPlugin](SpinalVersion.compilers)
trait IdslPlugin extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.idslplugin")
  override def artifactName = "spinalhdl-idsl-plugin"
  def moduleDeps = Seq(moduleRefs("idslpayload"))
  def mvnDeps = super.mvnDeps() ++ Seq(mvn"org.scala-lang:scala-compiler:${scalaVersion}")
  def pluginOptions = Task { Seq(s"-Xplugin:${assembly().path}") }
}

object sim extends Cross[Sim](SpinalVersion.compilers){
  def defaultCrossSegments = Seq(SpinalVersion.compilers.head)
}
trait Sim extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.sim")
  def mvnDeps = super.mvnDeps() ++ Seq(
    mvn"commons-io:commons-io:2.11.0",
    mvn"net.openhft:affinity:3.23.2",
    mvn"org.slf4j:slf4j-simple:2.0.5",
    mvn"com.github.oshi:oshi-core:6.4.0"
  )
  def publishVersion = SpinalVersion.sim
}

object lib extends Cross[Lib](SpinalVersion.compilers){
  def defaultCrossSegments = Seq(SpinalVersion.compilers.head)
}
trait Lib extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.lib")
  def moduleDeps = Seq(moduleRefs("core"), moduleRefs("sim"))
  def scalacOptions = super.scalacOptions() ++ modulePluginOptions()
  def mvnDeps = super.mvnDeps() ++ Seq(mvn"commons-io:commons-io:2.11.0", mvn"org.scalatest::scalatest:${scalatestVersion}",
    mvn"io.github.zhaokunhu::ipxactscalacases:0.0.3")
  def publishVersion = SpinalVersion.lib
}

import sys.process._
def gitHash(dir: os.Path) = (try {
  s"git -C ${dir.toString} rev-parse HEAD".!!
} catch {
  case e: java.io.IOException => "???"
}).linesIterator.next()

object core extends Cross[Core](SpinalVersion.compilers){
  def defaultCrossSegments = Seq(SpinalVersion.compilers.head)
}
trait Core extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.core")
  def moduleDeps = Seq(moduleRefs("idslplugin"), moduleRefs("sim"))

  def scalacOptions = super.scalacOptions() ++ modulePluginOptions()
  def mvnDeps = super.mvnDeps() ++ Seq(
    mvn"org.scala-lang:scala-reflect:${scalaVersion}",
    mvn"com.github.scopt::scopt:4.1.0",
    mvn"com.lihaoyi::sourcecode:0.3.0"
  )

  override def generatedSources = Task {
    val dest = Task.dest / "Info.scala"
    val code =
      s"""package spinal.core
         |object Info {
         |  val version = "%s"
         |  val name = "%s"
         |  val gitHash = "%s"
         |}
            |""".stripMargin.format(SpinalVersion.core, mainClass, gitHash(Task.dest))
    os.write(dest, code, createFolders = true)
    Seq(PathRef(Task.dest))
  }
}

object tester extends Cross[Tester](SpinalVersion.compilers){
  def defaultCrossSegments = Seq(SpinalVersion.compilers.head)
}
trait Tester extends SpinalModule with SpinalPublishModule {
  def mainClass = Some("spinal.tester")
  def moduleDeps = Seq(moduleRefs("core"), moduleRefs("sim"), moduleRefs("lib"))
  def scalacOptions = super.scalacOptions() ++ modulePluginOptions()
  def mvnDeps = super.mvnDeps() ++ Seq(mvn"org.scalatest::scalatest:${scalatestVersion}")
  def publishVersion = SpinalVersion.tester
}