// SPDX-License-Identifier: Apache-2.0

package firrtl.options

import firrtl.{AnnotationSeq, Transform}

import scopt.{OptionDef, OptionParser, Read}

/** Contains information about a [[Shell]] command line option
  * @tparam the type of the command line argument
  * @param longOption a long, double-dash option
  * @param toAnnotationSeq a function to convert the type into an [[firrtl.AnnotationSeq AnnotationSeq]]
  * @param helpText help text
  * @param shortOption an optional single-dash option
  * @param helpValueName a string to show as a placeholder argument in help text
  */
final class ShellOption[A: Read](
  val longOption:      String,
  val toAnnotationSeq: A => AnnotationSeq,
  val helpText:        String,
  val shortOption:     Option[String] = None,
  val helpValueName:   Option[String] = None) {

  /** Add this specific shell (command line) option to an option parser
    * @param p an option parser
    */
  final def addOption(p: OptionParser[AnnotationSeq]): Unit = {
    val f = Seq(
      (p: OptionDef[A, AnnotationSeq]) => p.action((x, c) => toAnnotationSeq(x).reverse ++ c),
      (p: OptionDef[A, AnnotationSeq]) => p.text(helpText),
      (p: OptionDef[A, AnnotationSeq]) => p.unbounded()
    ) ++
      shortOption.map(a => (p: OptionDef[A, AnnotationSeq]) => p.abbr(a)) ++
      helpValueName.map(a => (p: OptionDef[A, AnnotationSeq]) => p.valueName(a))

    f.foldLeft(p.opt[A](longOption))((a, b) => b(a))
  }
}

/** Indicates that this class/object includes options (but does not add these as a registered class)
  */
trait HasShellOptions {

  /** A sequence of options provided
    */
  def options: Seq[ShellOption[_]]

  /** Add all shell (command line) options to an option parser
    * @param p an option parser
    */
  final def addOptions(p: OptionParser[AnnotationSeq]): Unit = options.foreach(_.addOption(p))

}

/** A [[Transform]] that includes an option that should be exposed at the top level.
  *
  * @note To complete registration, include an entry in
  * src/main/resources/META-INF/services/firrtl.options.RegisteredTransform
  */
trait RegisteredTransform extends HasShellOptions { this: Transform => }

/** A class that includes options that should be exposed as a group at the top level.
  *
  * @note To complete registration, include an entry in
  * src/main/resources/META-INF/services/firrtl.options.RegisteredLibrary
  */
trait RegisteredLibrary extends HasShellOptions {

  /** The name of this library.
    *
    * This will be used when generating help text.
    */
  def name: String

}
