// See LICENSE for license details.

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

}
