{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}

-- | This module containt the type definitions necessary to build a Nomic rule. 
module Language.Nomyx.Expression where

import Data.Typeable
import Data.Time
import Control.Applicative hiding (Const)
import Data.Lens.Template
import Control.Monad.Error

type PlayerNumber = Int
type PlayerName = String
type RuleNumber = Int
type RuleName = String
type RuleDesc = String
type RuleText = String
type RuleCode = String
type EventNumber = Int
type EventName = String
type VarName = String
type Code = String
type OutputNumber = Int

-- * Nomyx Expression

data Eff = Effect | NoEffect
type Effect = 'Effect
type NoEffect = 'NoEffect

-- | A Nomex (Nomyx Expression) allows the players to write rules.
-- within the rules, you can access and modify the state of the game.
type Nomex = Exp Effect

-- | A NomexNE (Nomyx Expression No Effect) is a specialisation of the type that guaranties
-- that the instructions will have no effects.
type NomexNE = Exp NoEffect

data Exp :: Eff -> * -> *   where
   --Variables management
   NewVar         :: (Typeable a, Show a) => VarName -> a -> Nomex (Maybe (V a))
   ReadVar        :: (Typeable a, Show a) => V a -> Exp NoEffect (Maybe a)
   WriteVar       :: (Typeable a, Show a) => V a -> a -> Nomex Bool
   DelVar         :: (V a) -> Nomex Bool
   --Events management
   OnEvent        :: Typeable e => Event e -> ((EventNumber, EventData e) -> Nomex ()) -> Nomex EventNumber
   DelEvent       :: EventNumber -> Nomex Bool
   DelAllEvents   :: Typeable e => Event e -> Nomex ()
   SendMessage    :: (Typeable a, Show a) => Event (Message a) -> a -> Nomex ()
   --Rules management
   ProposeRule    :: RuleInfo -> Nomex Bool
   ActivateRule   :: RuleNumber -> Nomex Bool
   RejectRule     :: RuleNumber -> Nomex Bool
   AddRule        :: RuleInfo -> Nomex Bool
   ModifyRule     :: RuleNumber -> RuleInfo -> Nomex Bool
   GetRules       :: NomexNE [RuleInfo]
   --Players management
   GetPlayers     :: NomexNE [PlayerInfo]
   SetPlayerName  :: PlayerNumber -> PlayerName -> Nomex Bool
   DelPlayer      :: PlayerNumber -> Nomex Bool
   --Outputs
   NewOutput      :: Maybe PlayerNumber -> NomexNE String -> Nomex OutputNumber
   GetOutput      :: OutputNumber -> NomexNE (Maybe String)
   UpdateOutput   :: OutputNumber -> NomexNE String -> Nomex Bool
   DelOutput      :: OutputNumber -> Nomex Bool
   --Mileacenous
   SetVictory     :: NomexNE [PlayerNumber] -> Nomex ()
   CurrentTime    :: NomexNE UTCTime
   SelfRuleNumber :: NomexNE RuleNumber
   --Monadic bindings
   Return         :: a -> Exp e a
   Bind           :: Exp e a -> (a -> Exp e b) -> Exp e b
   ThrowError     :: String -> Exp Effect a
   CatchError     :: Nomex a -> (String -> Nomex a) -> Nomex a
   LiftEffect     :: NomexNE a -> Nomex a
   Simu           :: Nomex a -> NomexNE Bool -> NomexNE Bool

instance Typeable1 (Exp NoEffect) where
    typeOf1 _ = mkTyConApp (mkTyCon3 "main" "Language.Nomyx.Expression" "Exp NoEffect") []

instance Typeable1 (Exp Effect) where
    typeOf1 _ = mkTyConApp (mkTyCon3 "main" "Language.Nomyx.Expression" "Exp Effect") []

liftEffect :: NomexNE a -> Nomex a
liftEffect = LiftEffect  

instance Monad (Exp a) where
   return = Return
   (>>=) = Bind
   
instance Functor (Exp a) where
   fmap f e = Bind e $ Return . f

instance Applicative (Exp a) where
   pure = Return
   f <*> a = do
      f' <- f
      a' <- a
      return $ f' a'

instance MonadError String Nomex where
   throwError = ThrowError
   catchError = CatchError

instance Typeable a => Show (Exp NoEffect a) where
   show e = "<" ++ (show $ typeOf e) ++ ">"

instance Typeable a => Show (Exp Effect a) where
   show e = "<" ++ (show $ typeOf e) ++ ">"

instance (Typeable a, Typeable b) => Show (a -> b) where
    show e = '<' : (show . typeOf) e ++ ">"

-- * Variables

-- | a container for a variable name and type
data V a = V {varName :: String} deriving Typeable

-- * Events

-- | events types
data Player = Arrive | Leave deriving (Typeable, Show, Eq)
data RuleEvent = Proposed | Activated | Rejected | Added | Modified | Deleted deriving (Typeable, Show, Eq)
data Time           deriving Typeable
data EvRule         deriving Typeable
data Message m      deriving Typeable
data Victory        deriving Typeable
data Input a = Input PlayerNumber String (InputForm a) deriving Typeable
data InputForm a = Radio [(a, String)]
                 | Text
                 | TextArea
                 | Button
                 | Checkbox [(a, String)]
                 deriving Typeable

-- | events names
data Event a where
    Player      :: Player ->                     Event Player
    RuleEv      :: RuleEvent ->                  Event RuleEvent
    Time        :: UTCTime ->                    Event Time
    Message     :: String ->                     Event (Message m)
    InputEv     :: (Typeable a, Show a, Eq a) => Input a -> Event (Input a)
    Victory     ::                               Event Victory
    deriving (Typeable)

-- data sent back by inputs
data InputData a = RadioData a
                 | CheckboxData [a]
                 | TextData String
                 | TextAreaData String
                 | ButtonData

                  
-- | data associated with each events
data EventData a where
    PlayerData  ::             {playerData :: PlayerInfo}    -> EventData Player
    RuleData    ::             {ruleData :: RuleInfo}        -> EventData RuleEvent
    TimeData    ::             {timeData :: UTCTime}         -> EventData Time
    MessageData :: (Show m) => {messageData :: m}            -> EventData (Message m)
    InputData   :: (Show a) => {inputData :: InputData a}    -> EventData (Input a)
    VictoryData ::             {victoryData :: VictoryCond}  -> EventData Victory
    deriving (Typeable)

deriving instance             Show      (Event a)
deriving instance (Show a) => Show      (InputForm a)
deriving instance (Show a) => Show      (Input a)
deriving instance             Show      (EventData a)
deriving instance (Show a) => Show      (InputData a)
deriving instance (Show a) => Show      (Message a)
deriving instance             Show      Victory
deriving instance             Show      Time
deriving instance             Eq        Time
deriving instance             Eq        Victory
deriving instance             Eq        EvRule
deriving instance             Eq        (Message m)
deriving instance             Eq        (Event e)
deriving instance (Eq e) =>   Eq        (Input e)
deriving instance (Eq e) =>   Eq        (InputForm e)


type Msg a = Event (Message a)
type MsgData a = EventData (Message a)

-- * Rule

-- | Type of a rule function.
type Rule = Nomex ()
  
-- | An informationnal structure about a rule
data RuleInfo = RuleInfo { _rNumber      :: RuleNumber,       -- number of the rule (must be unique) TO CHECK
                           _rName        :: RuleName,         -- short name of the rule 
                           _rDescription :: String,           -- description of the rule
                           _rProposedBy  :: PlayerNumber,     -- player proposing the rule
                           _rRuleCode    :: Code,             -- code of the rule as a string
                           _rRule        :: Rule,             -- function representing the rule (interpreted from rRuleCode)
                           _rStatus      :: RuleStatus,       -- status of the rule
                           _rAssessedBy  :: Maybe RuleNumber} -- which rule accepted or rejected this rule
                           deriving (Typeable, Show)

instance Eq RuleInfo where
    (RuleInfo {_rNumber=r1}) == (RuleInfo {_rNumber=r2}) = r1 == r2

instance Ord RuleInfo where
     (RuleInfo {_rNumber=r1}) <= (RuleInfo {_rNumber=r2}) = r1 <= r2

-- | the status of a rule.
data RuleStatus = Active      -- Active rules forms the current Constitution
                | Pending     -- Proposed rules
                | Reject      -- Rejected rules
                deriving (Eq, Show, Typeable)
          
-- * Player

-- | informations on players
data PlayerInfo = PlayerInfo { _playerNumber :: PlayerNumber,
                               _playerName   :: String,
                               _playAs       :: Maybe PlayerNumber}
                               deriving (Eq, Typeable, Show)

instance Ord PlayerInfo where
   h <= g = (_playerNumber h) <= (_playerNumber g)

-- * Victory

data VictoryCond = VictoryCond RuleNumber (NomexNE [PlayerNumber]) deriving (Show, Typeable)

partial :: String -> Nomex (Maybe a) -> Nomex a
partial s nm = do
   m <- nm
   case m of
      Just a -> return a
      Nothing -> throwError s

concatMapM        :: (Monad m) => (a -> m [b]) -> [a] -> m [b]
concatMapM f xs   =  liftM concat (mapM f xs)

$( makeLenses [''RuleInfo, ''PlayerInfo] )