module GameMechanics where

import Game
import FizzBuzzCode
import Lucas
import System.Random (newStdGen, randomRs, mkStdGen, randomR)
import Control.Arrow

initializeGameState :: IO (GameState Int)
initializeGameState =
  do
    gen <- newStdGen
    let aiStartingPositions = take 5 (randomRs (1, 100) gen)
        ais = map (AI 3) aiStartingPositions
    return $ InPlay (Human 25 0) ais [0..100]


executeAiTurn :: GameState Int -> Int -> GameState (Clue Int)
executeAiTurn gameState seed =
                          let human = humanPlayer gameState
                              dmg = length $ filter ((>0).playerHealth) $ aiPlayers gameState
                              damagedHuman = damagePlayer human dmg
                              livingAis = filter isAlive $ aiPlayers gameState
                              (indexOfAiToReveal, _) = randomR (0, length livingAis - 1) $ mkStdGen seed
                              aiToReveal = livingAis !! indexOfAiToReveal
                              tileToReveal = playerPosition aiToReveal
                              boardWithReveal = map (\i -> if i == tileToReveal then EnemyHere i else NoInfo i) (board gameState)
                          in updateGameBoard gameState damagedHuman (aiPlayers gameState) boardWithReveal


executeHumanTurn :: GameState Int -> (Int, Int) -> GameState (FizzBuzzCode Int)
executeHumanTurn gameState (p, q) = case gameState of
                                      HumanWins -> HumanWins
                                      AiWins    -> AiWins
                                      Tie       -> Tie
                                      _         ->
                                          let theBoard = board gameState
                                              boardLength = length theBoard
                                              fizzSeq = createLucasUSequence p q (boardLength + 1)
                                              buzzSeq = createLucasVSequence p q (boardLength + 1)
                                              retBoard = castFizzBuzz theBoard fizzSeq buzzSeq
                                              ais = aiPlayers gameState
                                              damagedAis = map (damage retBoard) ais
                                          in updateGameBoard gameState (humanPlayer gameState) damagedAis retBoard


damage :: Board (FizzBuzzCode Int) -> Player -> Player
damage board player = let position = playerPosition player
                          predicate = (/=position).fizzBuzzValue
                          codes = dropWhile predicate board
                          code = if not (null codes)
                                 then head codes
                                 else error $ "FizzBuzzCode not found. Board: " ++ show (map (show &&& fizzBuzzValue) board) ++ " Player: " ++ show player
                      in case code of
                                   Norm _     -> player
                                   Fizz _     -> damagePlayer player 1
                                   Buzz _     -> damagePlayer player 1
                                   FizzBuzz _ -> damagePlayer player 2

updateGameBoard :: GameState a -> Player -> [Player] -> Board b -> GameState b
updateGameBoard gameState human ais newBoard =
                       let humanAlive = (>0) $ playerHealth human
                           aisAlive = any (>0) $ map playerHealth ais
                       in if humanAlive && aisAlive
                          then InPlay human ais newBoard
                          else if not (humanAlive || aisAlive)
                               then Tie
                               else if humanAlive
                                    then HumanWins
                                    else AiWins

resetBoard :: GameState a -> GameState Int
resetBoard (InPlay h ais brd) = InPlay h ais [0..(length brd - 1)]
resetBoard HumanWins = HumanWins
resetBoard AiWins = AiWins
resetBoard Tie = Tie
