{-# LANGUAGE NoMonomorphismRestriction, FlexibleInstances, GADTs,
    UndecidableInstances, DeriveDataTypeable, FlexibleContexts,
    GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeFamilies,
    TypeSynonymInstances, TemplateHaskell, ExistentialQuantification,
    TypeFamilies, ScopedTypeVariables, StandaloneDeriving, NamedFieldPuns,
    EmptyDataDecls, QuasiQuotes #-}

-- | 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 Data.Data (Data)
import GHC.Read (readListPrecDefault, readListDefault, Read(..), lexP, parens)
import qualified Text.ParserCombinators.ReadPrec as ReadPrec (prec)
import Text.Read.Lex (Lexeme(..))
import Text.ParserCombinators.ReadPrec (reset)
import GHC.Show (showList__)
import Debug.Trace.Helpers (traceM)
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 GameName = String
type Code = String

-- * Nomyx Expression

-- | A Nomex (Nomyx Expression) allows the players to write rules.
-- | within the rules, you can access and modify the state of the game.
-- | It is a compositional algebra defined with a GADT.
data Nomex a where
   --Variable management
   NewVar       :: (Typeable a, Show a, Eq a) => VarName           -> a              -> Nomex (Maybe (V a))
   ReadVar      :: (Typeable a, Show a, Eq a) => (V a)             -> Nomex (Maybe a)
   WriteVar     :: (Typeable a, Show a, Eq a) => (V a)             -> a              -> Nomex Bool
   DelVar       ::                               (V a)             -> Nomex Bool
   --Events management
   OnEvent      :: (Typeable e, Show e, Eq e) => Event e           -> ((EventNumber, EventData e) -> Nomex ()) -> Nomex EventNumber
   DelEvent     ::                               EventNumber       -> Nomex Bool
   DelAllEvents :: (Typeable e, Show e, Eq e) => Event e           -> Nomex ()
   SendMessage  :: (Typeable a, Show a, Eq a) => Event (Message a) -> a              -> Nomex ()
   --Rules management
   ProposeRule  ::                               Rule              -> Nomex Bool
   ActivateRule ::                               RuleNumber        -> Nomex Bool
   RejectRule   ::                               RuleNumber        -> Nomex Bool
   AddRule      ::                               Rule              -> Nomex Bool
   DelRule      ::                               RuleNumber        -> Nomex Bool
   ModifyRule   ::                               RuleNumber        -> Rule           -> Nomex Bool
   GetRules     ::                               Nomex [Rule]
   --Players management
   GetPlayers   ::                               Nomex [PlayerInfo]
   SetPlayerName::                               PlayerNumber      -> PlayerName     -> Nomex Bool
   DelPlayer    ::                               PlayerNumber      -> Nomex Bool
   --Mileacenous
   SetVictory   ::                               [PlayerNumber]    -> Nomex ()
   Output       ::                               PlayerNumber      -> String         -> Nomex ()
   CurrentTime  ::                               Nomex UTCTime
   SelfRuleNumber ::                             Nomex RuleNumber
   --Monadic bindings
   Return       ::                               a                 -> Nomex a
   Bind         ::                               Nomex a           -> (a -> Nomex b) -> Nomex b
   ThrowError   ::                               String            -> Nomex a
   CatchError   ::                               Nomex a           -> (String -> Nomex a) -> Nomex a
   deriving (Typeable)

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

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

instance MonadError String Nomex where
   throwError = ThrowError
   catchError = CatchError

instance Show a => Show (Nomex a) where
   show _ = "Nomex" -- ++ (show a)





-- * Variables

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

-- | stores the variable's data
data Var = forall a . (Typeable a, Show a, Eq a) =>
        Var { _vRuleNumber :: Int,
              _vName :: String,
              vData :: a}

instance Show Var where
    show (Var a b c) = (show a) ++ " " ++ (show b) ++ " " ++ (show c)

instance Eq Var where
    Var a b c == Var d e f = (a,b,c) === (d,e,f)

type Output = (PlayerNumber, String)


-- * 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 InputChoice c  deriving Typeable
data InputString    deriving Typeable
data Victory        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)
    InputChoice :: (Eq c, Show c) => PlayerNumber -> String -> [c] -> c -> Event (InputChoice c)
    InputString :: PlayerNumber -> String -> Event InputString
    Victory     ::                           Event Victory

-- | data associated with each events
data EventData a where
    PlayerData      :: {playerData :: PlayerInfo}         -> EventData Player
    RuleData        :: {ruleData :: Rule}                 -> EventData RuleEvent
    TimeData        :: {timeData :: UTCTime}              -> EventData Time
    MessageData     :: (Show m) => {messageData :: m}     -> EventData (Message m)
    InputChoiceData :: (Show c) => {inputChoiceData :: c} -> EventData (InputChoice c)
    InputStringData :: {inputStringData :: String}        -> EventData InputString
    VictoryData     :: {victoryData :: [PlayerInfo]}      -> EventData Victory

deriving instance             Typeable1 EventData
deriving instance             Typeable1 Event
deriving instance (Show a) => Show      (Event a)
deriving instance (Show a) => Show      (EventData a)
deriving instance (Show a) => Show      (Message a)
deriving instance (Show a) => Show      (InputChoice a)
deriving instance             Show      Time
deriving instance             Show      InputString
deriving instance             Show      Victory
deriving instance             Eq        Time
deriving instance             Eq        Victory
deriving instance             Eq        EvRule
deriving instance             Eq        (InputChoice a)
deriving instance             Eq        InputString
deriving instance             Eq        (Message m)
deriving instance (Eq e) =>   Eq        (Event e)


data EventHandler where
    EH :: (Typeable e, Show e, Eq e) =>
        {_eventNumber :: EventNumber,
         _ruleNumber  :: RuleNumber,
         event       :: Event e,
         handler     :: (EventNumber, EventData e) -> Nomex ()} -> EventHandler

instance Show EventHandler where
    show (EH en rn e _) = (show en) ++ " " ++ " " ++ (show rn) ++ " (" ++ (show e) ++"),\n"

instance Eq EventHandler where
    (EH {_eventNumber=e1}) == (EH {_eventNumber=e2}) = e1 == e2

instance Ord EventHandler where
    (EH {_eventNumber=e1}) <= (EH {_eventNumber=e2}) = e1 <= e2

-- * Rule

-- | Type of a rule function.
type RuleFunc = Nomex RuleResp

-- | Return type of a rule function.
-- it can be either nothing or another rule.
data RuleResp =
      Void
    | Meta (Rule -> Nomex BoolResp)
    deriving (Typeable)
--An extended type for booleans supporting immediate or delayed response (through a message)
data BoolResp = BoolResp Bool
              | MsgResp (Event (Message Bool))


instance Show RuleResp where
   show _ = "RuleResp"
  
-- | An informationnal structure about a rule
data Rule = Rule { _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
                   _rRuleFunc     :: RuleFunc,         -- 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 Rule where
    (Rule {_rNumber=r1}) == (Rule {_rNumber=r2}) = r1 == r2

instance Ord Rule where
     (Rule {_rNumber=r1}) <= (Rule {_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)
                
data SubmitRule = SubmitRule RuleName RuleDesc RuleCode deriving (Show, Read, Eq, Ord, Data, Typeable)


-- * Player

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

-- * Game
           
-- | The state of the game:
data Game = Game { _gameName      :: GameName,
                   _gameDesc      :: GameDesc,
                   _rules         :: [Rule],
                   _players       :: [PlayerInfo],
                   _variables     :: [Var],
                   _events        :: [EventHandler],
                   _outputs       :: [Output],
                   _victory       :: [PlayerNumber],
                   _currentTime   :: UTCTime
                 }
                   deriving (Typeable)
                   
data GameDesc = GameDesc { _desc :: String, _agora :: String} deriving (Eq, Show, Read, Ord)

--instance Show Game where
--    show (Game { _gameName, _rules, _players, _variables, _events, _outputs, _victory, _currentTime}) =
--        "Game Name = " ++ (show _gameName) ++ "\n Rules = " ++ (concat $ intersperse "\n " $ map show _rules) ++ "\n Players = " ++ (show _players) ++ "\n Variables = " ++
--        (show _variables) ++ "\n Events = " ++ (show _events) ++ "\n Outputs = " ++ (show _outputs) ++ "\n Victory = " ++ (show _victory) ++ "\n currentTime = " ++ (show _currentTime) ++ "\n"

instance Eq Game where
   (Game {_gameName=gn1}) == (Game {_gameName=gn2}) = gn1 == gn2

instance Ord Game where
   compare (Game {_gameName=gn1}) (Game {_gameName=gn2}) = compare gn1 gn2

--Game is not serializable in its entierety. We serialize only the adequate parts.   
instance Read Game where
  readPrec = parens $ ReadPrec.prec 11 $ do
     Ident "Game" <- lexP;
     Punc "{" <- lexP;
     Ident "_gameName" <- lexP;
     Punc "=" <- lexP;
     a1 <- reset readPrec;
     Punc "," <- lexP;
     Ident "_gameDesc" <- lexP;
     Punc "=" <- lexP;
     a2 <- reset readPrec;
     Punc "," <- lexP;
     Ident "_currentTime" <- lexP;
     Punc "=" <- lexP;
     a3 <- reset readPrec;
     Punc "}" <- lexP;
     return $ Game a1 a2 [] [] [] [] [] [] a3
  readList = readListDefault
  readListPrec = readListPrecDefault

instance Show Game where
   showsPrec p(Game b1 b2 _ _ _ _ _ _ b3) = showParen (p >= 11) $
      showString "Game {" .
      showString "_gameName = " .
      showsPrec 0 b1 .
      showString ", " .
      showString "_gameDesc = " .
      showsPrec 0 b2 .
      showString ", " .
      showString "_currentTime = " .
      showsPrec 0 b3 .
      showString "}"
   showList = showList__ (showsPrec 0)

-- | an equality that tests also the types.
(===) :: (Typeable a, Typeable b, Eq b) => a -> b -> Bool
(===) x y = cast x == Just y

-- | Replaces all instances of a value in a list by another value.
replaceWith :: (a -> Bool)   -- ^ Value to search
        -> a   -- ^ Value to replace it with
        -> [a] -- ^ Input list
        -> [a] -- ^ Output list
replaceWith f y = map (\z -> if f z then y else z)

tracePN :: (Monad m ) => PlayerNumber -> String -> m ()
tracePN pn s = traceM $ "Player " ++ (show pn) ++ " " ++ s
    
$( makeLenses [''Game, ''GameDesc, ''Rule, ''PlayerInfo, ''EventHandler, ''Var] )