from ..Script import Script
# from cura.Settings.ExtruderManager import ExtruderManager

class PauseAtHeight(Script):
    def __init__(self):
        super().__init__()

    def getSettingDataString(self):
        return """{
            "name": "Pause at height",
            "key": "PauseAtHeight",
            "metadata": {},
            "version": 2,
            "settings":
            {
                "pause_at":
                {
                    "label": "Pause at",
                    "description": "Whether to pause at a certain height or at a certain layer.",
                    "type": "enum",
                    "options": {"height": "Height", "layer_no": "Layer No."},
                    "default_value": "height"
                },
                "pause_height":
                {
                    "label": "Pause Height",
                    "description": "At what height should the pause occur",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 5.0,
                    "minimum_value": "0",
                    "minimum_value_warning": "0.27",
                    "enabled": "pause_at == 'height'"
                },
                "pause_layer":
                {
                    "label": "Pause Layer",
                    "description": "At what layer should the pause occur",
                    "type": "int",
                    "value": "math.floor((pause_height - 0.27) / 0.1) + 1",
                    "minimum_value": "0",
                    "minimum_value_warning": "1",
                    "enabled": "pause_at == 'layer_no'"
                },
                "head_park_x":
                {
                    "label": "Park Print Head X",
                    "description": "What X location does the head move to when pausing.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 190
                },
                "head_park_y":
                {
                    "label": "Park Print Head Y",
                    "description": "What Y location does the head move to when pausing.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 190
                },
                "retraction_amount":
                {
                    "label": "Retraction",
                    "description": "How much filament must be retracted at pause.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 0
                },
                "retraction_speed":
                {
                    "label": "Retraction Speed",
                    "description": "How fast to retract the filament.",
                    "unit": "mm/s",
                    "type": "float",
                    "default_value": 25
                },
                "extrude_amount":
                {
                    "label": "Extrude Amount",
                    "description": "How much filament should be extruded after pause. This is needed when doing a material change on Ultimaker2's to compensate for the retraction after the change. In that case 128+ is recommended.",
                    "unit": "mm",
                    "type": "float",
                    "default_value": 0
                },
                "extrude_speed":
                {
                    "label": "Extrude Speed",
                    "description": "How fast to extrude the material after pause.",
                    "unit": "mm/s",
                    "type": "float",
                    "default_value": 3.3333
                },
                "redo_layers":
                {
                    "label": "Redo Layers",
                    "description": "Redo a number of previous layers after a pause to increases adhesion.",
                    "unit": "layers",
                    "type": "int",
                    "default_value": 0
                },
                "standby_temperature":
                {
                    "label": "Standby Temperature",
                    "description": "Change the temperature during the pause",
                    "unit": "°C",
                    "type": "int",
                    "default_value": 0
                },
                "resume_temperature":
                {
                    "label": "Resume Temperature",
                    "description": "Change the temperature after the pause",
                    "unit": "°C",
                    "type": "int",
                    "default_value": 0
                }
            }
        }"""

    def execute(self, data: list):

        """data is a list. Each index contains a layer"""

        x = 0.
        y = 0.
        pause_at = self.getSettingValueByKey("pause_at")
        pause_height = self.getSettingValueByKey("pause_height")
        pause_layer = self.getSettingValueByKey("pause_layer")
        retraction_amount = self.getSettingValueByKey("retraction_amount")
        retraction_speed = self.getSettingValueByKey("retraction_speed")
        extrude_amount = self.getSettingValueByKey("extrude_amount")
        extrude_speed = self.getSettingValueByKey("extrude_speed")
        park_x = self.getSettingValueByKey("head_park_x")
        park_y = self.getSettingValueByKey("head_park_y")
        layers_started = False
        redo_layers = self.getSettingValueByKey("redo_layers")
        standby_temperature = self.getSettingValueByKey("standby_temperature")
        resume_temperature = self.getSettingValueByKey("resume_temperature")

        # T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
        # with open("out.txt", "w") as f:
            # f.write(T)

        # use offset to calculate the current height: <current_height> = <current_z> - <layer_0_z>
        layer_0_z = 0.
        current_z = 0
        got_first_g_cmd_on_layer_0 = False
        for index, layer in enumerate(data):
            lines = layer.split("\n")
            for line in lines:
                if ";LAYER:0" in line:
                    layers_started = True
                if not layers_started:
                    continue

                if self.getValue(line, "Z") is not None:
                    current_z = self.getValue(line, "Z")

                if pause_at == "height":
                    if self.getValue(line, "G") != 1 and self.getValue(line, "G") != 0:
                        continue

                    if not got_first_g_cmd_on_layer_0:
                        layer_0_z = current_z
                        got_first_g_cmd_on_layer_0 = True

                    x = self.getValue(line, "X", x)
                    y = self.getValue(line, "Y", y)

                    current_height = current_z - layer_0_z
                    if current_height < pause_height:
                        break #Try the next layer.
                else: #Pause at layer.
                    if not line.startswith(";LAYER:"):
                        continue
                    current_layer = line[len(";LAYER:"):]
                    try:
                        current_layer = int(current_layer)
                    except ValueError: #Couldn't cast to int. Something is wrong with this g-code data.
                        continue
                    if current_layer < pause_layer:
                        break #Try the next layer.

                prevLayer = data[index - 1]
                prevLines = prevLayer.split("\n")
                current_e = 0.

                # Access last layer, browse it backwards to find
                # last extruder absolute position
                for prevLine in reversed(prevLines):
                    current_e = self.getValue(prevLine, "E", -1)
                    if current_e >= 0:
                        break

                # include a number of previous layers
                for i in range(1, redo_layers + 1):
                    prevLayer = data[index - i]
                    layer = prevLayer + layer

                    # Get extruder's absolute position at the
                    # begining of the first layer redone
                    # see https://github.com/nallath/PostProcessingPlugin/issues/55
                    if i == redo_layers:
                        prevLines = prevLayer.split("\n")
                        for line in prevLines:
                            new_e = self.getValue(line, 'E', current_e)

                            if new_e != current_e:
                                current_e = new_e
                                break

                prepend_gcode = ";TYPE:CUSTOM\n"
                prepend_gcode += ";added code by post processing\n"
                prepend_gcode += ";script: PauseAtHeight.py\n"
                if pause_at == "height":
                    prepend_gcode += ";current z: {z}\n".format(z = current_z)
                    prepend_gcode += ";current height: {height}\n".format(height = current_height)
                else:
                    prepend_gcode += ";current layer: {layer}\n".format(layer = current_layer)

                # Retraction
                prepend_gcode += self.putValue(M = 83) + "\n"
                if retraction_amount != 0:
                    prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"

                # Move the head away
                prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
                prepend_gcode += self.putValue(G = 1, X = park_x, Y = park_y, F = 9000) + "\n"
                if current_z < 15:
                    prepend_gcode += self.putValue(G = 1, Z = 15, F = 300) + "\n"

                # Disable the E steppers
                prepend_gcode += self.putValue(M = 84, E = 0) + "\n"

                # Set extruder standby temperature
                prepend_gcode += self.putValue(M = 104, S = standby_temperature) + "; standby temperature\n"

                # Wait till the user continues printing
                prepend_gcode += self.putValue(M = 0) + ";Do the actual pause\n"

                # Set extruder resume temperature
                prepend_gcode += self.putValue(M = 109, S = resume_temperature) + "; resume temperature\n"

                # Push the filament back,
                if retraction_amount != 0:
                    prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"

                # Optionally extrude material
                if extrude_amount != 0:
                    prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = extrude_speed * 60) + "\n"

                # and retract again, the properly primes the nozzle
                # when changing filament.
                if retraction_amount != 0:
                    prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"

                # Move the head back
                prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
                prepend_gcode += self.putValue(G = 1, X = x, Y = y, F = 9000) + "\n"
                if retraction_amount != 0:
                    prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
                prepend_gcode += self.putValue(G = 1, F = 9000) + "\n"
                prepend_gcode += self.putValue(M = 82) + "\n"

                # reset extrude value to pre pause value
                prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"

                layer = prepend_gcode + layer


                # Override the data of this layer with the
                # modified data
                data[index] = layer
                return data
        return data
