# This PostProcessing Plugin script is released 
# under the terms of the AGPLv3 or higher
from typing import Optional, Tuple

from UM.Logger import Logger
from ..Script import Script

class FilamentChange(Script):

    _layer_keyword = ";LAYER:"

    def __init__(self):
        super().__init__()

    def getSettingDataString(self):
        return """{
            "name":"Filament Change",
            "key": "FilamentChange",
            "metadata": {},
            "version": 2,
            "settings":
            {
                "layer_number":
                {
                    "label": "Layer",
                    "description": "At what layer should color change occur. This will be before the layer starts printing. Specify multiple color changes with a comma.",
                    "unit": "",
                    "type": "str",
                    "default_value": "1"
                },

                "initial_retract":
                {
                    "label": "Initial Retraction",
                    "description": "Initial filament retraction distance. The filament will be retracted with this amount before moving the nozzle away from the ongoing print.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 30.0
                },
                "later_retract":
                {
                    "label": "Later Retraction Distance",
                    "description": "Later filament retraction distance for removal. The filament will be retracted all the way out of the printer so that you can change the filament.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 300.0
                }
            }
        }"""

    def execute(self, data: list):

        """data is a list. Each index contains a layer"""
        layer_nums = self.getSettingValueByKey("layer_number")
        initial_retract = self.getSettingValueByKey("initial_retract")
        later_retract = self.getSettingValueByKey("later_retract")
        
        color_change = "M600"
        
        if initial_retract is not None and initial_retract > 0.:
            color_change = color_change + (" E-%.2f" % initial_retract)
        
        if later_retract is not None and later_retract > 0.:
            color_change = color_change + (" L-%.2f" % later_retract)
        
        color_change = color_change + " ; Generated by FilamentChange plugin"
        
        layer_targets = layer_nums.split(",")
        if len(layer_targets) > 0:
            for layer_num in layer_targets:
                layer_num = int(layer_num.strip())
                if layer_num <= len(data):
                    index, layer_data = self._searchLayerData(data, layer_num - 1)
                    if layer_data is None:
                        Logger.log("e", "Could not found the layer")
                        continue
                    lines = layer_data.split("\n")
                    lines.insert(2, color_change)
                    final_line = "\n".join(lines)
                    data[index] = final_line

        return data

    ##  This method returns the data corresponding with the indicated layer number, looking in the gcode for
    #   the occurrence of this layer number.
    def _searchLayerData(self, data: list, layer_num: int) -> Tuple[int, Optional[str]]:
        for index, layer_data in enumerate(data):
            first_line = layer_data.split("\n")[0]
            # The first line should contain the layer number at the beginning.
            if first_line[:len(self._layer_keyword)] == self._layer_keyword:
                # If found the layer that we are looking for, then return the data
                if first_line[len(self._layer_keyword):] == str(layer_num):
                    return index, layer_data
        return 0, None