packages feed

gtvm-hs-1.0.0: app/Tool/SCP/TL.hs

{-# LANGUAGE OverloadedStrings #-}

module Tool.SCP.TL where

import Common.Config
import Common.CLIOptions
import Common.Util
import Common.IO ( badParseYAML )

import Tool.SCP.Common ( scpPrettyYamlCfg )

import GTVM.Internal.Json

import Options.Applicative

import GHC.Generics
import Control.Monad.IO.Class

import GTVM.SCP.TL

import Data.Text ( Text )
import Data.Yaml.Pretty qualified
import Data.Map qualified as Map

import Data.Yaml.Pretty qualified as Yaml.Pretty

import Numeric.Natural ( Natural )

data CfgToSCPTL = CfgToSCPTL
  { cfgToSCPTLStreamIn     :: Stream 'StreamIn  "YAML SCP"
  , cfgToSCPTLStreamOut    :: Stream 'StreamOut "SCPTL"
  , cfgToSCPTLSpeakerIDMap :: Maybe (StreamFile 'StreamIn "Speaker ID map YAML")
  } deriving (Eq, Show, Generic)

parseCLIOptsToSCPTL :: Parser CfgToSCPTL
parseCLIOptsToSCPTL =
    CfgToSCPTL <$> pStreamIn <*> pStreamOut <*> optional speakerIDMapOpt
  where
    speakerIDMapOpt =
        StreamFile <$> strOption (    long "speaker-ids"
                                   <> help "File containing speaker IDs" )

runToSCPTL :: MonadIO m => CfgToSCPTL -> m ()
runToSCPTL cfg = do
    speakerIDMap <- do
        case cfgToSCPTLSpeakerIDMap cfg of
          Nothing -> return $ const Nothing
          Just sidmapfp -> parseSpeakerMap sidmapfp
    let env = Env
                { envPendingPlaceholder = "TODO not yet translated"
                , envSpeakerIDMap       = speakerIDMap }
    let scpIn = cfgToSCPTLStreamIn cfg
    scpYAMLBs <- readStreamBytes scpIn
    scp <- badParseYAML @SCP' scpYAMLBs
    let scptl   = genTL env scp
        scptlBs = Data.Yaml.Pretty.encodePretty ycTLSeg scptl
    writeStreamTextualBytes (cfgToSCPTLStreamOut cfg) scptlBs

-- | Silly pretty config to get my preferred layout easily.
--
-- Looks silly, but gets the job done very smoothly. Snoyman's yaml library is
-- based for exposing this.
ycTLSeg :: Yaml.Pretty.Config
ycTLSeg =
      Yaml.Pretty.setConfDropNull True
    $ Yaml.Pretty.setConfCompare tlSegFieldOrdering
    $ Yaml.Pretty.defConfig

data SCPSpeakerData = SCPSpeakerData
  { scpSpeakerDataStringAtPointer :: Text
  } deriving (Generic, Eq, Show)

-- TODO previously was rejectUnknownFields = False, now True...
instance ToJSON   SCPSpeakerData where
    toJSON     = gtjg "scpSpeakerData"
    toEncoding = gteg "scpSpeakerData"
instance FromJSON SCPSpeakerData where
    parseJSON  = gpjg "scpSpeakerData"

parseSpeakerMap :: MonadIO m => StreamFile 'StreamIn s -> m (Natural -> Maybe Text)
parseSpeakerMap fp = do
    bs <- readStreamFileBytes fp
    speakers <- badParseYAML @[SCPSpeakerData] bs
    let speakerMap = Map.fromList $ zip [1..] speakers
    return $ \n -> scpSpeakerDataStringAtPointer <$> Map.lookup n speakerMap

data CfgApplySCPTL = CfgApplySCPTL
  { cfgApplySCPTLStreamIn  :: Stream     'StreamIn  "YAML SCP"
  , cfgApplySCPTLFileIn    :: StreamFile 'StreamIn  "SCPTL"
  , cfgApplySCPTLStreamOut :: Stream     'StreamOut "edited YAML SCP"
  } deriving (Eq, Show, Generic)

parseCLIOptsApplySCPTL :: Parser CfgApplySCPTL
parseCLIOptsApplySCPTL =
    CfgApplySCPTL <$> pStreamIn <*> pStreamFileIn <*> pStreamOut

runApplySCPTL :: MonadIO m => CfgApplySCPTL -> m ()
runApplySCPTL cfg = do
    scpYAMLBs <- readStreamBytes $ cfgApplySCPTLStreamIn cfg
    scp       <- badParseYAML @SCP' scpYAMLBs
    scptlYAMLBs <- readStreamFileBytes $ cfgApplySCPTLFileIn cfg
    scptl       <- badParseYAML @SCPTL' scptlYAMLBs
    case apply scp scptl of
      Left  err  -> error $ show err
      Right scp' ->
        let scpYAMLBs' = Yaml.Pretty.encodePretty scpPrettyYamlCfg scp'
         in writeStreamTextualBytes (cfgApplySCPTLStreamOut cfg) scpYAMLBs'