# Copyright (C) 2023 Justin Schuh <code@justinschuh.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.


[gcode_macro _km_print_status]
variable_last_status: 'none'
variable_status_stack: []
variable_command_list: {}
variable_status_list: [
  'ready', # Printer is ready to receive a job
  'filament_load', # Loading filament
  'filament_unload', # Unloading filament
  'bed_heating', # Waiting for the bed to reach target
  'chamber_heating', # Waiting for the chamber to reach target
  'homing', # Homing any axis
  'leveling_gantry', # Performing quad gantry-leveling
  'calibrating_z', # Performing z-tilt adjustment
  'meshing', # Calibrating a bed mesh
  'extruder_heating', # Waiting for the extruder to reach target
  'purging', # Printing purge line
  'printing', # Actively printing
  'pausing', # Print is paused
  'cancelling', # Print is being cancelled
  'completing', # Print completed
  ]
gcode:
  {% set ACTION = params.ACTION|trim|upper %}
  {% set STATUS = params.STATUS|default("")|trim|lower %}

  # Add a status handler.
  {% if ACTION == "ADD_EVENT" %}
    {% set COMMAND = params.COMMAND %}
    {% set ARGS = params.ARGS|default(0)|int != 0 %}
    {% set TYPE = params.TYPE|default('ENTER')|trim|upper %}
    {% set WHEN = params.WHEN|default('PRINTING')|trim|upper %}
    {% set FILTER_ENTER =
             (params.FILTER_ENTER|default("")|trim|lower).split(',')|
               select()|unique|list %}
    {% set FILTER_LEAVE =
             (params.FILTER_LEAVE|default("")|trim|lower).split(',')|
               select()|unique|list %}

    {% set STATUSES = STATUS.split(',')|map('trim')|list %}
    {% for s in (STATUSES if STATUSES[0] != 'all' else status_list) %}
      {% if not s in command_list %}
        {% set dummy = command_list.__setitem__(s,[]) %}
      {% endif %}
      {% set dummy = command_list[s].append({'cmd':COMMAND, 'args':ARGS,
                                             'type':TYPE, 'when':WHEN,
                                             'filt_e':FILTER_ENTER,
                                             'filt_l':FILTER_LEAVE}) %}
    {% endfor %}

  # Change the current status.
  {% elif ACTION == "CHANGE" %}
    {% if params.RESET_STACK|default(0)|int %}
      {% for dummy in range(status_stack|length) %}
        {% set dummy = status_stack.pop() %}
      {% endfor %}
      # TODO: Once python 3 is required, switch the for loop above to this:
      #{% set dummy = status_stack.clear() %}
    {% elif STATUS == "pop_status" %}
      {% set STATUS = (status_stack|default(['ready'], True)).pop() %}
    {% endif %}

    {% if STATUS not in status_list %}
      {action_raise_error("Status '%s' not valid."|format(STATUS))}
    {% endif %}

    {% if STATUS != last_status %}
      {% set is_printing =
           printer["gcode_macro print_start_set"].print.PRINT_START_PHASE|
             default("")|length != 0 %}
      {% set print_states = ('ALWAYS', 'PRINTING' if is_printing else 'IDLE') %}
      # Process commands for the state we're leaving first.
      {% for c in command_list[last_status]|default([]) %}
        {% if c.type in ('BOTH','LEAVE') and c.when in print_states and
              (not c.filt_l or STATUS in c.filt_l) %}
          M400
          {c.cmd}{% if c.args %} TYPE=LEAVE WHEN={print_states[1]
            } LAST_STATUS={last_status} NEXT_STATUS={STATUS}{% endif %}
        {% endif %}
      {% endfor %}
      # Process commands for the state we're entering last.
      {% for c in command_list[STATUS]|default([]) %}
        {% if c.type in ('BOTH','ENTER') and c.when in print_states and
              (not c.filt_e or STATUS in c.filt_e) %}
          M400
          {c.cmd}{% if c.args %} TYPE=ENTER WHEN={print_states[1]
            } LAST_STATUS={last_status} NEXT_STATUS={STATUS}{% endif %}
        {% endif %}
      {% endfor %}
      SET_GCODE_VARIABLE MACRO=_km_print_status VARIABLE=last_status VALUE="'{
        STATUS}'"
    {% endif %}

  # Push the current status onto the stack.
  {% elif ACTION == "PUSH_STATUS" %}
    {% set dummy = status_stack.append(last_status) %}

  # Illegal operation.
  {% else %}
    {action_raise_error("Action '%s' not valid."|format(ACTION))}
  {% endif %}

[gcode_macro gcode_on_print_status]
description: Adds a gcode command for a status event.
  Usage: GCODE_ON_PRINT_STATUS STATUS=<status> COMMAND=<gcode> [ARGS=<1|0>]
                               [WHEN=<PRINTING|IDLE|ALWAYS>]
                               [TYPE=<ENTER|LEAVE|BOTH>]
                               [FILTER_ENTER=<status list>]
                               [FILTER_LEAVE=<status list>]
gcode:
  {% set STATUS = (params.STATUS|lower).split(',')|map('trim')|list %}
  {% set TYPE = params.TYPE|default('ENTER')|trim|upper %}
  {% set WHEN = params.WHEN|default('PRINTING')|trim|upper %}
  {% set FILTER_ENTER =
       (params.FILTER_ENTER|default("")|trim|lower).split(',')|select()|list %}
  {% set FILTER_LEAVE =
       (params.FILTER_LEAVE|default("")|trim|lower).split(',')|select()|list %}

  # Error checking
  {% set status_list = printer["gcode_macro _km_print_status"].status_list %}
  {% for s in STATUS %}
    {% if not (s in status_list or s == 'all' and STATUS|length == 1) %}
      {action_raise_error("STATUS parameter '%s' not valid."|format(s))}
    {% endif %}
  {% endfor %}

  {% if TYPE not in ('ENTER', 'LEAVE', 'BOTH') %}
    {action_raise_error("TYPE paramater '%s' not valid."|format(TYPE))}
  {% elif WHEN not in ('PRINTING', 'IDLE', 'ALWAYS') %}
    {action_raise_error("WHEN parameter '%s' not valid."|format(WHEN))}
  {% elif TYPE == 'ENTER' and FILTER_LEAVE %}
    {action_raise_error("FILTER_LEAVE not valid with TYPE=ENTER.")}
  {% elif TYPE == 'LEAVE' and FILTER_ENTER %}
    {action_raise_error("FILTER_ENTER not valid with TYPE=LEAVE.")}
  {% endif %}
  {% for f in FILTER_ENTER %}
    {% if f not in status_list %}
      {action_raise_error("FILTER_ENTER parameter '%s' not valid."|format(f))}
    {% endif %}
  {% endfor %}
  {% for f in FILTER_LEAVE %}
    {% if f not in status_list %}
      {action_raise_error("FILTER_LEAVE parameter '%s' not valid."|format(f))}
    {% endif %}
  {% endfor %}

  # Run the command.
  _KM_PRINT_STATUS ACTION=ADD_EVENT {rawparams}
