{-# LANGUAGE TupleSections #-}
-- | General content types and operations.
module Game.LambdaHack.Common.Kind
  ( ContentData  -- re-exported without some operations
  , COps(..)
  , emptyCOps
  , ItemSpeedup
  , getKindMean, speedupItem
  , okind, omemberGroup, oisSingletonGroup, ouniqGroup, opick
  , ofoldlWithKey', ofoldlGroup', omapVector, oimapVector
  , olength, linearInterpolation, emptyMultiGroupMode, emptyMultiGroupItem
#ifdef EXPOSE_INTERNAL
  , emptyUnknownTile
  , emptyUIFactionGroupName
#endif
    -- * Operations both internal and used in unit tests
  , emptyUIFaction
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import qualified Data.Vector as V

import qualified Game.LambdaHack.Common.ItemAspect as IA
import qualified Game.LambdaHack.Common.Tile as Tile
import qualified Game.LambdaHack.Content.CaveKind as CK
import qualified Game.LambdaHack.Content.FactionKind as FK
import qualified Game.LambdaHack.Content.ItemKind as IK
import qualified Game.LambdaHack.Content.ModeKind as MK
import qualified Game.LambdaHack.Content.PlaceKind as PK
import qualified Game.LambdaHack.Content.RuleKind as RK
import qualified Game.LambdaHack.Content.TileKind as TK
import qualified Game.LambdaHack.Definition.Ability as Ability
import qualified Game.LambdaHack.Definition.Color as Color
import           Game.LambdaHack.Definition.ContentData
import           Game.LambdaHack.Definition.Defs
import           Game.LambdaHack.Definition.DefsInternal
  (GroupName (GroupName), toContentSymbol)
import           Game.LambdaHack.Definition.Flavour (dummyFlavour)

-- | Operations for all content types, gathered together.
--
-- Warning: this type is not abstract, but its values should not be
-- created ad hoc, even for unit tests, but should be constructed
-- with @makeData@ for each particular content kind, which includes validation,
-- and with @speedupItem@, etc., to ensure internal consistency.
--
-- The @emptyCOps@ is one such valid by construction value of this type,
-- except for the @cocave@ field. It's suitable for bootstrapping
-- and for tests not involving dungeon generation from cave templates.
data COps = COps
  { cocave        :: ContentData CK.CaveKind   -- server only
  , cofact        :: ContentData FK.FactionKind
  , coitem        :: ContentData IK.ItemKind
  , comode        :: ContentData MK.ModeKind   -- server only
  , coplace       :: ContentData PK.PlaceKind  -- server only, so far
  , corule        :: RK.RuleContent
  , cotile        :: ContentData TK.TileKind
  , coItemSpeedup :: ItemSpeedup
  , coTileSpeedup :: Tile.TileSpeedup
  }

instance Show COps where
  show _ = "game content"

instance Eq COps where
  (==) _ _ = True

emptyMultiGroupItem :: IK.ItemKind
emptyMultiGroupItem = IK.ItemKind
  { isymbol  = toContentSymbol 'E'
  , iname    = "emptyCOps item"
  , ifreq    = map (, 1) $ IK.mandatoryGroups ++ IK.mandatoryGroupsSingleton
  , iflavour = [dummyFlavour]
  , icount   = 0
  , irarity  = []
  , iverbHit = ""
  , iweight  = 0
  , idamage  = 0
  , iaspects = []
  , ieffects = []
  , idesc    = ""
  , ikit     = []
  }

emptyUnknownTile :: TK.TileKind
emptyUnknownTile = TK.TileKind  -- needs to have index 0 and alter 1
  { tsymbol  = 'E'
  , tname    = "unknown space"  -- name checked in validation
  , tfreq    = map (, 1) $ TK.mandatoryGroups ++ TK.mandatoryGroupsSingleton
  , tcolor   = Color.BrMagenta
  , tcolor2  = Color.BrMagenta
  , talter   = 1
  , tfeature = []
  }

emptyUIFactionGroupName :: GroupName FK.FactionKind
emptyUIFactionGroupName = GroupName "emptyUIFaction"

emptyUIFaction :: FK.FactionKind
emptyUIFaction = FK.FactionKind
  { fname = "emptyUIFaction"
  , ffreq = [(emptyUIFactionGroupName, 1)]
  , fteam = FK.TeamContinuity 999  -- must be > 0
  , fgroups = []
  , fskillsOther = Ability.zeroSkills
  , fcanEscape = False
  , fneverEmpty = True  -- to keep the dungeon alive
  , fhiCondPoly = []
  , fhasGender = False
  , finitDoctrine = Ability.TBlock
  , fspawnsFast = False
  , fhasPointman = False
  , fhasUI = True  -- to own the UI frontend
  , finitUnderAI = False
  , fenemyTeams = []
  , falliedTeams = []
  }

emptyMultiGroupMode :: MK.ModeKind
emptyMultiGroupMode = MK.ModeKind
  { mname   = "emptyMultiGroupMode"
  , mfreq   = map (, 1) MK.mandatoryGroups
  , mtutorial = False
  , mattract = False
  , mroster = [(emptyUIFactionGroupName, [])]
  , mcaves  = []
  , mendMsg = []
  , mrules  = ""
  , mdesc   = ""
  , mreason = ""
  , mhint   = ""
  }

-- | This is as empty, as possible, but still valid content, except for
-- @cocave@ which is empty and not valid (making it valid would require
-- bloating most other contents).
emptyCOps :: COps
emptyCOps =
  let corule = RK.emptyRuleContent
      coitem = IK.makeData (RK.ritemSymbols corule) [emptyMultiGroupItem] [] []
      cotile = TK.makeData [emptyUnknownTile] [] []
      cofact = FK.makeData [emptyUIFaction] [emptyUIFactionGroupName] []
  in COps
    { cocave = emptyContentData  -- not valid! beware when testing!
        -- to make valid cave content, we'd need to define a single cave kind,
        -- which involves creating and validating tile and place kinds, etc.
    , cofact
    , coitem
    , comode = MK.makeData cofact [emptyMultiGroupMode] [] []
    , coplace = PK.makeData cotile [] [] []
    , corule
    , cotile
    , coItemSpeedup = speedupItem coitem
    , coTileSpeedup = Tile.speedupTile False cotile
    }

-- | Map from an item kind identifier to the mean aspect value for the kind.
newtype ItemSpeedup = ItemSpeedup (V.Vector IA.KindMean)

getKindMean :: ContentId IK.ItemKind -> ItemSpeedup -> IA.KindMean
getKindMean kindId (ItemSpeedup is) = is V.! contentIdIndex kindId

speedupItem :: ContentData IK.ItemKind -> ItemSpeedup
speedupItem coitem =
  let f !kind =
        let kmMean = IA.meanAspect kind
            kmConst = not $ IA.aspectsRandom (IK.iaspects kind)
        in IA.KindMean{..}
  in ItemSpeedup $ omapVector coitem f
