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

[gcode_macro _load_unload]
gcode:
  {% set saved_extruder = printer.toolhead.extruder %}
  {% set EXTRUDER = params.EXTRUDER|default(saved_extruder)|lower %}
  {% set km = printer["gcode_macro _km_globals"] %}
  # Use the global min as default if provided, else use per extruder min + 5.
  {% set default_minimum = km.load_min_temp if km.load_min_temp else
         (printer.configfile.settings[EXTRUDER].min_extrude_temp + 5) %}
  {% if 'MINIMUM' in params %}
    {% set MINIMUM = params.MINIMUM|int %}
  # This is the special case for a filament change after an idle timeout.
  {% elif printer.pause_resume.is_paused and printer[EXTRUDER].target == 0 and
          printer["gcode_macro resume"].saved_extruder_temp %}
    {% set MINIMUM = printer["gcode_macro resume"].saved_extruder_temp %}
  # Use the target temp if higher than the default.
  {% elif printer[EXTRUDER].target > default_minimum %}
    {% set MINIMUM = printer[EXTRUDER].target %}
  {% else %}
    {% set MINIMUM = default_minimum %}
  {% endif %}
  {% if MINIMUM < printer.configfile.settings[EXTRUDER].min_extrude_temp %}
    { action_raise_error("Extrude below minimum temp.") }
  {% elif printer.pause_resume.is_paused %}
    # Clear the saved E if we're (un-)loading while paused.
    SET_GCODE_VARIABLE MACRO=resume VARIABLE=saved_e VALUE="{0.0}"
  {% endif %}
  {% set SPEED = params.SPEED|default(km.load_speed)|int %}
  {% set priming_length = km.load_priming_length %}
  {% set LENGTH = params.LENGTH|default(km.load_length)|float - priming_length%}
  {% if LENGTH < 0 %}
    {% set priming_length = (priming_length + LENGTH, 0)|max %}
    {% set LENGTH = 0 %}
  {% endif %}
  SAVE_GCODE_STATE NAME=_KM_LOAD_UNLOAD
  {% if EXTRUDER != saved_extruder%}
    ACTIVATE_EXTRUDER EXTRUDER={EXTRUDER}
  {% endif %}
  _KM_PRINT_STATUS ACTION=PUSH_STATUS
  {% if not printer.extruder.can_extrude or
        printer[EXTRUDER].target < default_minimum %}
    {action_respond_info("Preheating %s to %d" | format(EXTRUDER, MINIMUM))}
    M109 S{MINIMUM}
  {% endif %}
  _KM_PRINT_STATUS ACTION=CHANGE STATUS={"filament_load"
    if params.LOAD|int else "filament_unload"}
  M83
  {% set priming_speed = (km.load_priming_speed, SPEED)|min %}
  {% if params.LOAD|int %}
    G0 E{LENGTH} F{SPEED}
    G0 E{priming_length} F{priming_speed}
    G0 E{'%.4f' % -printer["gcode_macro resume"].saved_e} F{km.load_speed}
  {% else %}
    {% set pre_prime_distance = 5.0 %}
    {% set shaping_moves = 5 %}
    {% set shaping_distance = (shaping_moves * (1 + shaping_moves)/20) %}
    # Push forward to heat up, then retract to the edge of the meltzone.
    G0 E{pre_prime_distance} F{SPEED}
    G4 P500
    G0 E{'%.4f' % -(priming_length - shaping_distance)} F{SPEED}
    # Shape the tip with a pairs of short extrusions and retractions, where
    # the retractions get increasingly longer than the extrusion.
    {% for i in range(1, shaping_moves + 1) %}
      G0 E2 F{priming_speed}
      G0 E-{2 + (0.1 * i)} F{priming_speed}
    {% endfor %}
    # Retract the rest of the length.
    G0 E{'%.4f' % -(LENGTH + pre_prime_distance)} F{SPEED}
  {% endif %}
  RESTORE_GCODE_STATE NAME=_KM_LOAD_UNLOAD

  # Beep on completion
  _KM_BEEP_IF_AVAILABLE BEEPS={params.BEEPS|default(8)}
  _KM_PRINT_STATUS ACTION=CHANGE STATUS=pop_status

# TODO: Fix casing after front-ends get fixed
[gcode_macro LOAD_FILAMENT]
description: Loads filament to the extruder.
  Usage: LOAD_FILAMENT [LENGTH=<distance>] [SPEED=<speed>]
                       [EXTRUDER=<extruder>] [MINIMUM=<temperature>]
                       [BEEPS=<beep count on completion>]
gcode:
  _LOAD_UNLOAD LOAD=1{% for k in params
    %}{' '~k~'="'~params[k]~'"'}{% endfor %}

  # Dummy argument block for Mainsail
  {% set dummy = None if True else "
  M109
  {% set dummy = params.LENGTH|default(variable_load_length)|float %}
  {% set dummy = params.SPEED|default(variable_load_speed)|float %}
  {% set dummy = params.EXTRUDER|default(current extruder) %}
  {% set dummy = params.MINIMUM|default(min_extrude_temp)|int %}
  {% set dummy = params.BEEPS|default(10)|int %}
  " %} # End argument block for Mainsail

# TODO: Fix casing after front-ends get fixed
[gcode_macro UNLOAD_FILAMENT]
description: Unloads filament from the extruder.
  Usage: UNLOAD_FILAMENT [LENGTH=<distance>] [SPEED=<speed>]
                         [EXTRUDER=<extruder>] [MINIMUM=<temperature>]
                         [BEEPS=<beep count on completion>]
gcode:
  _LOAD_UNLOAD LOAD=0{% for k in params
    %}{' '~k~'="'~params[k]~'"'}{% endfor %}

  # Dummy argument block for Mainsail
  {% set dummy = None if True else "
  M109
  {% set dummy = params.LENGTH|default(variable_load_length)|float %}
  {% set dummy = params.SPEED|default(variable_load_speed)|float %}
  {% set dummy = params.EXTRUDER|default(default extruder) %}
  {% set dummy = params.MINIMUM|default(min_extrude_temp)|int %}
  {% set dummy = params.BEEPS|default(10)|int %}
  " %} # End argument block for Mainsail

[gcode_macro _pause_inner_m700]
gcode:
  {% set extruder = "extruder" ~ params.T|replace('0', '')
     if "T" in params else printer.toolhead.extruder %}
  {% if extruder != printer.toolhead.extruder%}
    ACTIVATE_EXTRUDER EXTRUDER={extruder}
  {% endif %}

  {% set z_param = (' Z="%f"' % params.Z) if 'Z' in params else "" %}

  {% if printer.idle_timeout.state|string == "Printing" %}
    PAUSE B="{0}" P=2{z_param}
  {% elif not printer.pause_resume.is_paused%}
    PARK B="{0}" P=2{z_param}
  {% endif %}

[gcode_macro m701]
description: Pauses/parks the toolhead then loads filament to the extruder.
  Usage: M701 [L<distance>] [Z<pos>] [T<extruder>]
gcode:
  {% if 'U' in params %}
    {% if not 'L' in params %}
      RESPOND TYPE=error MSG="Argument \"U\" substituted for missing \"L\"."
      {% set dummy = params.__setitem__("L", params.U) %}
    {% else %}
      RESPOND TYPE=error MSG="Invalid argument \"U\" ignored."
    {% endif %}
  {% endif %}

  _PAUSE_INNER_M700 {% for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}
  LOAD_FILAMENT BEEPS="{10}" LENGTH={
    params.L|default(printer["gcode_macro _km_globals"].load_length)|float}

[gcode_macro m702]
description: Pauses/parks the toolhead then unloads filament from the extruder.
  Usage: M702 [U<distance>] [Z<pos>] [T<extruder>]
gcode:
  {% if 'L' in params %}
    {% if not 'U' in params %}
      RESPOND TYPE=error MSG="Argument \"L\" substituted for missing \"U\"."
      {% set dummy = params.__setitem__("U", params.L) %}
    {% else %}
      RESPOND TYPE=error MSG="Invalid argument \"L\" ignored."
    {% endif %}
  {% endif %}

  _PAUSE_INNER_M700 {% for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}
  UNLOAD_FILAMENT BEEPS="{10}" LENGTH={
    params.U|default(printer["gcode_macro _km_globals"].load_length)|float}
