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

[gcode_macro set_heater_temperature_scaled]
gcode:
  {% set HEATER = params.HEATER.lower() %}
  {% set TARGET = params.TARGET|default(0.0)|float %}
  {% set scaled_heaters =
           printer["gcode_macro set_heater_scaling"].scaled_heaters %}
  {% if HEATER in scaled_heaters %}
    {% set scales = scaled_heaters[HEATER] %}
    {% set dummy = scales.__setitem__("real_target", TARGET) %}
    {%if TARGET > 0.0 %}
      {% set TARGET = (((TARGET + scales.bump) * scales.scale, scales.minimum)
                       | max, scales.maximum)|min %}
    {% endif %}
  {% endif %}
  SET_HEATER_TEMPERATURE HEATER="{HEATER}" TARGET={TARGET}

[gcode_macro temperature_wait_scaled]
gcode:
  {% set SENSOR = params.SENSOR.lower() %}
  {% set heater = SENSOR if not SENSOR.startswith("heater_generic") else
                  SENSOR.split()[1] %}
  {% set scaled_heaters =
           printer["gcode_macro set_heater_scaling"].scaled_heaters %}
  {% if heater in scaled_heaters %}
    {% set scales = scaled_heaters[heater] %}
    {% if "MINIMUM" in params and MINIMUM > 0.0 %}
      {% set MINIMUM = (((MINIMUM + scales.bump) * scales.scale, scales.minimum)
                        | max, scales.maximum)|min %}
    {% endif %}
    {% if "MAXIMUM" in params and MINIMUM > 0.0 %}
      {% set MAXIMUM = (((MAXIMUM + scales.bump) * scales.scale, scales.minimum)
                        | max, scales.maximum)|min %}
    {% endif %}
  {% else %}
    {% set settings = printer.configfile.settings[SENSOR] %}
    {% set MINIMUM = params.MINIMUM|default(settings.min_temp) %}
    {% set MAXIMUM = params.MAXIMUM|default(settings.max_temp) %}
  {% endif %}
  {% if printer[sensor].temperature < MINIMUM or
        printer[sensor].temperature > MAXIMUM %}
    TEMPERATURE_WAIT SENSOR="{SENSOR}" MINIMUM={MINIMUM} MAXIMUM={MAXIMUM}
  {% endif %}

[gcode_macro _gcode_wait_wrapper_inner]
gcode:
  # Macro variables are evaluated at instantiation, so this inner macro is what
  # gets us a scaled target value.
  {% set sensor = params.HEATER if params.HEATER in
       printer.heaters.available_heaters 
     else ("heater_generic " ~ params.HEATER) %}
  {% set target = printer[sensor].target %}
  {% if 'R' in params and target > 0.0 %}
    {% set T = params.T|default("") %}
    M{params.M}.6245197 S{target} { (" T"~T) if T else "" }
  {% elif printer[sensor].temperature < target %}
    TEMPERATURE_WAIT SENSOR="{sensor}" MINIMUM={target}
  {% endif %}

[gcode_macro _gcode_wait_wrapper]
gcode:
  {% set target = params.S | default(params.R | default(0))| float %}
  SET_HEATER_TEMPERATURE_SCALED HEATER="{params.HEATER}" TARGET={target}
  _GCODE_WAIT_WRAPPER_INNER{%
    for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}

[gcode_macro m109]
rename_existing: M109.6245197
gcode:
  {% set extruder = "extruder" ~ params.T|replace('0', '')
     if "T" in params else printer.toolhead.extruder %}
  _GCODE_WAIT_WRAPPER HEATER={extruder}{%
    for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}

[gcode_macro m190]
rename_existing: M190.6245197
gcode:
  _GCODE_WAIT_WRAPPER HEATER=heater_bed {%
    for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}

[gcode_macro m191]
description: Sets chamber temperature (with wait for heating).
  Usage: M191 [S<temp>]
gcode:
  # Just fake the R parameter for the chamber.
  {% if "R" in params %}
    {% set dummy = params.__setitem__("S", params.R) %}
  {% endif %}
  _GCODE_WAIT_WRAPPER HEATER=chamber {%
    for k in params %}{' '~k~'="'~params[k]~'"'}{% endfor %}

[gcode_macro m104]
rename_existing: M104.6245197
gcode:
  {% set extruder = "extruder" ~ params.T|replace('0', '')
     if "T" in params else printer.toolhead.extruder %}
  SET_HEATER_TEMPERATURE_SCALED HEATER={extruder} TARGET={params.S|default(0)}

[gcode_macro m140]
rename_existing: M140.6245197
gcode:
  SET_HEATER_TEMPERATURE_SCALED HEATER=heater_bed TARGET={params.S|default(0)}

[gcode_macro m141]
description: Sets chamber temperature.
  Usage: M141 [S<temp>]
gcode:
  SET_HEATER_TEMPERATURE_SCALED HEATER=chamber TARGET={params.S|default(0)}

[gcode_macro _check_heater_params]
gcode:
  {% set HEATER = params.HEATER|lower %}
  {% set sensor = params.HEATER %}
  {% if HEATER not in printer.heaters.available_heaters %}
    {% set sensor = "heater_generic " ~ HEATER %}
    {% if sensor not in printer.heaters.available_heaters %}
      { action_raise_error("The value '%s' is not valid for HEATER"
                            | format(HEATER)) }
    {% endif %}
  {% endif %}
  {% set settings = printer.configfile.settings[sensor] %}
  {% set scaled_heaters =
       printer["gcode_macro set_heater_scaling"].scaled_heaters %}
  {% if HEATER is in scaled_heaters %}
    {% set scales = scaled_heaters[HEATER] %}
  {% else %}
    {% set scales = { "scale": 1.0, "bump": 0.0, "minimum": settings.min_temp,
                      "maximum": settings.max_temp,
                      "real_target": printer[sensor].target} %}
  {% endif %}
  {% set SCALE = params.SCALE|default(scales.scale)|float %}
  {% set BUMP = params.BUMP|default(scales.bump)|float %}
  {% set MINIMUM = params.MINIMUM|default(scales.minimum)|float %}
  {% set MAXIMUM = params.MAXIMUM|default(scales.maximum)|float %}

  {% if SCALE <= 0 %}
    { action_raise_error("SCALE must be a positive value.") }
  {% elif MINIMUM < settings.min_temp or MINIMUM > settings.max_temp %}
    { action_raise_error("MINIMUM must be between %.1f and %.1f" |
      format(settings.min_temp, settings.max_temp)) }
  {% elif MAXIMUM < settings.min_temp or MAXIMUM > settings.max_temp %}
    { action_raise_error("MAXIMUM must be between %.1f and %.1f" |
      format(settings.min_temp, settings.max_temp)) }
  {% elif MINIMUM >= MAXIMUM %}
    { action_raise_error("MINIMUM must be less than MAXIMUM.") }
  {% elif "TARGET" in params and (params.TARGET|float < MINIMUM or
                                  params.TARGET|float > MAXIMUM) %}
    { action_raise_error("TARGET must be between %.1f and %.1f" |
      format(settings.min_temp, settings.max_temp)) }
  {% endif %}

[gcode_macro set_heater_scaling]
description: Sets scaling parameters for heater. If a new target temperature is passed it will be adjusted to the scaling parameters.
  Usage: SET_HEATER_SCALING [HEATER=<heater>] [SCALE=<scale_factor>]
                            [BUMP=<degrees>] [MAXIMUM=<degrees>]
                            [MINIMUM=<degrees>] [TARGET=<temp>]
variable_scaled_heaters: {}
gcode:
  # If no heater is supplied just list out all the scaled heaters.
  {% if "HEATER" is not in params %}
    {% set output = [] %}
    {% for h in scaled_heaters %}
      {% set dummy = output.append(
         "%s: Scale: %.2f Bump: %.1f Minimum:%.1f Maximum: %.1f"
         | format(h, scaled_heaters[h].scale, scaled_heaters[h].bump,
                  scaled_heaters[h].minimum, scaled_heaters[h].maximum)) %}
    {% endfor %}
    {% if output %}}{action_respond_info(output|join('\n'))}{% endif %}
  {% else %}
    # See if we need a new scaling entry for this heater.
    {% set HEATER = params.HEATER.lower() %}
    {% set settings = printer.configfile.settings[HEATER] %}
    {% if HEATER is not in scaled_heaters %}
      {% set dummy = scaled_heaters.__setitem__(HEATER, {"scale": 1.0,
         "bump": 0.0, "minimum": settings.min_temp,
         "maximum": settings.max_temp,
         "real_target": printer[HEATER].target}) %}
    {% endif %}
    _CHECK_HEATER_PARAMS{% for k in params %}{' '~k~'='~params[k]}{% endfor %}
    {% set scales = scaled_heaters[HEATER] %}
    {% set SCALE = params.SCALE|default(scales.scale)|float %}
    {% set BUMP = params.BUMP|default(scales.bump)|float %}
    {% set MAXIMUM = params.MAXIMUM|default(scales.maximum)|float %}
    {% set MINIMUM = params.MINIMUM|default(scales.minimum)|float %}
    {% set TARGET = params.TARGET|default(scales.real_target)|float %}
    {% set dummy = scales.__setitem__("scale", SCALE) %}
    {% set dummy = scales.__setitem__("bump", BUMP) %}
    {% set dummy = scales.__setitem__("maximum", MAXIMUM) %}
    {% set dummy = scales.__setitem__("minimum", MINIMUM) %}
    {% set dummy = scales.__setitem__("real_target", TARGET) %}

    {% set settings = printer.configfile.settings[HEATER] %}
    {% if SCALE != 1.0 or BUMP != 0.0 or MINIMUM != settings.min_temp or
          MAXIMUM != settings.max_temp %}
      {% set dummy = scaled_heaters.__setitem__(HEATER, scales) %}
    {% else %} # Remove a redundant scaling entry.
      {% set dummy = scaled_heaters.__delitem__(HEATER) %}
    {% endif %}
    SET_HEATER_TEMPERATURE_SCALED HEATER={HEATER} TARGET={TARGET}
    {action_respond_info(
       "%s: Scale: %.2f Bump: %.1f Minimum:%.1f Maximum: %.1f Target: %.1f"
       | format(HEATER, SCALE, BUMP, MINIMUM, MAXIMUM, TARGET))}
  {% endif %}
  # Dummy argument block for Mainsail
  {% set dummy = None if True else "
  {% set dummy = params.HEATER|default(e.g. extruder) %}
  {% set dummy = params.SCALE|default(1.0)|float %}
  {% set dummy = params.BUMP|default(0.0)|float %}
  {% set dummy = params.MAXIMUM|default(max_temp)|float %}
  {% set dummy = params.MINIMUM|default(min_temp)|float %}
  {% set dummy = params.TARGET|default(current target)|float %}
  " %} # End argument block for Mainsail

[gcode_macro reset_heater_scaling]
description: Clears scaling for heater (or all heaters if none specified).
  Usage: RESET_HEATER_SCALING [HEATER=<heater>]
gcode:
  {% if "HEATER" in params %}
    {% set HEATER = params.HEATER.lower() %}
    {% if HEATER not in printer.heaters.available_heaters %}
      { action_raise_error("The value '%s' is not valid for HEATER"
                           | format(HEATER)) }
    {% endif %}
    {% set settings = printer.configfile.settings[HEATER] %}
    SET_HEATER_SCALING HEATER={HEATER
                     } SCALE=1.0 BUMP=0.0 MINIMUM={settings.min_temp
                     } MAXIMUM={settings.max_temp}
  {% else %}
    # Clear everything if no heater was provided.
    SET_GCODE_VARIABLE MACRO=set_heater_scaling VARIABLE=scaled_heaters VALUE={
      "{}"}
  {% endif %}
