# Copyright (c) 2019 BigRep
# The Batch Printing plugin is released under the terms of the LGPLv3.

from typing import Any, Tuple
import numpy

from UM.Settings.ContainerRegistry import ContainerRegistry

from cura.Settings.ExtruderManager import ExtruderManager
from cura.CuraApplication import CuraApplication


class BatchDescr():

    @staticmethod
    def _securityDists(global_stack):
        polygon = numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"))

        minPt = polygon[0]
        maxPt = polygon[0]
        for point in polygon[1:]:
            minPt = numpy.minimum(minPt, point)
            maxPt = numpy.maximum(maxPt, point)

        maxPt -= minPt

        return (maxPt[0], maxPt[1])

    ##   Private convenience function to get a setting from the correct extruder (as defined by limit_to_extruder property).
    @staticmethod
    def _getSettingProperty(global_stack, node, setting_key: str, prop: str = "value") -> Any:
        if global_stack is None or node is None:
            return None
        per_mesh_stack = node.callDecoration("getStack")
        if per_mesh_stack:
            return per_mesh_stack.getProperty(setting_key, prop)

        extruder_index = global_stack.getProperty(setting_key, "limit_to_extruder")
        if extruder_index == "-1":
            # No limit_to_extruder
            extruder_stack_id = node.callDecoration("getActiveExtruder")
            if not extruder_stack_id:
                # Decoration doesn't exist
                extruder_stack_id = ExtruderManager.getInstance().extruderIds["0"]
            extruder_stack = ContainerRegistry.getInstance().findContainerStacks(id=extruder_stack_id)[0]
            return extruder_stack.getProperty(setting_key, prop)
        else:
            # Limit_to_extruder is set. The global stack handles this then
            return global_stack.getProperty(setting_key, prop)


    @staticmethod
    def _adhesionTypeAndMargin(global_stack, node) -> Tuple[str, float]:
        extra_margin = 0

        # Compensate for raft/skirt/brim
        # Add extra margin depending on adhesion type
        adhesion_type = global_stack.getProperty("adhesion_type", "value")

        if adhesion_type == "raft":
            extra_margin = max(0, BatchDescr._getSettingProperty(global_stack, node, "raft_margin"))
        elif adhesion_type == "brim":
            extra_margin = max(0, BatchDescr._getSettingProperty(global_stack, node, "brim_line_count") * BatchDescr._getSettingProperty(global_stack, node, "skirt_brim_line_width"))
        elif adhesion_type == "none":
            extra_margin = 0
        elif adhesion_type == "skirt":
            extra_margin = max(
                0, BatchDescr._getSettingProperty(global_stack, node, "skirt_gap") +
                BatchDescr._getSettingProperty(global_stack, node, "skirt_line_count") * BatchDescr._getSettingProperty(global_stack, node, "skirt_brim_line_width"))

        return (adhesion_type, extra_margin)


    @staticmethod
    def getMachineDescr(node):
        machineID = CuraApplication.getInstance().getMachineManager().activeMachine.definition.getId()
        isStudio = machineID in ("bigrep_studio", "bigrep_studio_g2", "bigrep_viio")

        global_stack = CuraApplication.getInstance().getGlobalContainerStack()

        secDists = BatchDescr._securityDists(global_stack)
        securityDistance0 = secDists[0] if isStudio else secDists[1]
        securityDistance1 = secDists[1] if isStudio else secDists[0]

        adhesionType, adhesionMargin = BatchDescr._adhesionTypeAndMargin(global_stack, node)

        machineDescr = {
            "machineID": machineID,
            "isStudio": isStudio,
            "machine_size": [global_stack.getProperty("machine_width", "value"), global_stack.getProperty("machine_depth", "value")],
            "machine_preferedStartPoint": [global_stack.getProperty("br_machine_width_prefered_start_point", "value"), global_stack.getProperty("br_machine_depth_prefered_start_point", "value")],
            "adhesionType": adhesionType,
            "adhesionMargin": adhesionMargin,
            "tandemMode": global_stack.getProperty("br_tandem_enable", "value"),
            "batch_printing_90deg_rotation": global_stack.getProperty("br_batch_printing_90deg_rotation", "value"),
            "securityDistance0": securityDistance0,
            "securityDistance1": securityDistance1
        }

        return machineDescr
