{-|
Module      : State
Copyright   : (c) 2014 Kaashif Hymabaccus
License     : GPL-3
Maintainer  : kaashif@kaashif.co.uk
Stability   : experimental
Portability : POSIX
-}
module State where

import qualified Player
import qualified Board
import String

-- | Data structure passed around by the main game loop
data State = State { player :: Player.Player    -- ^ The one and only player
                   , board :: Board.Board       -- ^ The map of cells the game takes place in
                   , continue :: Bool           -- ^ Whether to continue running, or to terminate after the current loop
                   , messages :: [String]       -- ^ Message queue to be printed next loop
                   }

-- | Reads the initial board from file and creates the initial game state
initState :: IO State
initState = Board.readBoard >>= \s -> return State { player = Player.initial
                                                   , board = s
                                                   , continue = True
                                                   , messages = []
                                                   }

-- | Returns a function to modify the game state based on a command it is given
process :: String -> (State -> State)
process "quit" = discontinue
process "help" = addMessage terseHelp
process "north" = describe.(modPlayer Player.north).markVisited
process "east" = describe.(modPlayer Player.east).markVisited
process "south" = describe.(modPlayer Player.south).markVisited
process "west" = describe.(modPlayer Player.west).markVisited
process "look" = describe
process _ = id

-- | Appends a message to the queue waiting to be printed
addMessage :: String -> State -> State
addMessage msg s = s { messages = (messages s) ++ [msg] }

-- | Prints all messages in the queue
printMessages :: State -> IO ()
printMessages s = (putStr . unlines . map (init . unlines . wrapLine 72) . messages) s

-- | Checks whether the game state is valid (i.e. whether "Player" is on the "Board")
valid :: State -> Bool
valid s = Board.valid (player s) (board s)

-- | Modifies player record using the given function
modPlayer :: (Player.Player -> Player.Player) -> State -> State
modPlayer f s = s { player = (f . player) s }

-- | Sets the continue record of the state to false, stopping the game
discontinue :: State -> State
discontinue s = s { continue = False }

-- | Adds an appropriate description for the "Player"'s location to the queue
describe :: State -> State
describe s = addMessage (Board.describe (board s) $ Player.position $ player s) s

-- | Marks the "Player"'s current location as visited
markVisited :: State -> State
markVisited s = s { board = Board.markVisited (player s) (board s) }

-- | Clears the message queue and other ephemeral state
reset :: State -> State
reset s = s { messages = [] }

-- | Shorter help, printed in-game
terseHelp = "valid commands: help, quit, north, east, south, west, look"
