{-#LANGUAGE OverloadedStrings #-} {-#LANGUAGE FlexibleContexts, MultiParamTypeClasses #-} {-#LANGUAGE OverloadedLabels #-} {-#LANGUAGE StandaloneDeriving#-} {-#LANGUAGE QuasiQuotes #-} {-#LANGUAGE TemplateHaskell, DataKinds #-} module Network.SC2.Internal.ConstantGenerator where import Prelude hiding ((.)) import Control.Category((.)) import Data.Aeson import System.FilePath import System.Directory (getHomeDirectory, doesFileExist) import Network.SC2.Internal.Directories import Language.Haskell.TH.Syntax import Language.Haskell.TH import Language.Haskell.TH.Quote import qualified Data.Text as T (pack, unpack, Text, append, words, toTitle,toUpper, concat, isSuffixOf) import Network.SC2.Internal.Types import Data.Char (toUpper) import Lens.Labels hiding ((^.), (.~)) import Lens.Labels.Unwrapped import Data.Maybe import Control.Lens readStableIDjson :: IO (FilePath, IDs) readStableIDjson = do home <- getHomeDirectory let path' = home userSC2SubDirectory "stableid.json" exists <- doesFileExist path' let path = if exists then path' else "backupStableID.json" contents' <- decodeFileStrict' path :: IO (Maybe IDs) let contents = fromMaybe (error "Error parsing stableid.json") contents' return (home, contents) stableIDjsonContents :: Q IDs stableIDjsonContents = do (path, contents) <- runIO readStableIDjson --FIXME: addDependentFile is causing a permisison error. Unsure if with stack or GHC. --addDependentFile path return contents scAbilitiesDeclarations :: Q [Dec] scAbilitiesDeclarations = do contents <- stableIDjsonContents let (ha:ra) = filter (\a -> not (Data.Maybe.isJust (remapID a) || abilityButtonName a == "") ) $ abilities contents let abilities' = (ha & #canonicalName .~ "NullAbility" ) : ra scEntityEnumGen "ability" abilities' scBuffsDeclarations :: Q [Dec] scBuffsDeclarations = do contents <- stableIDjsonContents let (hb:rb) = buffs contents let buffs' = (hb & #canonicalName .~ "NullBuff" ) : rb scEntityEnumGen "buff" buffs' scEffectsDeclarations :: Q [Dec] scEffectsDeclarations = do contents <- stableIDjsonContents let (he:re) = effects contents let effects' = (he & #canonicalName .~ "NullEffect" ) : re scEntityEnumGen "effect" effects' scUpgradesDeclarations :: Q [Dec] scUpgradesDeclarations = do contents <- stableIDjsonContents let (hu:ru) = upgrades contents let upgrades' = (hu & #canonicalName .~ "NullUpgrade" ) : ru scEntityEnumGen "upgrade" upgrades' scUnitsDeclarations :: Q [Dec] scUnitsDeclarations = do contents <- stableIDjsonContents let units' = units contents scEntityEnumGen "unit" units' sanitiseName :: T.Text -> String sanitiseName name = let titled = T.unpack name in case titled of ('1':'0':rest) -> "Ten" ++ rest ('1':'2':rest) -> "Twelve" ++ rest ('1':'4':rest) -> "Fourteen" ++ rest ('1':'6':rest) -> "Sixteen" ++ rest ('1':'8':rest) -> "Eighteen" ++ rest ('2':'0':rest) -> "Twenty" ++ rest ('2':'2':rest) -> "TwentyTwo" ++ rest ('2':'4':rest) -> "TwentyFour" ++ rest ('2':'5':'0':rest) -> "TwoFifty" ++ rest ('3':'3':'0':rest) -> "ThreeThirty" ++ rest ('1':rest) -> "One" ++ rest ('2':rest) -> "Two" ++ rest ('3':rest) -> "Three" ++ rest ('4':rest) -> "Four" ++ rest ('5':rest) -> "Five" ++ rest ('6':rest) -> "Six" ++ rest ('7':rest) -> "Seven" ++ rest ('8':rest) -> "Eight" ++ rest ('9':rest) -> "Nine" ++ rest ('0':rest) -> "Zero" ++ rest --todo: the rest as appropriate (t:rest) -> toUpper t : rest instance HasLens' Ability "canonicalName" T.Text where lensOf' _ = lens theName (\a n -> a{abilityName = n}) where theName (Ability _ "TerranBuild" "Cancel" Nothing _ Nothing) = "HaltTerranBuild" theName (Ability _ "ProtossBuild" "Cancel" Nothing _ Nothing) = "HaltProtossBuild" theName (Ability _ "ZergBuild" "Cancel" Nothing _ Nothing) = "HaltZergBuild" theName (Ability _ "TerranBuild" bName Nothing _ Nothing) = "Build" `T.append` bName theName (Ability _ "ProtossBuild" bName Nothing _ Nothing) = "Build" `T.append` bName theName (Ability _ "ZergBuild" bName Nothing _ Nothing) = "Build" `T.append` bName theName (Ability _ "WarpGateTrain" bName Nothing _ Nothing) = "WarpGateTrain" `T.append` bName theName (Ability _ "SuperWarpGateTrain" bName Nothing _ Nothing) = "SuperWarpGateTrain" `T.append` bName theName (Ability _ name bName Nothing 0 Nothing) = (if "Train" `T.isSuffixOf` name then "Train" else name) `T.append` bName theName (Ability _ name bName _ _ _ ) | T.toUpper name == T.toUpper bName = (T.concat . fmap T.toTitle . T.words) name theName (Ability _ name bName Nothing idx Nothing) = if "Train" `T.isSuffixOf` name then "Train" `T.append` bName else name `T.append` bName`T.append` (T.pack . show $ idx) theName (Ability _ name bName (Just fName) idx Nothing) = T.concat . fmap T.toTitle . T.words $ fName theName a = error (show a) data EntityDecls = EntityDecls {constructor :: Con, fromClause :: Clause, toClause :: Clause} scEntityEnumGen :: RawIDMappable a => String -> [a] -> Q [Dec] scEntityEnumGen nameLower@(firstLetter:restName) entities = go where nameUpper = toUpper firstLetter : restName typeName = mkName (nameUpper ++ "Type") typeDecl = DataD [] typeName [] Nothing cons [] vals = map con entities cons = map constructor vals con entity = let name = entity ^. #canonicalName . to sanitiseName . to mkName rawid = entity ^. #id conC = NormalC name [] fromClause = Clause [LitP (IntegerL rawid)] (NormalB (ConE name)) [] toClause' = Clause [ConP name []] (NormalB (LitE (IntegerL rawid))) [] in EntityDecls {constructor = conC, fromClause = fromClause, toClause = toClause'} entityFromIntName = mkName "fromInt" entityFromIntDefault = Clause [WildP] (NormalB (AppE (VarE entityFromIntName) (LitE(IntegerL 0)))) [] fromIntDecl = FunD entityFromIntName (map fromClause vals ++ [entityFromIntDefault]) entityToIntName = mkName "toInt" entityToIntDefault = Clause [WildP] (NormalB ((LitE(IntegerL 0)))) [] toIntDecl = FunD entityToIntName (map toClause vals ++ [entityToIntDefault]) instances' = let name = conT typeName in [d| deriving instance Eq $name deriving instance Show $name deriving instance Enum $name deriving instance Bounded $name |] go = do instances <- instances' return ([typeDecl, fromIntDecl, toIntDecl] ++ instances)