# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program 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 2
#  of the License, or (at your option) any later version.
#
#  This program 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 this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# Copyright 2011, Ryan Inch

import bpy

from bpy.types import (
    Operator,
)

from bpy.props import (
    BoolProperty,
    StringProperty,
    IntProperty
)

from . import internals

from .internals import (
    layer_collections,
    qcd_slots,
    update_property_group,
    get_modifiers,
    get_move_selection,
    get_move_active
)

from .operators import rto_history


class MoveToQCDSlot(Operator):
    '''Move object(s) to QCD slot'''
    bl_label = "Move To QCD Slot"
    bl_idname = "view3d.move_to_qcd_slot"
    bl_options = {'REGISTER', 'UNDO'}

    slot: StringProperty()
    toggle: BoolProperty()

    def execute(self, context):
        global qcd_slots
        global layer_collections

        selected_objects = get_move_selection()
        active_object = get_move_active()
        internals.move_triggered = True

        qcd_laycol = None
        slot_name = qcd_slots.get_name(self.slot)

        if slot_name:
            qcd_laycol = layer_collections[slot_name]["ptr"]

        else:
            return {'CANCELLED'}


        if not selected_objects:
            return {'CANCELLED'}

        # adds object to slot
        if self.toggle:
            if not active_object:
                active_object = selected_objects[0]

            if not active_object.name in qcd_laycol.collection.objects:
                for obj in selected_objects:
                    if obj.name not in qcd_laycol.collection.objects:
                        qcd_laycol.collection.objects.link(obj)

            else:
                for obj in selected_objects:
                    if obj.name in qcd_laycol.collection.objects:

                        if len(obj.users_collection) == 1:
                            continue

                        qcd_laycol.collection.objects.unlink(obj)


        # moves object to slot
        else:
            for obj in selected_objects:
                if obj.name not in qcd_laycol.collection.objects:
                    qcd_laycol.collection.objects.link(obj)

                for collection in obj.users_collection:
                    qcd_idx = qcd_slots.get_idx(collection.name)
                    if qcd_idx != self.slot:
                        collection.objects.unlink(obj)


        # update the active object if needed
        if not context.active_object:
            try:
                context.view_layer.objects.active = active_object

            except RuntimeError: # object not in visible slot
                pass

        # update header UI
        cm = bpy.context.scene.collection_manager
        cm.update_header.clear()
        new_update_header = cm.update_header.add()
        new_update_header.name = "updated"

        return {'FINISHED'}


class ViewMoveQCDSlot(Operator):
    bl_label = ""
    bl_idname = "view3d.view_move_qcd_slot"
    bl_options = {'REGISTER', 'UNDO'}

    slot: StringProperty()

    @classmethod
    def description(cls, context, properties):
        global qcd_slots

        slot_name = qcd_slots.get_name(properties.slot)

        slot_string = f"QCD Slot {properties.slot}: \"{slot_name}\"\n"

        hotkey_string = (
            "  * Shift-Click to toggle QCD slot.\n"
            "  * Ctrl-Click to move objects to QCD slot.\n"
            "  * Ctrl-Shift-Click to toggle objects' slot"
            )

        return f"{slot_string}{hotkey_string}"

    def invoke(self, context, event):
        global layer_collections
        global qcd_history

        modifiers = get_modifiers(event)

        if modifiers == {"shift"}:
            bpy.ops.view3d.view_qcd_slot(slot=self.slot, toggle=True)

            return {'FINISHED'}

        elif modifiers == {"ctrl"}:
            bpy.ops.view3d.move_to_qcd_slot(slot=self.slot, toggle=False)
            return {'FINISHED'}

        elif modifiers == {"ctrl", "shift"}:
            bpy.ops.view3d.move_to_qcd_slot(slot=self.slot, toggle=True)
            return {'FINISHED'}

        else:
            bpy.ops.view3d.view_qcd_slot(slot=self.slot, toggle=False)
            return {'FINISHED'}

class ViewQCDSlot(Operator):
    '''View objects in QCD slot'''
    bl_label = "View QCD Slot"
    bl_idname = "view3d.view_qcd_slot"
    bl_options = {'REGISTER', 'UNDO'}

    slot: StringProperty()
    toggle: BoolProperty()

    def execute(self, context):
        global qcd_slots
        global layer_collections
        global rto_history

        qcd_laycol = None
        slot_name = qcd_slots.get_name(self.slot)

        if slot_name:
            qcd_laycol = layer_collections[slot_name]["ptr"]

        else:
            return {'CANCELLED'}

        if self.toggle:
            # get current child exclusion state
            child_exclusion = []

            laycol_iter_list = [qcd_laycol.children]
            while len(laycol_iter_list) > 0:
                new_laycol_iter_list = []
                for laycol_iter in laycol_iter_list:
                    for layer_collection in laycol_iter:
                        child_exclusion.append([layer_collection, layer_collection.exclude])
                        if len(layer_collection.children) > 0:
                            new_laycol_iter_list.append(layer_collection.children)

                laycol_iter_list = new_laycol_iter_list

            # toggle exclusion of qcd_laycol
            qcd_laycol.exclude = not qcd_laycol.exclude

            # set correct state for all children
            for laycol in child_exclusion:
                laycol[0].exclude = laycol[1]

            # set layer as active layer collection
            context.view_layer.active_layer_collection = qcd_laycol

        else:
            for laycol in layer_collections.values():
                if laycol["name"] != qcd_laycol.name:
                    laycol["ptr"].exclude = True

            qcd_laycol.exclude = False

            # exclude all children
            laycol_iter_list = [qcd_laycol.children]
            while len(laycol_iter_list) > 0:
                new_laycol_iter_list = []
                for laycol_iter in laycol_iter_list:
                    for layer_collection in laycol_iter:
                        layer_collection.exclude = True
                        if len(layer_collection.children) > 0:
                            new_laycol_iter_list.append(layer_collection.children)

                laycol_iter_list = new_laycol_iter_list

            # set layer as active layer collection
            context.view_layer.active_layer_collection = qcd_laycol

        # update header UI
        cm = bpy.context.scene.collection_manager
        cm.update_header.clear()
        new_update_header = cm.update_header.add()
        new_update_header.name = "updated"

        view_layer = context.view_layer.name
        if view_layer in rto_history["exclude"]:
            del rto_history["exclude"][view_layer]
        if view_layer in rto_history["exclude_all"]:
            del rto_history["exclude_all"][view_layer]

        return {'FINISHED'}


class RenumerateQCDSlots(Operator):
    '''Re-numerate QCD slots\n  * Ctrl-Click to include collections marked by the user as non QCD slots'''
    bl_label = "Re-numerate QCD Slots"
    bl_idname = "view3d.renumerate_qcd_slots"
    bl_options = {'REGISTER', 'UNDO'}

    def invoke(self, context, event):
        global qcd_slots

        qcd_slots.clear_slots()

        if event.ctrl:
            qcd_slots.overrides.clear()

        update_property_group(context, renumerate_qcd=True)

        return {'FINISHED'}