# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2023 @Andrej730
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bonsai.  If not, see <http://www.gnu.org/licenses/>.


import os
import bpy
import ifcopenshell
import bonsai.tool as tool
from bonsai.bim.helper import prop_with_search
from bonsai.bim.module.model.data import AuthoringData
from bpy.types import WorkSpaceTool
from functools import partial


class CoveringTool(WorkSpaceTool):
    bl_space_type = "VIEW_3D"
    bl_context_mode = "OBJECT"
    bl_idname = "bim.covering_tool"
    bl_label = "Covering Tool"
    bl_description = "Create and edit coverings, including ceiling, flooring, cladding, roofing, moulding, skirtingboard, insulation, membrane, sleeving, and wrapping coverings"
    bl_icon = os.path.join(os.path.dirname(__file__), "ops.authoring.covering")
    bl_widget = None
    ifc_element_type = "IfcCoveringType"
    bl_keymap = tool.Blender.get_default_selection_keypmap() + (
        ("bim.covering_hotkey", {"type": "A", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_A")]}),
        ("bim.covering_hotkey", {"type": "G", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_G")]}),
    )

    @classmethod
    def draw_settings(cls, context, layout, ws_tool):
        CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)


add_layout_hotkey = partial(tool.Blender.add_layout_hotkey_operator, tool_name="covering", module_name=__name__)


class CoveringToolUI:
    @classmethod
    def draw(cls, context, layout, ifc_element_type=None):
        cls.layout = layout
        cls.props = tool.Model.get_model_props()
        cls.covering_props = tool.Covering.get_covering_props()

        row = cls.layout.row(align=True)
        if not tool.Ifc.get():
            row.label(text="No IFC Project", icon="ERROR")
            return

        if not AuthoringData.is_loaded:
            AuthoringData.load(ifc_element_type)
        elif AuthoringData.data["ifc_element_type"] != ifc_element_type:
            AuthoringData.load(ifc_element_type)

        if context.region.type == "TOOL_HEADER":
            cls.draw_header_interface()
        elif context.region.type in ("UI", "WINDOW"):
            cls.draw_basic_bim_tool_interface()

        cls.draw_default_interface()

    @classmethod
    def draw_header_interface(cls):
        cls.draw_type_selection_interface()

    @classmethod
    def draw_default_interface(cls):
        row = cls.layout.row(align=True)
        row.prop(data=cls.props, property="rl3", text="RL")
        row = cls.layout.row(align=True)
        row.prop(data=cls.covering_props, property="ceiling_height", text="Ceiling Height")
        if AuthoringData.data["ifc_classes"]:
            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_A")
            row.operator("bim.add_occurrence", text="Add")

            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_A")
            row.operator("bim.add_instance_flooring_covering_from_cursor")

            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_A")
            row.operator("bim.add_instance_ceiling_covering_from_cursor")

            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_A")
            row.operator("bim.add_instance_flooring_coverings_from_walls")

            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_A")
            row.operator("bim.add_instance_ceiling_coverings_from_walls")

            row = cls.layout.row(align=True)
            row.label(text="", icon="EVENT_SHIFT")
            row.label(text="", icon="EVENT_G")
            row.operator("bim.regen_selected_covering_object")

    @classmethod
    def draw_type_selection_interface(cls):
        # shared by both sidebar and header
        row = cls.layout.row(align=True)
        if AuthoringData.data["ifc_classes"]:
            row = cls.layout.row(align=True)
            row.label(text="", icon="FILE_3D")
            prop_with_search(row, cls.props, "relating_type_id", text="")
            row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="")
        else:
            row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR")
            row = cls.layout.row()
            row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="Launch Type Manager")

    @classmethod
    def draw_basic_bim_tool_interface(cls):
        cls.draw_type_selection_interface()

        if AuthoringData.data["ifc_classes"]:
            if cls.props.ifc_class:
                box = cls.layout.box()
                if thumbnail := AuthoringData.data["relating_type_data"].get("thumbnail"):
                    box.template_icon(icon_value=thumbnail, scale=5)
                else:
                    op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
                    op.ifc_class = cls.props.ifc_class


class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
    bl_idname = "bim.covering_hotkey"
    bl_label = ""
    bl_options = {"REGISTER", "UNDO", "INTERNAL"}
    hotkey: bpy.props.StringProperty()
    description: bpy.props.StringProperty()

    @classmethod
    def poll(cls, context):
        return tool.Ifc.get()

    @classmethod
    def description(cls, context, operator):
        return operator.description or ""

    def _execute(self, context):
        self.props = tool.Covering.get_covering_props()
        getattr(self, f"hotkey_{self.hotkey}")()

    def invoke(self, context, event):
        # https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
        return self.execute(context)

    def draw(self, context):
        pass

    def hotkey_S_A(self):
        active_obj = bpy.context.active_object
        element = tool.Ifc.get_entity(active_obj)
        container = tool.Root.get_default_container()

        if AuthoringData.data["relating_type_data"].get("predefined_type") == "FLOORING":
            if element and bpy.context.selected_objects and element.is_a("IfcWall"):
                bpy.ops.bim.add_instance_flooring_coverings_from_walls()
            elif container:
                bpy.ops.bim.add_instance_flooring_covering_from_cursor()
            else:
                bpy.ops.bim.add_occurrence()
        elif AuthoringData.data["relating_type_data"].get("predefined_type") == "CEILING":
            if element and bpy.context.selected_objects and element.is_a("IfcWall"):
                bpy.ops.bim.add_instance_ceiling_coverings_from_walls()
            elif container:
                bpy.ops.bim.add_instance_ceiling_covering_from_cursor()
            else:
                bpy.ops.bim.add_occurrence()
        else:
            bpy.ops.bim.add_occurrence()

    def hotkey_S_G(self):
        active_obj = bpy.context.active_object
        element = tool.Ifc.get_entity(active_obj)
        if (
            element
            and bpy.context.selected_objects
            and element.is_a("IfcCovering")
            and AuthoringData.data["active_material_usage"] == "LAYER3"
        ):
            bpy.ops.bim.regen_selected_covering_object()
