module AiVsAi.GameData where

import Prelude

import qualified Data.Map 
--import qualified Data.Map as Map
--import Language.Haskell.Pretty 



--import Data.Typeable.Internal (Typeable)

--import Data.Data (Data)


data Action =
    Move TileID
    |Fire UnitID
    |Pass 
    |ChaseOrFireAt UnitID
    |MoveTowards TileID
    |Retreat
    deriving (Eq, Show, Read)
    
type ActionChoice = UnitState -> [Action] -> GameState -> Action

type Strategy = UnitType -> ActionChoice




--copy of error function, used in liquidHaskell to make sure never called
--notImplemented = error

--data for the different teams
data Team = TeamF | TeamC
    deriving (Show, Read, Eq, Ord)

--Data for different types of games
data GameType = Deathmatch | Endurance Int
    deriving (Eq, Show, Read, Ord)

data Result = FWin | CWin | Draw
    deriving (Eq, Show, Read, Ord)


type TileID = (Int, Int)


data Tile = Tile {
    occupant :: TileOccupant,
    xcoord :: Int,
    ycoord :: Int{-,
    left :: Maybe TileID,
    right :: Maybe TileID,
    top :: Maybe TileID,
    bottom :: Maybe TileID,
    topleft :: Maybe TileID,
    topright :: Maybe TileID,
    bottomleft :: Maybe TileID,
    bottomright :: Maybe TileID-}
} deriving (Show, Read)




--Stores if this tile is empty, filled with unit, or filled with an obstacle
data TileOccupant = 
    TileUnit UnitID
    | TileEnv EnvUnit 
    | Empty
    deriving (Eq, Show, Read)




--TODO change to have actual type
data EnvUnit = Tree | Window
    deriving (Show, Read, Eq)



--Each unit has a unique ID number
type UnitID = Int
--    deriving (Eq, Show, Read, Ord)


type TileMap = Data.Map.Map TileID Tile
type UnitMap = Data.Map.Map UnitID UnitState


data UnitState = UnitState {
    idNum :: UnitID,
    pos :: (Int, Int),
    unitType :: UnitType,
    unitTeam :: Team,
    isAlive :: Bool,
    hp :: Int,
    lastFireTurn :: Int
} deriving (Show, Read)


data UnitType = Scout | Tank
    deriving (Show, Read, Eq, Ord)

    
--The game state is the state of each tile
data GameState = GameState 
    {
    gameTiles :: Data.Map.Map TileID Tile,
    gameUnits :: Data.Map.Map UnitID UnitState,
    turn :: Int, --TODO Integer? Shouldn't need really big numbers
    gameMapWidth :: Int,
    gameMapHeight :: Int,
    gameType :: GameType,
    unitQueue :: [(Team, UnitID)]
    }  deriving (Show, Read)


defaultGameState = GameState 
  {gameTiles = Data.Map.empty,
   gameUnits = Data.Map.empty,
   turn = 0,
   gameMapWidth = 0,
   gameMapHeight = 0,
   gameType = Deathmatch,
   unitQueue = [] }  


-- | Used to avoid !
justElem :: Ord a => Data.Map.Map a b -> a -> b
justElem dict key = case (Data.Map.lookup key dict) of
  (Just e) -> e
  _ -> error $ "Key not in map"    
    
    
occupantAt :: TileID -> GameState -> TileOccupant
occupantAt loc gs = occupant tile
    where 
        tiles = gameTiles gs
        tile = justElem tiles loc

tileEmpty :: TileID -> GameState -> Bool
tileEmpty tid gs = (occupantAt tid gs) == Empty

unitByID :: UnitID -> GameState -> UnitState
unitByID u gs=  justElem (gameUnits gs)  u    
    
    
getUnitState gs uid = justElem (gameUnits gs) uid


--easy getters and setters
getUnitPos gs = pos . getUnitState gs
getUnitTeam gs = unitTeam . getUnitState gs
getUnitType gs = unitType . getUnitState   gs 
getUnitHealth gs = hp . getUnitState   gs    


--TODO better place?
type GameTime = Float