{-# LANGUAGE DeriveGeneric, FlexibleInstances, RankNTypes #-}

-- | This module contains data types for actions.
module Game.Cosanostra.Types
    ( -- * Actions
      ActionType(..)
    , Compulsion(..)
    , SelectorAtom(..)
    , Selector
    , ActionGroup
    , Action(..)
    , ActionTraits(..)
    , Act(..)
    , ActTrace(..)

    -- * Constraints
    , ConstraintAtom(..)
    , Constraint

    -- * Effects
    , CauseOfDeath(..)
    , EffectType(..)
    , Effect(..)
    , EffectTrace(..)

    -- * Players
    , Player(..)

    -- * Factions
    , Faction(..)
    , FactionTraits(..)

    -- * Messages
    , MessageInfo(..)
    , Message(..)

    -- * Game
    , Turn
    , Phase(..)
    , Game(..)

    -- * Plan
    , PlanBinding(..)
    , PlanError(..)
    , Plan
    , PlannerInfo(..)
    , Planner

    -- * Ballot
    , Consensus(..)
    , Ballot
    , Tally

    -- * Collections
    , Expr(..)
    , Players(..)
    , Factions
    , Actions
    , ActionGroups
    ) where

import Control.Lens
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.State
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import qualified Data.Text as T
import GHC.Generics (Generic)
import System.Random

-- | Distinct actions that can be performed.
data ActionType = Visit
                -- ^ Visits a target.
                | FruitVend
                -- ^ Gives a target some fruit.
                | Greet
                -- ^ Greet a target with your faction.
                | Roleblock
                -- ^ Stop a target from performing their action.
                | Rolestop
                -- ^ Stop a target from having actions performed on them, except
                -- strongman kills.
                | Protect
                -- ^ Protect a target from death.
                | Kill
                -- ^ Kill a target.
                | StrongmanKill
                -- ^ Kill a target, ignoring bulletproof.
                | DesperadoKill { _actionTypeKillableFactions :: S.Set Faction
                                -- ^ Factions that can be killed by the
                                -- desperado.
                                }
                -- ^ Kill a target, but if the target is not part of the
                -- killable faction, commit suicide.
                | KamikazeKill
                -- ^ Kill a target, but also kill yourself at the same time.
                | Suicide
                -- ^ Kill yourself.
                | Drive
                -- ^ Switch two players, such that any action targeting them
                -- will target the other player.
                | Redirect
                -- ^ Cause all actions used by a player to target a different
                -- player.
                | Deflect
                -- ^ Cause all actions targeting a player to target a different
                -- player.
                | Bodyguard
                -- ^ Bodyguard a player, such that if the player attempted to be
                -- killed, the bodyguard will be killed instead.
                | Hide
                -- ^ Hide behind another player, such that the source is
                -- invulnerable unless the target dies, in which case the source
                -- will also die.
                | Jail
                -- ^ Block a player's actions but protect them from kills.
                | Babysit
                -- ^ Protect a player from death if you are alive.
                | Frame { _actionTypeFramedFaction :: Faction
                        -- ^ Faction to frame as.
                        }
                -- ^ Frames someone as being part of another faction.
                | Commute
                -- ^ Protect yourself from all actions.
                | Recruit { _actionTypeRecruitFaction :: Faction
                          -- ^ Faction to recruit into.
                          , _actionTypeKnowsFactionMembers :: Bool
                          -- ^ Whether or not the recruited player will know
                          -- the other action members.
                          , _actionTypeAgenda :: Constraint
                          -- ^ Agenda for the player to fulfill.
                          }
                -- ^ Recruit a player into a faction.
                | Vanillaize
                -- ^ Remove all of a player's actions.
                | Watch
                -- ^ See all players who visited a player at night.
                | Track
                -- ^ See all players who a player visited at night.
                | Voyeur
                -- ^ See all actions performed on a player at night.
                | Follow
                -- ^ See all actions a player performed at night.
                | Investigate { _actionTypeGuiltyFactions :: S.Set Faction
                              -- ^ The set of factions that appear guilty to the
                              -- investigator.
                              }
                -- ^ See if a player is in a given faction, or is framed as a
                -- given faction.
                | RoleInvestigate
                -- ^ See the role of a player.
                | ForensicInvestigate
                -- ^ See all players who targeted a dead player.
                | Pardon
                -- ^ Make a player unlynchable.
                | Veto
                -- ^ Veto a lynching.
                | StealVote
                -- ^ Steal another player's vote. If the target player dies,
                -- their vote is not stolen.
                | Weak { _actionTypeWeakFaction :: Faction
                       -- ^ The faction the action is weak to.
                       , _actionTypeRealActionType :: ActionType
                       -- ^ The underlying action type.
                       , _actionTypeWeakKillAction :: Action
                       -- ^ The action used to kill on weakness.
                       , _actionTypeBlocksAction :: Bool
                       -- ^ Whether or not the action should be blocked on
                       -- weakness.
                       }
                -- ^ Modifies an action to be weak to a given faction.
                deriving (Eq, Ord, Show, Generic)

-- | The compulsion for an action.
data Compulsion = Voluntary -- ^ The action __may__ be performed.
                | Required -- ^ The action __must__ be performed.
                | Forced -- ^ The action __must__ be performed, and is only
                         -- automatically performed.
                deriving (Eq, Ord, Show, Generic)

data SelectorAtom = Participant
                  | Self
                  | Alive
                  | WasLynched
                  deriving (Eq, Ord, Show, Generic)

type Selector = Expr SelectorAtom

newtype Action = Action Int
                 deriving (Eq, Ord, Show, Generic)

-- | An Action holds additional metadata about an action's type.
data ActionTraits = ActionTraits
    { _actionTraitsType :: ActionType
    -- ^ The type of the action.

    , _actionTraitsNinja :: Bool
    -- ^ Whether the action can be seen by other players.
    } deriving (Eq, Ord, Show, Generic)

-- | An ActionGroup is a group of mutually exclusive actions. Only one action
-- in the group can be planned during a single phase. This is global across all
-- players, e.g. an action group may span multiple players.
--
-- Note that action groups are incapable of representing a one-shot action
-- shared across multiple players – in practice, such actions don't really
-- exist.
type ActionGroup = Int

-- | An Act is the enactment of an action – it holds who performed the action
-- and who the action is being performed on.
data Act = Act
    { _actAction :: Action
    -- ^ The action to perform.

    , _actSource :: Player
    -- ^ The source of the action.

    , _actTargets :: [Player]
    -- ^ The targets of the action.

    , _actUnaffectable :: Bool
    -- ^ The act cannot be affected by any effects.

    , _actTrace :: ActTrace
    -- ^ The trace for the act.
    } deriving (Eq, Ord, Show, Generic)

data Phase = Night
           | Day
           deriving (Eq, Ord, Show, Read, Generic)

-- | An act trace traces an act's origin to the originating effect. If there is
-- no originating effect, the act must be planned originally by the user.
--
-- This is a unique token for identifying acts.
data ActTrace = ActFromPlan { _actTracePlanTurn :: Turn
                            , _actTracePlanPhase :: Phase
                            , _actTracePlanGroup :: ActionGroup
                            }
             | ActFromRewrite { _actTraceDependentTrace :: ActTrace
                              , _actTraceEffectTrace :: EffectTrace
                              , _actTraceIndex :: Int
                              }
             deriving (Eq, Ord, Show, Generic)

-- | How the death occurred.
data CauseOfDeath = Killed
                  -- ^ The player was killed.
                  | CommittedSuicide
                  -- ^ The player committed suicide.
                  | Lynched
                  -- ^ The player was lynched.
                  | ModKilled { _causeOfDeathReason :: T.Text }
                  -- ^ The player was killed by the moderator.
                  deriving (Eq, Ord, Show, Generic)

-- | Distinct effects that players can hold.
data EffectType = Death { _effectTypeCauseOfDeath :: CauseOfDeath }
                -- ^ The player is dead.
                | Granted { _effectTypeGrantedAction :: Action
                          -- ^ The action set granted.
                          , _effectTypeGrantedGroup :: ActionGroup
                          -- ^ Which action group this grant is part of.
                          , _effectTypeGrantedCompulsion :: Compulsion
                          -- ^ The compulsion for the grant.
                          , _effectTypeGrantedIrrevocable :: Bool
                          -- ^ Whether or not the grant is irrevocable.
                          }
                -- ^ The player was granted a set of mutually exclusive
               -- actions.
                | Vanillaized { _effectTypeRevokesIrrevocable :: Bool
                              -- ^ Whether or not irrevocable grants are
                              -- revoked. This should only be used if the player
                              -- is recruited.
                              }
                -- ^ The player has lost all of their granted actions.
                | Recruited { _effectTypeRecruitedFaction :: Faction
                            -- ^ The faction the player is recruited into.
                            , _effectTypeKnowsFactionMembers :: Bool
                            -- ^ The player knows the other members of the
                            -- faction.
                            , _effectTypeTraitor :: Bool
                            -- ^ The player is a traitor.
                            , _effectTypeAgenda :: Constraint
                            -- ^ The additional agenda the player must fulfill.
                            }
                -- ^ The player is part of a faction.
                | Framed { _effectTypeFramedFaction :: Faction
                         -- ^ The faction the player is framed as.
                         }
                -- ^ The player is framed as (pretends to be) part of a faction.
                | Reflex { _effectTypeReflex :: Action
                         -- ^ The reflex action the player will perform.
                         }
                -- ^ The player will perform an action when targeted.
                | Roleblocked
                -- ^ The player cannot perform actions.
                | Bulletproof
                -- ^ The player cannot be killed.
                | Macho
                -- ^ The player cannot be protected.
                | Ascetic
                -- ^ The player cannot be targeted by non-killing actions.
                | Untargetable
                -- ^ The player cannot be targeted at all.
                | Loved { _effectTypeLoverDeathAction :: Action
                        -- ^ The action the lover will perform when the player
                        -- dies.
                        , _effectTypeLover :: Player
                        -- ^ The lover.
                        }
                -- ^ The player, on death, will cause their lover to perform an
                -- action (usually suicide).
                | Redirected { _effectTypeRedirectee :: Player
                             -- ^ The target for redirection.
                             }
                -- ^ The player has all their actions targeting another player.
                | Deflected { _effectTypeDeflectee :: Player
                            -- ^ The target for deflection.
                            }
                -- ^ The player will deflect all actions targeted at them to
                -- another player.
                | Switched { _effectTypeSwitched1 :: Player
                           -- ^ A target for switching.
                           , _effectTypeSwitched2 :: Player
                           -- ^ A target for switching.
                           }
                -- ^ Specialized bidirectional version of 'Deflected'.
                | Bodyguarded { _effectTypeBodyguard :: Player
                              -- ^ The player's bodyguard.
                              }
                -- ^ The player will deflect kills onto their bodyguard unless
                -- their bodyguard is dead.
                | Bomb { _effectTypeBomb :: Action
                       -- ^ The action the player will perform when killed.
                       }
                -- ^ The player, when killed, will perform an action on their
                -- killer.
                | Vengeful { _effectTypeVengeance :: Action
                           -- ^ The action the player will perform when killed.
                           , _effectTypeVengee :: Player
                           -- ^ The target the player will perform their action
                           -- on,
                           }
                -- ^ The player, when killed, will perform an action on another
                -- player.
                | Gravedigger { _effectTypeGravedig :: Action
                              -- ^ The action that shows up to actions that
                              -- see gravedigger actions.
                              }
                -- ^ The player will be seen as targeting dead players.
                | Unlynchable
                -- ^ The player cannot be lynched.
                | Hiding { _effectTypeHider :: Player
                         -- ^ Someone hiding behind the player.
                         }
                -- ^ The player is hiding another player.
                | Friendship { _effectTypeFriend :: Player
                             -- ^ The player's friend.
                             }
                -- ^ The player is friends with another player.
                | VoteChanged { _effectTypeVoteDelta :: Int
                              -- ^ The amount their vote was changed by.
                              }
                -- ^ The player had their vote altered.
--                | Deferred { _effectTypeDeferred :: Effect
--                           }
--                -- ^ Defers an effect for the next turn.
                deriving (Eq, Ord, Show, Generic)

data ConstraintAtom = DuringPhase Phase
                    | FulfillsSelector Selector Player
                    | StartsAtTurn Turn
                    | EndsAtTurn Turn
                    | NPlusKthTurn Int Int
                    deriving (Eq, Ord, Show, Generic)

type Constraint = Expr ConstraintAtom

-- | An instance of an effect.
data Effect = Effect
    { _effectType :: EffectType
    -- ^ The type of the effect.

    , _effectUses :: Maybe Int
    -- ^ How many more uses the effect has. 'Nothing' for infinite.

    , _effectConstraint :: Constraint
    -- ^ Constraint for the effect.

    , _effectTrace :: EffectTrace
    -- ^ The trace for the effect.
    } deriving (Eq, Ord, Show, Generic)

-- | An effect trace traces an effect's origin to the originating act. If there
-- is no originating act, the effect must have been assigned to the user at the
-- start of the game.
--
-- This is a unique token for identifying effects.
data EffectTrace = EffectFromLynch { _effectTraceLynchTurn :: Turn }
                 -- ^ The origin of the effect was from lynching.
                 | EffectFromStart { _effectTraceIndex :: Int }
                 -- ^ The origin of the effect was from game start.
                 | EffectFromAct { _effectTraceActTrace :: ActTrace
                                 , _effectTraceIndex :: Int
                                 }
                 -- ^ The origin of the effect was from an act.
                 | EffectFromMod { _effectTraceIndex :: Int }
                 -- ^ The origin of the effect was from the moderator.
                 deriving (Eq, Ord, Show, Generic)

newtype Player = Player Int
                 deriving (Eq, Ord, Show, Generic)

-- | Traits defining factions.
data FactionTraits = FactionTraits
    { _factionTraitsIsPrimary :: Bool
    -- ^ Marks primary factions, i.e. a "last-standing" faction.
    } deriving (Show, Generic)

-- | Faction tokens.
newtype Faction = Faction Int
                  deriving (Eq, Ord, Show, Generic)

-- | Underlying info for messages.
data MessageInfo = GuiltInfo { _messageInfoIsGuilty :: Bool }
                 -- ^ An investigation result.
                 | PlayersInfo { _messageInfoPlayers :: S.Set Player }
                 -- ^ A result consisting of players.
                 | ActionsInfo { _messageInfoActions :: S.Set Action }
                 -- ^ A result consisting of action types.
                 | FruitInfo
                 -- ^ A piece of fruit.
                 | RoleInfo { _messageInfoPlayer :: Player }
                 -- ^ A result indicating role information should be displayed.
                 | GreetingInfo { _messageInfoGreeter :: Player
                                , _messageInfoGreeterFaction :: Faction
                                }
                 -- ^ A greeting from a player.
                 deriving (Show, Generic)

type Turn = Int

data Game = Game
    { _gamePlayers :: Players
    , _gameActions :: Actions
    , _gameFactions :: Factions
    , _gameTurn :: Turn
    , _gamePhase :: Phase
    , _gameModActionIndex :: Int
    , _gameHistory :: [Act]
    , _gameConsensus :: Consensus
    , _gameRng :: StdGen
    } deriving (Show, Generic)

data PlanBinding = PlanBinding
    { _planBindingAction :: Action
    , _planBindingSource :: Player
    , _planBindingCompulsion :: Compulsion
    } deriving (Eq, Ord, Show, Generic)

-- | Errors when trying to plan.
data PlanError = ActionNotPlannable
               -- ^ The action is not plannable by the source.
               | ActionNotPlanned
               -- ^ The action was not planned.
               | ActionAlreadyPlanned
               -- ^ The action was already planned.
               | CompulsionForced
                -- ^ The action has a 'Compulsion' of 'Forced'.
               | IncorrectNumberOfTargets
               -- ^ There are not the right number of targets for the action.
               | TargetInvalid (Index [Player])
               -- ^ One of the targets is invalid.
               deriving (Show, Generic)

-- | A plan to be executed at night.
type Plan = M.Map ActionGroup Act

data PlannerInfo = PlannerInfo
    { _plannerInfoPlayers :: Players
    , _plannerInfoActions :: Actions
    , _plannerInfoTurn :: Turn
    , _plannerInfoPhase :: Phase
    }

type Planner = StateT Plan (ReaderT PlannerInfo (Except PlanError))

-- | A message, which may occur for an act (see
-- 'Game.Cosanostra.Action.messagesFor').
data Message = Message
    { _messageInfo :: MessageInfo
    -- ^ The message's underlying info.

    , _messageActTrace :: ActTrace
    -- ^ The act that caused the message to be sent.

    , _messageAssociatesWithAct :: Bool
    -- ^ Whether the message should be associated with an act when returned.
    } deriving (Show, Generic)

data Consensus = StrictMajority
               -- ^ The player with the strict majority of votes will be
               -- lynched.
               | MostVotes
               -- ^ The player with the most votes will be lynched.
               deriving (Show, Eq, Generic)

type Ballot = M.Map Player Player
type Tally = M.Map Player Int

data Expr a = Atom a
            | Trivial
            | Impossible
            | And (Expr a) (Expr a)
            | Or (Expr a) (Expr a)
            | Not (Expr a)
            deriving (Eq, Ord, Show, Generic)

newtype Players = Players (M.Map Player [Effect])
                  deriving (Show, Eq, Generic)

type Factions = M.Map Faction FactionTraits
type Actions = M.Map Action ActionTraits

type ActionGroups = M.Map ActionGroup (S.Set PlanBinding)
