<'
# Copyright (c) 2012-2015,2017,2020-2023, 2025-2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution and use  in source  and binary  forms,  with or without
# modification, are permitted provided that the following conditions are
# met:
#
#   1. Redistributions of  source  code must retain the  above copyright
#      notice and this list of conditions.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice and  this list of  conditions in the  documentation and/or
#      other materials provided with the distribution.
#
#                                      Anthony Mallet on Wed Aug  1 2012
#

args components
lang c
'>
#include <string.h>

#include "activity.h"
#include "component.h"
#include "prof.h"

<'foreach component $components {'>
<'  set comp [$component name]'>

/* <"[--- $comp =========================================================]"> */

/* codels */
<"[language mapping [$component codels] off]">

<'  foreach task [$component tasks] {'>
/* <"[--- [$task name] ==================================================]"> */
<'    foreach obj [list $task {*}[$task services]] {'>

/* <"[--- [$obj class] [$obj name] --------------------------------------]"> */
<'      foreach state [$obj fsm] {'>
<'        set codel [$obj fsm $state]'>
<'        set func [$obj class]_[$obj name]_[$state name]'>

/* state <"[$state name]"> */
static genom_event
<"$comp">_codel_<"$func">(
  union genom_<"$comp">_activity_any *a, struct genom_<"$comp">_component *self)
{
  genom_event s;

  genom_prof_decl(event);
  genom_prof_enter(&event);

  genom_take_resource(
    self,
<'        foreach m [$codel mutex] {'>
<'          switch [$m class] {'>
<'            codel { set rname [$m cname] }'>
<'            service { set rname genom_${comp}_svc_[$m name]_controlcb }'>
<'            default { error "unsupported object" }'>
<'          }'>
<'          if {[catch {$m task} t]} {'>
    self->resources.control == <"$rname"> ||
<'          } else {'>
    self->resources.task_<"[$t name]"> == <"$rname"> ||
<'          }'>
<'        }'>
    self->resources.all,

    self->resources.task_<"[$task name]"> = <"[$codel cname]">);
<'
        # build parameter list
        set plist [list]
        foreach p [$codel parameters] {
          switch -glob -- [$p src] {
            local {
              if {[$obj class] eq "task"} {
                error "task codel with service parameter"
              }
              set m "a->[$obj name]_.params.[$p name]"
            }
            remote	{ set m "self->remotes.[[$p remote] name]_" }
            port	{ set m "self->ports.[[$p port] name]_" }
            ids		{ set m "self->ids[$p member]" }
            default	{ error "unsupported codel parameter source" }
          }

          switch  -glob -- "[$p dir]/[$p src]" {
            in/* - */port	{ lappend plist [[$p type] pass value $m] }
            default		{ lappend plist [[$p type] pass reference $m] }
          }
        }
        lappend plist "&self->tasks.[$task name]_.context"
'>
  genom_prof_start(&event);
  s = <"[$codel invoke $plist]">;
  genom_give_resource(self, self->resources.task_<"[$task name]"> = NULL);
  genom_prof_leave(&event);

  genom_prof_collect(
    &event, self->instance,
    "<"[$task name]">", "<"[$obj name]">", "<"[$codel cname]">",
    a->any.state, s);
  return s;
}
<'      }'>

/* invoke codels according to <"[$obj name]"> state */
static enum genom_activity_status
<"$comp">_invoke_<"[$obj class]_[$obj name]">(
  union genom_<"$comp">_activity_any *a, struct genom_<"$comp">_component *self)
{
<'      if {[llength [$obj fsm]]} {'>
  genom_event s;
<'      }'>

  genom_log_debug(
    self->instance,
    "<"task [$task name] invoking [$obj class] [$obj name] %s">", a->any.state);

<'      foreach state [$obj fsm] {'>
<'        set func [$obj class]_[$obj name]_[$state name]'>
  if (a->any.state == <"[$state cname]">
<'        if {[$state name] in {start stop}} {'>
      || a->any.state == <"$comp">_<"[$state name]"> /* default external event */
<'        }'>
    ) {
    s = <"$comp">_codel_<"$func">(a, self);
    genom_log_debug(self->instance, "<"[$obj class] [$obj name]"> yielded %s", s);
    if (
<'        foreach y [[$obj fsm $state] yield] {'>
<'          if {"pause" in [$y kind]} continue'>
<'          if {"ether" eq [$y name]} continue'>
      s == <"[$y cname]"> ||
<'        }'>
      0) {
      a->any.state = s;
      return ACT_RUN;
    }
    if (
<'        foreach y [[$obj fsm $state] yield] {'>
<'          if {"pause" in [$y kind]} { '>
      (s == <"[$y cname]"> && (s = <"[[$obj fsm [$y name] type] cname]">)) ||
<'          }'>
<'        }'>
      0) {
      a->any.state = s;
      a->any.pause = 1;
      return ACT_RUN;
    }
    if (
<'        foreach y [[$obj fsm $state] yield] {'>
<'          if {"ether" eq [$y name]} {'>
      s == <"[$y cname]"> ||
<'          }'>
<'        }'>
      0) {
      a->any.state = <"$comp">_ether;
      return ACT_ETHER;
    }
    goto ex;
  }

<'      }'>
<'      if {[$obj class] eq "task"} {'>
  /* noop default task start and stop, if not already handled above */
  if (a->any.state == <"$comp">_start || a->any.state == <"$comp">_stop) {
    a->any.state = <"$comp">_ether;
    return ACT_ETHER;
  }
<'      } else {'>
  /* default activity stop, if not already handled above */
  if (a->any.state == <"$comp">_stop) {
    genom_interrupted_detail d;

    assert(a->any.interruptedby);
    strncpy(d.by, a->any.interruptedby, sizeof(d.by));
    d.by[sizeof(d.by)-1] = *"";
    s = genom_interrupted(&d, &self->tasks.<"[$task name]">_.context);
    goto ex;
  }
<'      }'>

  /* this cannot happen by construction */
  genom_log_warn(
    self->instance, "bad state (%s) in <"[$obj class]_[$obj name]">",
    a->any.state?a->any.state : "genom_ok");
  abort();
  /*NOTREACHED*/

<'      if {[llength [$obj fsm]]} {'>
ex:
  if (
<'        foreach t [$obj throw] {'>
    s != <"[$t cname]">_id &&
<'        }'>
    1) {
    genom_bad_transition_detail d;
    strncpy(d.from, a->any.state, sizeof(d.from));
    d.from[sizeof(d.from)-1] = *"";
    strncpy(d.to, s, sizeof(d.to));
    d.to[sizeof(d.to)-1] = *"";
    genom_log_warn(
      self->instance,
      "bad transition from %s to %s in <"[$obj class] [$obj name]">",
      a->any.state, s?s:"genom_ok");
    s = genom_bad_transition(&d, &self->tasks.<"[$task name]">_.context);
  }

  a->any.exdetail = self->tasks.<"[$task name]">_.context.raised(
    &a->any.state, &self->tasks.<"[$task name]">_.context);
<'      }'>
  return ACT_ETHER;
}

<'    }'>


/* <"[--- task [$task name] invocation ----------------------------------]"> */

enum genom_activity_status
genom_<"$comp">_<"[$task name]">_invoke(
  union genom_<"$comp">_activity_any *activity,
  struct genom_<"$comp">_component *self)
{
  switch(activity->any.sid) {
    case -1:
      return <"${comp}_invoke_task_[$task name]">(activity, self);

<'    foreach s [$task services] {'>
    case <"$comp">_<"[$s name]">_SVCID:
      return <"${comp}_invoke_[$s class]_[$s name]">(activity, self);
<'    }'>
  }

  /* cannot happen */
  genom_log_warn(
    self->instance,
    "bad service id %d in task <"[$task name]">", activity->any.sid);
  abort();
  return ACT_VOID;
}

<'  }'>
<'}'>
/* eof */
