module tutorial.engines.template_behaviour;

import tutorial.engines.template_messages open;
import tutorial.engines.template_config open;
import tutorial.engines.template_environment 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;

syntax alias Val := Nat;

type FirstArgument :=
  mkFirstArgument@{
    data : Val;
  };

type SecondArgument :=
  mkSecondArgument@{
    data : String;
  };

type TemplateActionArgument :=
  | TemplateActionArgumentOne FirstArgument
  | TemplateActionArgumentTwo SecondArgument;

TemplateActionArguments : Type := List TemplateActionArgument;

TemplateAction : Type :=
  Action
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TemplateActionInput : Type :=
  ActionInput
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg;

TemplateActionEffect : Type :=
  ActionEffect
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TemplateActionExec : Type :=
  ActionExec
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

justHiAction (input : TemplateActionInput) : Option TemplateActionEffect :=
  let
    env := ActionInput.env input;
    args := ActionInput.args input;
  in case args of
       | TemplateActionArgumentTwo mkSecondArgument@{data := data} :: _ :=
         some
           mkActionEffect@{
             env :=
               env@EngineEnv{localState := mkTemplateLocalState@{
                                             taskQueue :=
                                               mkCustomData@{
                                                 word := data;
                                               };
                                           }};
             msgs := [];
             timers := [];
             engines := [];
           }
       | _ := none;

exampleReplyAction
  (input : TemplateActionInput) : Option TemplateActionEffect :=
  let
    cfg := ActionInput.cfg input;
    env := ActionInput.env input;
    trigger := ActionInput.trigger input;
    args := ActionInput.args input;
  in case getEngineMsgFromTimestampedTrigger trigger of
       | some mkEngineMsg@{
                msg := Anoma.MsgTemplate (TemplateMsgExampleRequest req);
                sender := sender;
                target := target;
                mailbox := mailbox;
              } :=
         some
           mkActionEffect@{
             env := env;
             msgs :=
               [
                 mkEngineMsg@{
                   sender := getEngineIDFromEngineCfg cfg;
                   target := sender;
                   mailbox := some 0;
                   msg :=
                     Anoma.MsgTemplate
                       (TemplateMsgExampleReply
                         (ok
                           mkExampleReplyOk@{
                             argOne := ExampleRequest.argOne req;
                           }));
                 };
               ];
             timers := [];
             engines := [];
           }
       | _ := none;

justHiActionLabel : TemplateActionExec := Seq [justHiAction];

exampleReplyActionLabel : TemplateActionExec := Seq [exampleReplyAction];

doBothActionLabel : TemplateActionExec :=
  Seq [justHiAction; exampleReplyAction];

TemplateGuard : Type :=
  Guard
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TemplateGuardOutput : Type :=
  GuardOutput
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

TemplateGuardEval : Type :=
  GuardEval
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

justHiGuard
  (trigger : TemplateTimestampedTrigger)
  (cfg : TemplateCfg)
  (env : TemplateEnv)
  : Option TemplateGuardOutput :=
  let
    emsg := getEngineMsgFromTimestampedTrigger trigger;
  in case emsg of
       | some mkEngineMsg@{msg := Anoma.MsgTemplate TemplateMsgJustHi} :=
         some
           mkGuardOutput@{
             action := justHiActionLabel;
             args :=
               [
                 TemplateActionArgumentTwo
                   mkSecondArgument@{
                     data := "Hello World!";
                   };
               ];
           }
       | _ := none;

exampleReplyGuard
  (trigger : TemplateTimestampedTrigger)
  (cfg : TemplateCfg)
  (env : TemplateEnv)
  : Option TemplateGuardOutput :=
  case getEngineMsgFromTimestampedTrigger trigger of
    | some mkEngineMsg@{
             msg := Anoma.MsgTemplate (TemplateMsgExampleRequest req);
             sender := mkPair none _;
           } -- from local engines only (NodeID is none)
    :=
      some
        mkGuardOutput@{
          action := exampleReplyActionLabel;
          args := [];
        }
    | _ := none;

TemplateBehaviour : Type :=
  EngineBehaviour
    TemplateLocalCfg
    TemplateLocalState
    TemplateMailboxState
    TemplateTimerHandle
    TemplateActionArguments
    Anoma.Msg
    Anoma.Cfg
    Anoma.Env;

module template_behaviour_example;
  
  exTemplateBehaviour : TemplateBehaviour :=
    mkEngineBehaviour@{
      guards := First [justHiGuard; exampleReplyGuard];
    };
end;