-- | A module for compiling Scala source files using @scalac@.
module System.Build.Scala.Scalac(
                                  Fsc,
                                  Scalac,
                                  -- * @Scalac@ members
                                  debug,
                                  nowarn,
                                  verbose,
                                  deprecation,
                                  unchecked,
                                  classpath,
                                  sourcepath,
                                  bootclasspath,
                                  extdirs,
                                  directory,
                                  encoding,
                                  target,
                                  print,
                                  optimise,
                                  explaintypes,
                                  uniqid,
                                  version,
                                  help,
                                  (?),
                                  etc,
                                  -- * @Scalac@ values
                                  scalac,
                                  scalac',
                                  kscalac,
                                  fscalac,
                                  -- * @Fsc@ members
                                  reset,
                                  shutdown,
                                  server,
                                  flags,
                                  -- * @Fsc@ values
                                  fsc
                                ) where

import Prelude hiding (print)
import System.Build.Args
import System.Build.Scala.Debug
import System.Build.Scala.Target
import System.Build.CompilePaths
import System.Build.Extensions
import System.Build.OutputDirectory
import System.Build.OutputReferenceSet
import System.Build.OutputReferenceGet
import System.Build.Command
import Control.Monad
import Data.List
import Data.Maybe
import System.FilePath

-- | Scalac is the compiler for Scala source files.
data Scalac = Scalac {
  debug :: Maybe Debug,        -- ^ @-g@
  nowarn :: Bool,              -- ^ @-nowarn@
  verbose :: Bool,             -- ^ @-verbose@
  deprecation :: Bool,         -- ^ @-deprecation@
  unchecked :: Bool,           -- ^ @-unchecked@
  classpath :: [FilePath],     -- ^ @-classpath@
  sourcepath :: [FilePath],    -- ^ @-sourcepath@
  bootclasspath :: [FilePath], -- ^ @-bootclasspath@
  extdirs :: [FilePath],       -- ^ @-extdirs@
  directory :: Maybe FilePath, -- ^ @-d@
  encoding :: Maybe String,    -- ^ @-encoding@
  target :: Maybe Target,      -- ^ @-target@
  print :: Bool,               -- ^ @-print@
  optimise :: Bool,            -- ^ @-optimise@
  explaintypes :: Bool,        -- ^ @-explaintypes@
  uniqid :: Bool,              -- ^ @-uniqid@
  version :: Bool,             -- ^ @-version@
  help :: Bool,                -- ^ @-help@
  (?) :: Maybe FilePath,       -- ^ @\@@
  etc :: Maybe String
}

-- | A @Scalac@ with nothing set.
scalac :: Scalac
scalac = Scalac Nothing False False False False [] [] [] [] Nothing Nothing Nothing False False False False False False Nothing Nothing

-- | Construct a @Scalac@.
scalac' :: Maybe System.Build.Scala.Debug.Debug
           -> Bool
           -> Bool
           -> Bool
           -> Bool
           -> [FilePath]
           -> [FilePath]
           -> [FilePath]
           -> [FilePath]
           -> Maybe FilePath
           -> Maybe String
           -> Maybe Target
           -> Bool
           -> Bool
           -> Bool
           -> Bool
           -> Bool
           -> Bool
           -> Maybe FilePath
           -> Maybe String
           -> Scalac
scalac' = Scalac

-- | Convert the given scalac to a list of command line options which may be used by other scala tools.
kscalac :: Scalac -> [String]
kscalac (Scalac debug'
                 nowarn'
                 verbose'
                 deprecation'
                 unchecked'
                 classpath'
                 sourcepath'
                 bootclasspath'
                 extdirs'
                 directory'
                 encoding'
                 target'
                 print'
                 optimise'
                 explaintypes'
                 uniqid'
                 version'
                 help'
                 script'
                 etc') = ["g" -~> debug',
                         "nowarn" ~~ nowarn',
                         "verbose" ~~ verbose',
                         "deprecation" ~~ deprecation',
                         "unchecked" ~~ unchecked',
                         "classpath" ~: classpath',
                         "sourcepath" ~: sourcepath',
                         "bootclasspath" ~: bootclasspath',
                         "extdirs" ~: extdirs',
                         "d" ~~> directory',
                         "encoding" ~~> encoding',
                         "target" -~> target',
                         "print" ~~ print',
                         "optimise" ~~ optimise',
                         "explaintypes" ~~ explaintypes',
                         "uniqid" ~~ uniqid',
                         "version" ~~ version',
                         "help" ~~ help',
                         (:) '@' ~? script',
                         fromMaybe [] etc']

instance Show Scalac where
  show s = kscalac s ^^^ " "

instance CompilePaths Scalac where
  j =>> ps = show j ++ ' ' : space ps

instance Extensions Scalac where
  exts _ = ["java", "scala"]

instance OutputDirectory Scalac where
  outdir = directory

instance OutputReferenceSet Scalac where
  setReference p j = j { classpath = p }

instance OutputReferenceGet Scalac where
  getReference = classpath

instance Command Scalac where
  command _ = let envs = [
                            ("SCALA_HOME", (</> "bin" </> "scalac")),
                            ("SCALAC", id)
                         ]
              in fromMaybe "scalac" `fmap` tryEnvs envs

-- | The Scala fast compiler (@fsc@).
data Fsc = Fsc {
  fscalac :: Scalac,                -- ^ The scalac options to use.
  reset :: Bool,                    -- ^ @-reset@
  shutdown :: Bool,                 -- ^ @-shutdown@
  server :: Maybe (String, String), -- ^ @-server@
  flags :: [String]                 -- ^ @-flags@
}

instance Show Fsc where
  show (Fsc fscalac' reset' shutdown' server' flags') = (kscalac fscalac' ++ ["reset" ~~ reset', "shutdown" ~~ shutdown', maybe [] (uncurry (++)) server', intercalate " " (fmap ("-J" ++) flags')]) ^^^ " "

instance CompilePaths Fsc where
  j =>> ps = show j ++ ' ' : space ps

instance Extensions Fsc where
  exts _ = ["java", "scala"]

instance OutputDirectory Fsc where
  outdir = directory . fscalac

instance OutputReferenceSet Fsc where
  setReference p j = let s = fscalac j in j { fscalac = setReference p s }

instance OutputReferenceGet Fsc where
  getReference = getReference . fscalac

instance Command Fsc where
  command _ = let envs = [
                            ("SCALA_HOME", (</> "bin" </> "fsc")),
                            ("FSC", id)
                         ]
              in fromMaybe "fsc" `fmap` tryEnvs envs

-- | A @Fsc@ with nothing set.
fsc :: Fsc
fsc = Fsc scalac False False Nothing []