-- | General content types and operations.
module Game.LambdaHack.Common.Kind
  ( ContentData, COps(..)
  , emptyCOps
  , ItemSpeedup
  , emptyItemSpeedup, getKindMean, speedupItem
  , TileSpeedup(..), Tab(..)
  , emptyTileSpeedup, emptyTab
  , okind, omemberGroup, oisSingletonGroup, ouniqGroup, opick
  , ofoldlWithKey', ofoldlGroup', omapVector, oimapVector
  , olength, linearInterpolation
  ) where

import Prelude ()

import Game.LambdaHack.Core.Prelude

import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import           Data.Word (Word8)

import qualified Game.LambdaHack.Common.ItemAspect as IA
import           Game.LambdaHack.Content.CaveKind
import           Game.LambdaHack.Content.ItemKind (ItemKind)
import qualified Game.LambdaHack.Content.ItemKind as IK
import           Game.LambdaHack.Content.ModeKind
import           Game.LambdaHack.Content.PlaceKind
import           Game.LambdaHack.Content.RuleKind
import           Game.LambdaHack.Content.TileKind (TileKind)
import           Game.LambdaHack.Definition.ContentData
import           Game.LambdaHack.Definition.Defs

-- | Operations for all content types, gathered together.
data COps = COps
  { cocave        :: ContentData CaveKind   -- server only
  , coitem        :: ContentData ItemKind
  , comode        :: ContentData ModeKind   -- server only
  , coplace       :: ContentData PlaceKind  -- server only, so far
  , corule        :: RuleContent
  , cotile        :: ContentData TileKind
  , coItemSpeedup :: ItemSpeedup
  , coTileSpeedup :: TileSpeedup
  }

instance Show COps where
  show _ = "game content"

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

emptyCOps :: COps
emptyCOps = COps
  { cocave  = emptyContentData
  , coitem  = emptyContentData
  , comode  = emptyContentData
  , coplace = emptyContentData
  , corule  = emptyRuleContent
  , cotile  = emptyContentData
  , coItemSpeedup = emptyItemSpeedup
  , coTileSpeedup = emptyTileSpeedup
  }

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

emptyItemSpeedup :: ItemSpeedup
emptyItemSpeedup = ItemSpeedup V.empty

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

-- | A lot of tabulated maps from tile kind identifier to a property
-- of the tile kind.
data TileSpeedup = TileSpeedup
  { isClearTab         :: Tab Bool
  , isLitTab           :: Tab Bool
  , isWalkableTab      :: Tab Bool
  , isDoorTab          :: Tab Bool
  , isChangableTab     :: Tab Bool
  , isSuspectTab       :: Tab Bool
  , isHideAsTab        :: Tab Bool
  , consideredByAITab  :: Tab Bool
  , isVeryOftenItemTab :: Tab Bool
  , isCommonItemTab    :: Tab Bool
  , isOftenActorTab    :: Tab Bool
  , isNoItemTab        :: Tab Bool
  , isNoActorTab       :: Tab Bool
  , isEasyOpenTab      :: Tab Bool
  , isEmbedTab         :: Tab Bool
  , isAquaticTab       :: Tab Bool
  , alterMinSkillTab   :: Tab Word8
  , alterMinWalkTab    :: Tab Word8
  }

-- Vectors of booleans can be slower than arrays, because they are not packed,
-- but with growing cache sizes they may as well turn out faster at some point.
-- The advantage of vectors are exposed internals, in particular unsafe
-- indexing. Also, in JS, bool arrays are obviously not packed.
-- | A map morally indexed by @ContentId TileKind@.
newtype Tab a = Tab (U.Vector a)

emptyTileSpeedup :: TileSpeedup
emptyTileSpeedup = TileSpeedup emptyTab emptyTab emptyTab emptyTab emptyTab
                               emptyTab emptyTab emptyTab emptyTab emptyTab
                               emptyTab emptyTab emptyTab emptyTab emptyTab
                               emptyTab emptyTab emptyTab

emptyTab :: U.Unbox a => Tab a
emptyTab = Tab $! U.empty
