module Reanimate.Driver.CLI
  ( getDriverOptions
  , Options(..)
  , Command(..)
  , Preset(..)
  , Format(..)
  , Raster(..)
  , showFormat
  , showRaster
  ) where

import           Data.Char
import           Data.Monoid
import           Options.Applicative
import           Prelude
import           Reanimate.Render    (FPS, Format (..), Height, Raster (..),
                                      Width)

newtype Options = Options
  { optsCommand :: Command
  } deriving (Show)

data Command
  = Raw
    { rawOutputFolder :: FilePath
    , rawFrameOffset  :: Int
    , rawPrettyPrint  :: Bool
    }
  | Test
  | Check
  | View
    { viewVerbose   :: Bool
    , viewGHCPath   :: Maybe FilePath
    , viewGHCOpts   :: [String]
    , viewOrigin    :: Maybe FilePath
    }
  | Render
    { renderTarget  :: Maybe String
    , renderFPS     :: Maybe FPS
    , renderWidth   :: Maybe Width
    , renderHeight  :: Maybe Height
    , renderCompile :: Bool
    , renderFormat  :: Maybe Format
    , renderPreset  :: Maybe Preset
    , renderRaster  :: Raster
    , renderPartial :: Bool
    , renderHash    :: Bool
    }
   deriving (Show)

data Preset = Youtube | ExampleGif | Quick | MediumQ | HighQ | LowFPS
  deriving (Show)

readRaster :: String -> Maybe Raster
readRaster raster =
  case map toLower raster of
    "none"          -> Just RasterNone
    "auto"          -> Just RasterAuto
    "inkscape"      -> Just RasterInkscape
    "rsvg"          -> Just RasterRSvg
    "imagemagick"   -> Just RasterMagick
    _               -> Nothing

showRaster :: Raster -> String
showRaster RasterNone     = "none"
showRaster RasterAuto     = "auto"
showRaster RasterInkscape = "inkscape"
showRaster RasterRSvg     = "rsvg"
showRaster RasterMagick   = "imagemagick"

readFormat :: String -> Maybe Format
readFormat fmt =
  case map toLower fmt of
    "mp4"  -> Just RenderMp4
    "gif"  -> Just RenderGif
    "webm" -> Just RenderWebm
    _      -> Nothing

showFormat :: Format -> String
showFormat RenderMp4  = "mp4"
showFormat RenderGif  = "gif"
showFormat RenderWebm = "webm"

readPreset :: String -> Maybe Preset
readPreset preset =
  case map toLower preset of
    "youtube" -> Just Youtube
    "gif"     -> Just ExampleGif
    "quick"   -> Just Quick
    "medium"  -> Just MediumQ
    "high"    -> Just HighQ
    "lowfps"  -> Just LowFPS
    _         -> Nothing

showPreset :: Preset -> String
showPreset Youtube    = "youtube"
showPreset ExampleGif = "gif"
showPreset Quick      = "quick"
showPreset MediumQ    = "medium"
showPreset HighQ      = "high"
showPreset LowFPS     = "lowfps"

options :: Parser Options
options = Options <$> commandP

commandP :: Parser Command
commandP = subparser(
     command "test" testCommand
  <> commandGroup "Internal commands"
  <> internal )
  <|> hsubparser
    ( command "check" checkCommand
    <> command "view" viewCommand
    <> command "render" renderCommand
    <> command "raw" rawCommand
    )
  <|> infoParser viewCommand

rawCommand :: ParserInfo Command
rawCommand = info parse
    (progDesc "Output raw SVGs for animation at 60 fps. Used internally by viewer.")
  where
    parse = Raw
      <$> strOption
        ( long "output" <>
          short 'o' <>
          metavar "PATH" <>
          help "Output folder" <>
          value ".")
      <*> option auto
        ( long "offset" <>
          metavar "NUMBER" <>
          help "Frame offset" <>
          value 0)
      <*> switch
        ( long "pretty-print" <>
          short 'p' <>
          help "Pretty print svg")

testCommand :: ParserInfo Command
testCommand = info (parse <**> helper)
    (progDesc "Generate 10 frames spread out evenly across the animation. Used \
              \internally by the test-suite.")
  where
    parse = pure Test

checkCommand :: ParserInfo Command
checkCommand = info parse
    (progDesc "Run a system's diagnostic and report any missing external dependencies.")
  where
    parse = pure Check

viewCommand :: ParserInfo Command
viewCommand = info parse
    (progDesc "Play animation in browser window.")
  where
    parse = View
      <$> switch
        (long "verbose" <> short 'v')
      <*> optional (strOption (long "ghc"
                    <> metavar "PATH"
                    <> help "Path to GHC binary"))
      <*> many (strOption (long "ghc-opt"
                <> short 'G'
                <> help "Additional option to pass to ghc"))
      <*> optional (strOption (long "self"
                    <> metavar "PATH"
                    <> help "Source file used for live-reloading"))

renderCommand :: ParserInfo Command
renderCommand = info parse
    (progDesc "Render animation to file.")
  where
    -- fromPreset :: (Maybe Preset -> (Command -> Command))
    -- fromPreset Nothing = id
    -- fromPreset (Just ExampleGif) = \cmd -> cmd{renderFPS=24}
    -- modParser :: Parser (Command -> Command)
    -- modParser = fmap fromPreset $
    --   optional (option (maybeReader readPreset)
    --       (long "preset" <> showDefaultWith showPreset
    --       <> metavar "TYPE"
    --       <> help "Parameter presets: youtube, gif, quick"))
    parse = Render
      <$> optional (strOption (long "target"
                    <> short 'o'
                    <> metavar "FILE"
                    <> help "Write output to FILE"))
      <*> optional (option auto
          (long "fps" <> metavar "FPS"
          <> help "Set frames per second."))
      <*> optional (option auto
          (long "width" <> short 'w' <> metavar "PIXELS"
          <> help "Set video width."))
      <*> optional (option auto
          (long "height" <> short 'h'
          <> metavar "PIXELS" <> help "Set video height."))
      <*> switch (long "compile"
                  <> help "Compile source code before rendering.")
      <*> optional (option (maybeReader readFormat)
          (long "format" <> metavar "FMT"
          <> help "Video format: mp4, gif, webm"))
      <*> optional (option (maybeReader readPreset)
          (long "preset" <> showDefaultWith showPreset
          <> metavar "TYPE"
          <> help "Parameter presets: youtube, gif, quick, medium, high"))
      <*> option (maybeReader readRaster)
          (long "raster" <> showDefaultWith showRaster
          <> metavar "RASTER"
          <> value RasterNone
          <> help "Raster engine: none, auto, inkscape, rsvg, imagemagick")
      <*> switch
        (long "partial"
        <> help "Produce partial animation even if frame generation was \
                \interrupted by ctrl-c")
      <*> flag True False
        (long "disable-hashing"
        <> help "Disable SVG dedup via hashing. This might improve performance \
                \if all your frames are unique.")

opts :: ParserInfo Options
opts = info (options <**> helper )
  ( fullDesc
  <> progDesc "This program contains an animation which can either be viewed \
              \in a web-browser or rendered to disk."
  )

getDriverOptions :: IO Options
getDriverOptions = customExecParser (prefs showHelpOnError) opts