module arch.node.engines.ticker_behaviour;

import arch.node.engines.ticker_messages open;
import arch.node.engines.ticker_config open;
import arch.node.engines.ticker_environment open;

import prelude open;
import arch.node.types.basics open;
import arch.node.types.identities open;
import arch.node.types.messages open;
import arch.node.types.engine open;
import arch.node.types.anoma as Anoma open;

type ReplyTo :=
  mkReplyTo@{
    whoAsked : Option EngineID;
    mailbox : Option MailboxID;
  };

type TickerActionArgument := | TickerActionArgumentReplyTo ReplyTo;

TickerActionArguments : Type := List TickerActionArgument;

TickerAction : Type :=
  Action
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TickerActionInput : Type :=
  ActionInput
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg;

TickerActionEffect : Type :=
  ActionEffect
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TickerActionExec : Type :=
  ActionExec
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

incrementAction (input : TickerActionInput) : Option TickerActionEffect :=
  let
    env := ActionInput.env input;
    counterValue := TickerLocalState.counter (EngineEnv.localState env);
  in some
    mkActionEffect@{
      env :=
        env@EngineEnv{localState := mkTickerLocalState@{
                                      counter := counterValue + 1;
                                    }};
      msgs := [];
      timers := [];
      engines := [];
    };

countReplyAction (input : TickerActionInput) : Option TickerActionEffect :=
  let
    cfg := ActionInput.cfg input;
    env := ActionInput.env input;
    trigger := ActionInput.trigger input;
    counterValue := TickerLocalState.counter (EngineEnv.localState env);
  in case getEngineMsgFromTimestampedTrigger trigger of
       | some emsg :=
         some
           mkActionEffect@{
             env := env;
             msgs :=
               [
                 mkEngineMsg@{
                   sender := getEngineIDFromEngineCfg cfg;
                   target := EngineMsg.sender emsg;
                   mailbox := some 0;
                   msg :=
                     Anoma.MsgTicker
                       (TickerMsgCountReply
                         mkCountReply@{
                           counter := counterValue;
                         });
                 };
               ];
             timers := [];
             engines := [];
           }
       | _ := none;

incrementActionLabel : TickerActionExec := Seq [incrementAction];

countReplyActionLabel : TickerActionExec := Seq [countReplyAction];

TickerGuard : Type :=
  Guard
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TickerGuardOutput : Type :=
  GuardOutput
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TickerGuardEval : Type :=
  GuardEval
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

incrementGuard
  (trigger : TimestampedTrigger TickerTimerHandle Anoma.Msg)
  (cfg : TickerCfg)
  (env : TickerEnv)
  : Option TickerGuardOutput :=
  let
    emsg := getEngineMsgFromTimestampedTrigger trigger;
  in case emsg of
       | some mkEngineMsg@{msg := Anoma.MsgTicker TickerMsgIncrement} :=
         some
           mkGuardOutput@{
             action := incrementActionLabel;
             args := [];
           }
       | _ := none;

countReplyGuard
  (trigger : TimestampedTrigger TickerTimerHandle Anoma.Msg)
  (cfg : TickerCfg)
  (env : TickerEnv)
  : Option TickerGuardOutput :=
  case getEngineMsgFromTimestampedTrigger trigger of
    | some mkEngineMsg@{msg := Anoma.MsgTicker TickerMsgCountRequest} :=
      some
        mkGuardOutput@{
          action := countReplyActionLabel;
          args := [];
        }
    | _ := none;

TickerBehaviour : Type :=
  EngineBehaviour
    TickerLocalCfg
    TickerLocalState
    TickerMailboxState
    TickerTimerHandle
    TickerActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

tickerBehaviour : TickerBehaviour :=
  mkEngineBehaviour@{
    guards := First [incrementGuard; countReplyGuard];
  };