{# SPDX-License-Identifier: MIT #}
{# Copyright © 2021 André Santos #}

{% import 'py/common.py.jinja' as G %}
{% import 'py/predicates.py.jinja' as P %}

{##############################################################################}
{# TIMER MACROS #}
{##############################################################################}

{% macro _on_timer(sm) %}
{# this is called if there is a timeout; the size of the pool must be >= 1 #}
if self._state == {{ G.STATE_ACTIVE }}:
    assert len(self._pool) >= 1, 'missing trigger event'
    while self._pool and (stamp - self._pool[0].timestamp) >= {{ sm.timeout }}:
        self._pool.popleft()
    if not self._pool:
{{ G.change_to_state(G.STATE_SAFE, returns=false)|indent(8, first=true) }}
{%- endmacro %}


{##############################################################################}
{# MSG EVENT MACROS #}
{##############################################################################}

{% macro _on_msg(sm, event, topic, from_state) -%}
{% if event.event_type == G.EVENT_ACTIVATOR %}
{# after or after-until -#}
{{ G.activator_event(event, topic, s=G.STATE_SAFE) }}
{%- elif event.event_type == G.EVENT_TERMINATOR %}
{# until or after-until -#}
{{ G.terminator_event(sm, event, topic) }}
{%- elif event.event_type == G.EVENT_BEHAVIOUR %}
{{ _behaviour(sm, event, topic) }}
{%- elif event.event_type == G.EVENT_TRIGGER %}
{{ _trigger(sm, event, topic, from_state) }}
{%- else %}
assert False, 'unexpected event type: {{ event.event_type }}'
{%- endif %}
{%- endmacro %}

{% macro _behaviour(sm, event, topic) -%}
assert len(self._pool) >= 1, 'missing trigger event'
    {% if event.activator %}
assert len(self.witness) >= 1, 'missing activator event'
v_{{ event.activator }} = self.witness[0].msg
    {% endif %}
    {% if event.trigger %}
for rec in self._pool:
    v_{{ event.trigger }} = rec.msg
{{ _fail_if(sm, event.predicate, topic)|indent(4, first=true) -}}
    {%- else %}
rec = self._pool[0]
{{ _fail_if(sm, event.predicate, topic) -}}
    {%- endif %}
{%- endmacro %}

{% macro _fail_if(sm, phi, topic) -%}
    {% call G.change_to_state_if(phi, G.STATE_FALSE) %}
self.witness.append(rec)
self.witness.append(MsgRecord('{{ topic }}', stamp, msg))
{{ G.clear_pool(sm) }}
    {%- endcall %}
{%- endmacro %}

{% macro _trigger(sm, event, topic, from_state) -%}
    {% if event.activator %}
assert len(self.witness) >= 1, 'missing activator'
v_{{ event.activator }} = self.witness[0].msg
    {% endif %}
    {% if from_state == G.STATE_SAFE %}
        {% call G.change_to_state_if(event.predicate, G.STATE_ACTIVE) %}
{{ G.add_to_pool(sm, topic) }}
        {%- endcall %}
    {% elif from_state == G.STATE_ACTIVE %}
        {% if sm.pool_size == 1 and sm.timeout < 0 %}
return False # nothing to do
        {% else %}
            {% call G.do_if(event.predicate) %}
{{ G.add_to_pool(sm, topic) }}
            {%- endcall %}
        {% endif %}
    {% else %}
assert False, 'unexpected source state: {{ from_state }}'
    {% endif %}
{%- endmacro %}


{##############################################################################}
{# RENDERED CODE #}
{##############################################################################}

{# assume: global('state_machine') #}
{% call(cb) G.state_machine(state_machine) -%}
    {% if cb == G.CALLBACK_TIMER -%}
{{ _on_timer(state_machine) }}
    {%- elif cb == G.CALLBACK_MSG -%}
{{ _on_msg(state_machine, varargs[0], varargs[1], varargs[2]) }}
    {%- else -%}
assert False, 'unexpected macro function type: {{ args }}'
    {%- endif %}
{%- endcall %}
