# Copyright (C) <2021>  <Reston Stanton>

# ##### 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 3 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
# MERCHANTIBILITY 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, see <http://www.gnu.org/licenses/>.
#
# ##### END GPL LICENSE BLOCK #####


import bpy
import bpy_extras
import os
import shutil
import time
from .. import preferences, Icons

from ..Shared import overlays, modifiers, material, forces, particles

auto_ops_running = False


def add_poll(context):
    if context.active_object:
        if context.active_object.type != "MESH":
            return False
        if modifiers.find_modifier(context.active_object):
            return False
        else:
            return True
    else:
        return False


def move_to_collection(obj, kind, context):
    """Move obj to the domain specified collection based on the kind

    kind (FLOW, EFFECTOR, FORCE)"""

    if preferences.get_preferences().move_to_collection:
        domain = context.scene.MantaPro.active_domain
        active_object = context.active_object
        if domain:
            modifier_settings = modifiers.get_modifier_domain(domain=domain)
            old_collections = obj.users_collection

            if kind == "FLOW" and not modifier_settings.fluid_group:
                return
            elif kind == "EFFECTOR" and not modifier_settings.effector_group:
                return
            elif kind == "FORCE" and not modifier_settings.effector_weights.collection:
                return

            for i in old_collections:
                i.objects.unlink(obj)

            if kind == "FLOW":
                modifier_settings.fluid_group.objects.link(obj)
            elif kind == "EFFECTOR":
                modifier_settings.effector_group.objects.link(obj)
            elif kind == "FORCE":
                modifier_settings.effector_weights.collection.objects.link(obj)
            # restore active object
            context.view_layer.objects.active = active_object


"""
  ███                 █████                            ██████                                  █████     ███
 ░░░                 ░░███                            ███░░███                                ░░███     ░░░
 ████  ████████    ███████   ██████  █████ █████     ░███ ░░░  █████ ████ ████████    ██████  ███████   ████   ██████  ████████
░░███ ░░███░░███  ███░░███  ███░░███░░███ ░░███     ███████   ░░███ ░███ ░░███░░███  ███░░███░░░███░   ░░███  ███░░███░░███░░███
 ░███  ░███ ░███ ░███ ░███ ░███████  ░░░█████░     ░░░███░     ░███ ░███  ░███ ░███ ░███ ░░░   ░███     ░███ ░███ ░███ ░███ ░███
 ░███  ░███ ░███ ░███ ░███ ░███░░░    ███░░░███      ░███      ░███ ░███  ░███ ░███ ░███  ███  ░███ ███ ░███ ░███ ░███ ░███ ░███
 █████ ████ █████░░████████░░██████  █████ █████     █████     ░░████████ ████ █████░░██████   ░░█████  █████░░██████  ████ █████
░░░░░ ░░░░ ░░░░░  ░░░░░░░░  ░░░░░░  ░░░░░ ░░░░░     ░░░░░       ░░░░░░░░ ░░░░ ░░░░░  ░░░░░░     ░░░░░  ░░░░░  ░░░░░░  ░░░░ ░░░░░

"""


def set_domain_index(context, obj):
    context.scene.MantaPro.active_domain = obj
    pre_keys = context.scene.MantaPro.domains.keys()
    item = context.scene.MantaPro.domains.add()
    item.obj = obj
    item.name = obj.name
    if len(pre_keys) == 0:
        context.scene.MantaPro.domains_index = 0
    else:
        item.index = context.scene.MantaPro.domains[pre_keys[-1]].index + 1
        context.scene.MantaPro.domains_index = item.index


def set_flow_index(context, obj):
    context.scene.MantaPro.active_flow = obj
    pre_keys = context.scene.MantaPro.flows.keys()
    item = context.scene.MantaPro.flows.add()
    item.obj = obj
    item.name = obj.name

    if len(pre_keys) == 0:
        context.scene.MantaPro.flows_index = 0
    else:
        item.index = context.scene.MantaPro.flows[pre_keys[-1]].index + 1
        context.scene.MantaPro.flows_index = item.index


def set_effector_index(context, obj):
    context.scene.MantaPro.active_effector = obj
    pre_keys = context.scene.MantaPro.effectors.keys()
    item = context.scene.MantaPro.effectors.add()
    item.obj = obj
    item.name = obj.name

    if len(pre_keys) == 0:
        context.scene.MantaPro.effectors_index = 0
    else:
        item.index = context.scene.MantaPro.effectors[pre_keys[-1]].index + 1
        context.scene.MantaPro.effectors_index = item.index


def set_force_index(context, obj):
    context.scene.MantaPro.active_force = obj
    pre_keys = context.scene.MantaPro.forces.keys()
    item = context.scene.MantaPro.forces.add()
    item.obj = obj
    item.name = obj.name

    if len(pre_keys) == 0:
        context.scene.MantaPro.forces_index = 0
    else:
        item.index = context.scene.MantaPro.forces[pre_keys[-1]].index + 1
        context.scene.MantaPro.forces_index = item.index


def set_effector(context, obj):
    set_effector_index(context, obj)


"""
   █████████       █████     █████        █████                                     ███
  ███░░░░░███     ░░███     ░░███        ░░███                                     ░░░
 ░███    ░███   ███████   ███████      ███████   ██████  █████████████    ██████   ████  ████████
 ░███████████  ███░░███  ███░░███     ███░░███  ███░░███░░███░░███░░███  ░░░░░███ ░░███ ░░███░░███
 ░███░░░░░███ ░███ ░███ ░███ ░███    ░███ ░███ ░███ ░███ ░███ ░███ ░███   ███████  ░███  ░███ ░███
 ░███    ░███ ░███ ░███ ░███ ░███    ░███ ░███ ░███ ░███ ░███ ░███ ░███  ███░░███  ░███  ░███ ░███
 █████   █████░░████████░░████████   ░░████████░░██████  █████░███ █████░░████████ █████ ████ █████
░░░░░   ░░░░░  ░░░░░░░░  ░░░░░░░░     ░░░░░░░░  ░░░░░░  ░░░░░ ░░░ ░░░░░  ░░░░░░░░ ░░░░░ ░░░░ ░░░░░
"""


class MantaProAddDomain(bpy.types.Operator):
    bl_idname = "mantapro.add_domain"
    bl_label = "add domain object"
    bl_description = "add domain object so scene"

    bl_options = {'REGISTER', 'UNDO'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):
        layout = self.layout

        col = layout.column(align=True)
        col.operator("mantapro_liquid.add_domain",
                     icon_value=Icons.get_icon_id("domain_liquid"), text="Liquid")

        col = layout.column(align=True)
        col.operator("mantapro_gas.add_domain",
                     icon_value=Icons.get_icon_id("domain_gas"), text="Gas")


class MantaProMakeDomain(bpy.types.Operator):
    bl_idname = "mantapro.make_domain"
    bl_label = "make domain object"
    bl_description = "make active object a domain"

    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return add_poll(context)

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):
        layout = self.layout

        col = layout.column(align=True)
        col.operator("mantapro_liquid.make_domain",
                     icon_value=Icons.get_icon_id("domain_liquid"), text="Liquid")

        col = layout.column(align=True)
        col.operator("mantapro_gas.make_domain",
                     icon_value=Icons.get_icon_id("domain_gas"), text="Gas")


"""
   █████████       █████     █████       ██████  ████
  ███░░░░░███     ░░███     ░░███       ███░░███░░███
 ░███    ░███   ███████   ███████      ░███ ░░░  ░███   ██████  █████ ███ █████
 ░███████████  ███░░███  ███░░███     ███████    ░███  ███░░███░░███ ░███░░███
 ░███░░░░░███ ░███ ░███ ░███ ░███    ░░░███░     ░███ ░███ ░███ ░███ ░███ ░███
 ░███    ░███ ░███ ░███ ░███ ░███      ░███      ░███ ░███ ░███ ░░███████████
 █████   █████░░████████░░████████     █████     █████░░██████   ░░████░████
░░░░░   ░░░░░  ░░░░░░░░  ░░░░░░░░     ░░░░░     ░░░░░  ░░░░░░     ░░░░ ░░░░
"""


class MantaProMakeFlow(bpy.types.Operator):
    bl_idname = "mantapro.make_flow"
    bl_label = "make domain object"
    bl_description = "make active object a domain"

    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return add_poll(context)

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):
        layout = self.layout

        col = layout.column(align=True)
        col.operator("mantapro_liquid.make_flow",
                     icon_value=Icons.get_icon_id("flow_liquid"), text="Liquid")

        col = layout.column(align=True)
        col.operator("mantapro_gas.make_flow",
                     icon_value=Icons.get_icon_id("flow_gas_both"), text="Gas")


"""
   █████████       █████     █████                ██████     ██████                     █████
  ███░░░░░███     ░░███     ░░███                ███░░███   ███░░███                   ░░███
 ░███    ░███   ███████   ███████      ██████   ░███ ░░░   ░███ ░░░   ██████   ██████  ███████    ██████  ████████
 ░███████████  ███░░███  ███░░███     ███░░███ ███████    ███████    ███░░███ ███░░███░░░███░    ███░░███░░███░░███
 ░███░░░░░███ ░███ ░███ ░███ ░███    ░███████ ░░░███░    ░░░███░    ░███████ ░███ ░░░   ░███    ░███ ░███ ░███ ░░░
 ░███    ░███ ░███ ░███ ░███ ░███    ░███░░░    ░███       ░███     ░███░░░  ░███  ███  ░███ ███░███ ░███ ░███
 █████   █████░░████████░░████████   ░░██████   █████      █████    ░░██████ ░░██████   ░░█████ ░░██████  █████
░░░░░   ░░░░░  ░░░░░░░░  ░░░░░░░░     ░░░░░░   ░░░░░      ░░░░░      ░░░░░░   ░░░░░░     ░░░░░   ░░░░░░  ░░░░░
"""


class MantaProEffector(bpy.types.Operator):
    bl_idname = "mantapro.make_effector"
    bl_label = "make selected effector"
    bl_description = "make selected object an effector"

    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return add_poll(context)

    def execute(self, context):
        # get current area
        area = context.area.ui_type
        context.area.ui_type = "VIEW_3D"

        obj = context.active_object
        bpy.ops.object.modifier_add(type='FLUID')
        context.object.modifiers["Fluid"].fluid_type = 'EFFECTOR'
        set_effector(context, obj)
        move_to_collection(obj, "EFFECTOR", context)

        context.area.ui_type = area

        if preferences.get_preferences().auto_ops:
            context.scene.MantaPro.auto_ops = True
        return {'FINISHED'}


"""
   █████████       █████     █████       ██████
  ███░░░░░███     ░░███     ░░███       ███░░███
 ░███    ░███   ███████   ███████      ░███ ░░░   ██████  ████████   ██████   ██████
 ░███████████  ███░░███  ███░░███     ███████    ███░░███░░███░░███ ███░░███ ███░░███
 ░███░░░░░███ ░███ ░███ ░███ ░███    ░░░███░    ░███ ░███ ░███ ░░░ ░███ ░░░ ░███████
 ░███    ░███ ░███ ░███ ░███ ░███      ░███     ░███ ░███ ░███     ░███  ███░███░░░
 █████   █████░░████████░░████████     █████    ░░██████  █████    ░░██████ ░░██████
░░░░░   ░░░░░  ░░░░░░░░  ░░░░░░░░     ░░░░░      ░░░░░░  ░░░░░      ░░░░░░   ░░░░░░
"""


class MantaProAddForce(bpy.types.Operator):
    bl_idname = "mantapro.add_force"
    bl_label = "Add Force Object"
    bl_description = "add force object to scene"

    bl_options = {'REGISTER', 'UNDO'}

    type: bpy.props.StringProperty()

    def execute(self, context):
        bpy.ops.object.effector_add(type=self.type)
        force = context.active_object
        set_force_index(context, force)
        move_to_collection(force, "FORCE", context)

        if preferences.get_preferences().auto_ops:
            context.scene.MantaPro.auto_ops = True
        return {'FINISHED'}


class MantaProMakeForce(bpy.types.Operator):
    bl_idname = "mantapro.make_force"
    bl_label = "make force object"
    bl_description = "make force object so scene"

    bl_options = {'REGISTER', 'UNDO'}

    type: bpy.props.StringProperty()

    @classmethod
    def poll(cls, context):
        if context.active_object:
            if context.active_object.field:
                return context.active_object.field.type == "NONE"

        return False

    def execute(self, context):
        force = context.active_object
        force.field.type = self.type
        set_force_index(context, force)
        move_to_collection(force, "FORCE", context)

        if preferences.get_preferences().auto_ops:
            context.scene.MantaPro.auto_ops = True
        return {'FINISHED'}


class MantaProMakeForceMenu(bpy.types.Operator):
    bl_idname = "mantapro.make_force_menu"
    bl_label = "Make Force"
    bl_description = "make force object so scene"

    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        if context.active_object:
            if context.active_object.field:
                return context.active_object.field.type == "NONE"

        return False

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):
        layout = self.layout
        layout.label(text="Make Object Force")

        forces.draw_add_menu(layout, "mantapro.make_force")


class MantaProAddForceMenu(bpy.types.Operator):
    bl_idname = "mantapro.add_force_menu"
    bl_label = "Add Force"
    bl_description = "add force object so scene"

    bl_options = {'REGISTER', 'UNDO'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):

        layout = self.layout
        layout.label(text="Add Force Object")

        forces.draw_add_menu(layout, "mantapro.add_force")


"""
   █████████                                                   ████
  ███░░░░░███                                                 ░░███
 ███     ░░░   ██████  ████████    ██████  ████████   ██████   ░███      ██████  ████████   █████
░███          ███░░███░░███░░███  ███░░███░░███░░███ ░░░░░███  ░███     ███░░███░░███░░███ ███░░
░███    █████░███████  ░███ ░███ ░███████  ░███ ░░░   ███████  ░███    ░███ ░███ ░███ ░███░░█████
░░███  ░░███ ░███░░░   ░███ ░███ ░███░░░   ░███      ███░░███  ░███    ░███ ░███ ░███ ░███ ░░░░███
 ░░█████████ ░░██████  ████ █████░░██████  █████    ░░████████ █████   ░░██████  ░███████  ██████
  ░░░░░░░░░   ░░░░░░  ░░░░ ░░░░░  ░░░░░░  ░░░░░      ░░░░░░░░ ░░░░░     ░░░░░░   ░███░░░  ░░░░░░
                                                                                 ░███
                                                                                 █████
                                                                                ░░░░░
"""


class MantaProUiLists(bpy.types.Operator):
    bl_idname = "mantapro.ui_lists"
    bl_label = "Show Lists"
    bl_description = " "

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=200)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):
        layout = self.layout
        settings = context.scene.MantaPro
        show_ops = preferences.get_preferences().show_list_ops
        col = layout.column()

        if show_ops:
            col.operator("mantapro.quick_setup_menu",
                         text="Quick setup", icon="PLUS")
            col.operator("mantapro.add_domain", icon="ADD")
            col.operator("mantapro.make_domain", icon="ADD",
                         text="Make Selected Domain")

            col.separator(factor=2)
        col.template_list("OBJECT_UL_domains", "", settings,
                          "domains", settings, "domains_index")

        col.separator(factor=2)

        if show_ops:
            col.operator("mantapro.make_flow", icon="ADD",
                         text="Make Selected Flow")
            col.operator("mantapro_gas.make_paint", icon="BRUSH_DATA",
                         text="Make Selected Flow and Paint")

            col.separator(factor=2)
        col.template_list("OBJECT_UL_flows", "", settings,
                          "flows", settings, "flows_index")

        col.separator(factor=2)

        if show_ops:
            col.operator("mantapro.make_effector", icon="ADD",
                         text="Make Selected Effector")
            col.separator(factor=2)
        col.template_list("OBJECT_UL_effector", "", settings,
                          "effectors", settings, "effectors_index")

        col.separator(factor=2)

        if show_ops:
            col.operator("mantapro.add_force_menu",
                         icon="PLUS", text="Add Force Field")

            col.operator("mantapro.make_force_menu", icon="ADD",
                         text="Make Selected Force Field")

        col.template_list("OBJECT_UL_force", "", settings,
                          "forces", settings, "forces_index")


class MantaProSwitchType(bpy.types.Operator):
    bl_idname = "mantapro.switch_type"
    bl_label = "Switch Type"
    bl_description = "switch the fluid type (Gas to Liquid or Liquid to Gas)"

    kind: bpy.props.StringProperty(name="type")

    def execute(self, context):
        kind = self.kind
        # domain switch
        if kind == "DOMAIN":
            domain = context.scene.MantaPro.active_domain
            modifier_settings = modifiers.get_modifier_domain(domain=domain)

            if modifier_settings.domain_type == "GAS":
                modifier_settings.domain_type = "LIQUID"
                material.add_material(domain, "MantaPro Liquid")
            else:
                modifier_settings.domain_type = "GAS"
                material.add_material(domain, "MantaPro Gas")

        # flow switch
        elif kind == "FLOW":
            flow = context.scene.MantaPro.active_flow
            modifier_settings = modifiers.find_modifier(flow).flow_settings

            if modifier_settings.flow_type != "LIQUID":
                modifier_settings.flow_type = "LIQUID"
            else:
                modifier_settings.flow_type = preferences.get_preferences().gas_flow_type
                flow.MantaPro.flow_type = preferences.get_preferences().gas_flow_type

        return {'FINISHED'}


class MantaProTransparentMaterial(bpy.types.Operator):
    bl_idname = "mantapro.transparent_material"
    bl_label = "Set Transparent Material"
    bl_description = "set object Material to transparent"

    obj: bpy.props.StringProperty(name="obj", default="NONE")

    def execute(self, context):
        if self.obj != "NONE":
            obj = bpy.data.objects[self.obj]
            material.add_material(obj, "TRANSPARENT")
        return {'FINISHED'}


class MantaProSwitchForce(bpy.types.Operator):
    bl_idname = "mantapro.switch_force"
    bl_label = "Switch force type"
    bl_description = "Switch force type"

    bl_options = {'REGISTER', 'UNDO'}

    type: bpy.props.StringProperty()

    def execute(self, context):
        force = context.scene.MantaPro.active_force
        force.field.type = self.type
        return {'FINISHED'}


class MantaProSwitchForceMenu(bpy.types.Operator):
    bl_idname = "mantapro.switch_force_menu"
    bl_label = "Switch Force"
    bl_description = "Switch force type for active flow"

    bl_options = {'REGISTER', 'UNDO'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_popup(self, width=100)
        return {'FINISHED'}

    def execute(self, context):
        return {'FINISHED'}

    def draw(self, context):

        layout = self.layout
        layout.label(text="Switch Force")

        forces.draw_add_menu(layout, "mantapro.switch_force")


"""
     █████                                     ███
    ░░███                                     ░░░
  ███████   ██████  █████████████    ██████   ████  ████████       ██████  ████████   █████
 ███░░███  ███░░███░░███░░███░░███  ░░░░░███ ░░███ ░░███░░███     ███░░███░░███░░███ ███░░
░███ ░███ ░███ ░███ ░███ ░███ ░███   ███████  ░███  ░███ ░███    ░███ ░███ ░███ ░███░░█████
░███ ░███ ░███ ░███ ░███ ░███ ░███  ███░░███  ░███  ░███ ░███    ░███ ░███ ░███ ░███ ░░░░███
░░████████░░██████  █████░███ █████░░████████ █████ ████ █████   ░░██████  ░███████  ██████
 ░░░░░░░░  ░░░░░░  ░░░░░ ░░░ ░░░░░  ░░░░░░░░ ░░░░░ ░░░░ ░░░░░     ░░░░░░   ░███░░░  ░░░░░░
                                                                           ░███
                                                                           █████
                                                                          ░░░░░
"""


class MantaProToggleBounds(bpy.types.Operator):
    bl_idname = "mantapro.toggle_bounds"
    bl_label = "toggle bounds"
    bl_description = "toggle bounding box display for domain"

    kind: bpy.props.StringProperty(name="type")

    @classmethod
    def poll(cls, context):
        return context.scene.MantaPro.active_domain

    def execute(self, context):
        obj = context.scene.MantaPro.active_domain
        overlays.toggle_doamin_bounds(context, obj)

        return {'FINISHED'}


class MantaProDisplayBounds(bpy.types.Operator):
    bl_idname = "mantapro.display_bounds"
    bl_label = "display bounds"
    bl_description = "display bounding box of domain \nIs not acuate to domain size, for acuate domain size use toggle bounds"

    kind: bpy.props.StringProperty(name="type")

    @classmethod
    def poll(cls, context):
        return context.scene.MantaPro.active_domain

    def execute(self, context):
        obj = context.scene.MantaPro.active_domain

        if obj.display_type == "SOLID":
            obj.display_type = "BOUNDS"
        else:
            obj.display_type = "SOLID"

        return {'FINISHED'}


class MantaProMoveCache(bpy.types.Operator, bpy_extras.io_utils.ImportHelper):
    bl_idname = "mantapro.move_cache"
    bl_label = "Move Cache"
    bl_description = "move fluid cache location"

    filter_glob: bpy.props.StringProperty(
        default='*.', options={'HIDDEN'})

    filename: bpy.props.StringProperty(default='')

    make_folder: bpy.props.BoolProperty(
        name=" Make Folder", description="Make a folder to store the cache in", default=True)

    remove_dir: bpy.props.BoolProperty(
        name="Remove Directory", description="Remove the directory the cache is stored in", default=False)

    @classmethod
    def poll(cls, context):
        return context.scene.MantaPro.active_domain

    def execute(self, context):
        obj = context.scene.MantaPro.active_domain
        modifier_settings = modifiers.get_modifier_domain(domain=obj)
        filepath = self.filepath
        cache_path = modifier_settings.cache_directory
        cache_path = bpy.path.abspath(cache_path)

        # remove an filename
        # split file path
        # split_path = filepath.split("\\")
        split_path = os.path.normpath(filepath).split(os.sep)

        # remove the filename
        if self.filename:
            split_path = split_path[:-1]

        # rejoin the path
        filepath = ""
        for i in split_path:
            filepath += i + os.sep

        if self.make_folder:
            filepath = os.path.join(filepath, "MantaProCache")
            iterations = 000
            while os.path.exists(filepath):
                filepath = filepath + str(iterations)
                iterations += 1
                if iterations > 100:
                    self.report(
                        {"ERROR"}, "unable to make destination (within reason)")
                    return {'CANCELLED'}

            os.makedirs(filepath)

        dirs = os.listdir(cache_path)
        for i in dirs:
            start = os.path.join(cache_path, i)
            dest = os.path.join(filepath, i)
            # dest = filepath
            shutil.copytree(start, dest)
        modifier_settings.cache_directory = filepath
        obj.MantaPro.moved_cache = True

        if self.remove_dir:
            shutil.rmtree(cache_path)
        return {'FINISHED'}


class MantaProMoveCacheIgnore(bpy.types.Operator):
    bl_idname = "mantapro.move_cache_ignore"
    bl_label = "Ignore"
    bl_description = "Ignore move cache request (NOT recommended)"

    @classmethod
    def poll(cls, context):
        return context.scene.MantaPro.active_domain

    def execute(self, context):
        obj = context.scene.MantaPro.active_domain
        obj.MantaPro.moved_cache = True
        return {'FINISHED'}


class MantaProReplayClear(bpy.types.Operator):
    bl_idname = "mantapro.replay_clear"
    bl_label = "Replay Clear"
    bl_description = "reset the cache for the replay cache type"

    @ classmethod
    def poll(cls, context):
        domain = context.scene.MantaPro.active_domain
        modifier = modifiers.get_modifier_domain(domain=domain)
        if domain and modifier.cache_type == "REPLAY":
            return True
        else:
            return False

    def execute(self, context):
        if context.scene.MantaPro.active_domain.MantaPro.locked_cache:
            return {'CANCELLED'}
        modifier_settings = modifiers.get_modifier_domain(context)

        modifier_settings.cache_type = 'REPLAY'

        context.scene.frame_current = modifier_settings.cache_frame_start
        return {'FINISHED'}


class MantaProFixMaterial(bpy.types.Operator):
    bl_idname = "mantapro.fix_material"
    bl_label = "Fix Material"
    bl_description = "fix material"

    kind: bpy.props.StringProperty()

    @ classmethod
    def poll(cls, context):
        domain = context.scene.MantaPro.active_domain
        if domain:
            return True
        else:
            return False

    def execute(self, context):
        domain = context.scene.MantaPro.active_domain
        if self.kind == "GAS":
            material.add_material(domain, "MantaPro Gas")
        elif self.kind == "LIQUID":
            material.add_material(domain, "MantaPro Liquid")
        elif self.kind == "EMBER":
            flow = context.scene.MantaPro.active_flow
            particle = flow.MantaPro.ember_particle_object
            material.add_material(particle, "MantaPro Ember")
        return {'FINISHED'}


class MantaProToggleVoxlesView(bpy.types.Operator):
    bl_idname = "mantapro.toggle_voxles_view"
    bl_label = "Toggle Voxels"
    bl_description = "Show the current voxel size \nuseful for debuging simulations"

    @ classmethod
    def poll(cls, context):
        return context.scene.MantaPro.active_domain

    def execute(self, context):
        domain = context.scene.MantaPro.active_domain
        modifier_settings = modifiers.get_modifier_domain(domain=domain)
        is_on = (modifier_settings.color_ramp_field == 'FLAGS' and modifier_settings.use_slice and modifier_settings.show_gridlines)
        if is_on:
            modifier_settings.use_slice = False
        else:
            modifier_settings.color_ramp_field = 'FLAGS'
            modifier_settings.use_slice = True
            modifier_settings.show_gridlines = True

        return {'FINISHED'}


"""
    ██████  ████
   ███░░███░░███
  ░███ ░░░  ░███   ██████  █████ ███ █████     ██████  ████████   █████
 ███████    ░███  ███░░███░░███ ░███░░███     ███░░███░░███░░███ ███░░
░░░███░     ░███ ░███ ░███ ░███ ░███ ░███    ░███ ░███ ░███ ░███░░█████
  ░███      ░███ ░███ ░███ ░░███████████     ░███ ░███ ░███ ░███ ░░░░███
  █████     █████░░██████   ░░████░████      ░░██████  ░███████  ██████
 ░░░░░     ░░░░░  ░░░░░░     ░░░░ ░░░░        ░░░░░░   ░███░░░  ░░░░░░
                                                       ░███
                                                       █████
                                                      ░░░░░
"""


class MantaProSetupEmber(bpy.types.Operator):
    bl_idname = "mantapro.setup_ember"
    bl_label = "Add Ember"
    bl_description = "Setup Ember Particle System"

    @ classmethod
    def poll(cls, context):
        flow = context.scene.MantaPro.active_flow
        if flow:
            return True
        else:
            return False

    def execute(self, context):
        flow = context.scene.MantaPro.active_flow

        # add particle system
        modifier = flow.modifiers.new(
            name="MantaPro Ember", type="PARTICLE_SYSTEM")

        system = modifier.particle_system
        settings = system.settings
        settings.name = "MantaPro Ember"
        settings.MantaPro.is_ember = True

        # setup particle system
        settings.frame_end = context.scene.frame_end + 1
        settings.effector_weights.gravity = 0.1
        settings.use_modifier_stack = True
        settings.normal_factor = 4
        settings.count = 450
        settings.lifetime = 60
        settings.lifetime_random = 0.8
        settings.brownian_factor = 1
        settings.particle_size = 0.1
        settings.size_random = 0.8
        settings.factor_random = 0.8

        settings.display_size = 0.05

        # setup sub forces system
        settings.use_self_effect = True
        settings.effector_amount = 50
        settings.force_field_1.type = "FORCE"
        settings.force_field_1.strength = 7
        settings.force_field_1.noise = 1
        settings.force_field_1.use_max_distance = True
        settings.force_field_1.distance_max = 0.05
        settings.force_field_1.use_gravity_falloff = True

        # add force
        bpy.ops.mantapro.add_force(type="FLUID")
        force = context.active_object
        force.name = "EMBER FORCE"
        force.MantaPro.force_ember = True
        force.field.strength = 10

        flow.MantaPro.ember_force_object = force

        # move force to a new collection
        force_collections = force.users_collection
        for i in force_collections:
            i.objects.unlink(force)
        collection = bpy.data.collections.new("EMBER_FORCE")
        flow.users_collection[0].children.link(collection)
        collection.objects.link(force)

        # setup domain
        force.field.source_object = context.scene.MantaPro.active_domain
        flow.MantaPro.ember_domain = context.scene.MantaPro.active_domain

        # add particle objects
        particle, collection = particles.add_ember_particles(context)
        flow.MantaPro.ember_particle_object = particle

        settings.render_type = 'OBJECT'
        settings.instance_object = particle

        # hide render
        if flow.hide_render:
            flow.show_instancer_for_render = False
            flow.hide_render = False

        return {'FINISHED'}


class MantaProRemoveEmber(bpy.types.Operator):
    bl_idname = "mantapro.remove_ember"
    bl_label = "Remove Ember"
    bl_description = "Remove Ember Particle System"

    @ classmethod
    def poll(cls, context):
        flow = context.scene.MantaPro.active_flow
        if flow:
            return True
        else:
            return False

    def execute(self, context):
        flow = context.scene.MantaPro.active_flow

        modifier = modifiers.find_ember_modifier(flow)
        particle = modifier.particle_system.settings.instance_object
        flow.modifiers.remove(modifier)

        force = flow.MantaPro.ember_force_object
        if force:
            force.MantaPro.force_ember = False
            bpy.data.objects.remove(force)

            if context.scene.MantaPro.active_force == force:
                context.scene.MantaPro.active_force = None

        if particle:
            if "MantaPro_Ember" in particle.name:
                if particle.users == 2:
                    collection = particle.users_collection[0]
                    bpy.data.objects.remove(particle)
                    if not collection.all_objects:
                        bpy.data.collections.remove(collection)

        # remove force collection
        for i in bpy.data.collections:
            if "EMBER_FORCE" in i.name:
                if not i.objects.items():
                    bpy.data.collections.remove(i)

        # hide render
        if not flow.show_instancer_for_render:
            flow.hide_render = True

        return {'FINISHED'}


class MantaProEmberDomainSync(bpy.types.Operator):
    bl_idname = "mantapro.ember_doamin_sync"
    bl_label = "sync"
    bl_description = "sync Ember domain"

    def execute(self, context):
        flow = context.scene.MantaPro.active_flow
        if flow:
            force = flow.MantaPro.ember_force_object
            if force:
                force.field.source_object = flow.MantaPro.ember_domain
        return {'FINISHED'}


class MantaProToggleEmberParticles(bpy.types.Operator):
    bl_idname = "mantapro.toggle_ember_particles"
    bl_label = "Reveal Particles"
    bl_description = "toggle particles objects visibility in the viewport"

    def execute(self, context):
        flow = context.scene.MantaPro.active_flow
        particle = flow.MantaPro.ember_particle_object

        if particle:
            collection = particle.users_collection[0]
            if collection:
                if collection.hide_viewport:
                    collection.hide_viewport = False
                else:
                    collection.hide_viewport = True
                return {'FINISHED'}
        return {'CANCELLED'}


class MantaProQuickToggleEmberParticles(bpy.types.Operator):
    bl_idname = "mantapro.quick_toggle_ember_particles"
    bl_label = "Quick Toggle"
    bl_description = "Switch display to Point or Object"

    @classmethod
    def poll(cls, context):
        if context.scene.MantaPro.active_flow:
            return True
        else:
            return False

    def execute(self, context):
        # get settings
        flow = context.scene.MantaPro.active_flow
        system = particles.find_ember_system(flow)
        settings = system.settings
        if system:
            first = settings.display_method
            if first == "DOT":
                switch = "RENDER"
            else:
                switch = "DOT"
            settings.display_method = switch
        return {'FINISHED'}


"""
   █████████                       █████                          █████
  ███░░░░░███                     ░░███                          ░░███
 ███     ░░░   ██████  ████████   ███████    ██████  █████ █████ ███████       ██████  ████████   █████
░███          ███░░███░░███░░███ ░░░███░    ███░░███░░███ ░░███ ░░░███░       ███░░███░░███░░███ ███░░
░███         ░███ ░███ ░███ ░███   ░███    ░███████  ░░░█████░    ░███       ░███ ░███ ░███ ░███░░█████
░░███     ███░███ ░███ ░███ ░███   ░███ ███░███░░░    ███░░░███   ░███ ███   ░███ ░███ ░███ ░███ ░░░░███
 ░░█████████ ░░██████  ████ █████  ░░█████ ░░██████  █████ █████  ░░█████    ░░██████  ░███████  ██████
  ░░░░░░░░░   ░░░░░░  ░░░░ ░░░░░    ░░░░░   ░░░░░░  ░░░░░ ░░░░░    ░░░░░      ░░░░░░   ░███░░░  ░░░░░░
                                                                                       ░███
                                                                                       █████
                                                                                      ░░░░░
"""


class MantaProBake(bpy.types.Operator):
    bl_idname = "mantapro.bake"
    bl_label = "bake cache"
    bl_description = "Bake cache"

    bl_options = {'REGISTER'}

    op: bpy.props.StringProperty(name="type")
    domain_type: bpy.props.StringProperty(name="domain type")

    def execute(self, context):
        overide = context.copy()
        overide["object"] = context.scene.MantaPro.active_domain
        overide["active_object"] = context.scene.MantaPro.active_domain
        if context.scene.MantaPro.active_domain.MantaPro.locked_cache:
            return {'CANCELLED'}

        if self.op == "DATA_B":
            bpy.ops.fluid.bake_data(overide, "INVOKE_DEFAULT")
        elif self.op == "DATA_F":
            bpy.ops.fluid.free_data(overide, "INVOKE_DEFAULT")
        elif self.op == "MESH_B":
            bpy.ops.fluid.bake_mesh(overide, "INVOKE_DEFAULT")
        elif self.op == "MESH_F":
            bpy.ops.fluid.free_mesh(overide, "INVOKE_DEFAULT")
        elif self.op == "PARTICLE_B":
            bpy.ops.fluid.bake_particles(overide, "INVOKE_DEFAULT")
        elif self.op == "PARTICLE_F":
            bpy.ops.fluid.free_particles(overide, "INVOKE_DEFAULT")
        elif self.op == "ALL_B":
            bpy.ops.fluid.bake_all(overide, "INVOKE_DEFAULT")
        elif self.op == "ALL_F":
            bpy.ops.fluid.free_all(overide, "INVOKE_DEFAULT")
        elif self.op == "NOISE_B":
            bpy.ops.fluid.bake_noise(overide, "INVOKE_DEFAULT")
        elif self.op == "NOISE_F":
            bpy.ops.fluid.free_noise(overide, "INVOKE_DEFAULT")
        elif self.op == "GUIDES_B":
            bpy.ops.fluid.bake_guides(overide, "INVOKE_DEFAULT")
        elif self.op == "GUIDES_F":
            bpy.ops.fluid.free_guides(overide, "INVOKE_DEFAULT")

        return {'FINISHED'}


class MantaProBakeEmber(bpy.types.Operator):
    bl_idname = "mantapro.bake_ember"
    bl_label = "bake cache"
    bl_description = "Bake Particle Cache"

    bl_options = {'REGISTER'}

    op: bpy.props.StringProperty(name="type")

    def execute(self, context):
        overide = context.copy()
        overide["object"] = context.scene.MantaPro.active_flow
        overide["active_object"] = context.scene.MantaPro.active_flow
        overide['point_cache'] = particles.find_ember_system(
            context.scene.MantaPro.active_flow).point_cache

        if self.op == "CACHE_B":
            bpy.ops.ptcache.bake(overide, "INVOKE_DEFAULT", bake=True)
        elif self.op == "TO_FRAME_B":
            bpy.ops.ptcache.bake(overide, "INVOKE_DEFAULT", bake=False)
        elif self.op == "CURRENT_TO_B":
            bpy.ops.ptcache.bake_from_cache(overide, "INVOKE_DEFAULT")
        elif self.op == "CACHE_F":
            bpy.ops.ptcache.free_bake(overide, "INVOKE_DEFAULT")

        elif self.op == "CACHE_B_ALL":
            bpy.ops.ptcache.bake_all(overide, "INVOKE_DEFAULT", bake=True)
        elif self.op == "TO_FRAME_B_ALL":
            bpy.ops.ptcache.bake_all(overide, "INVOKE_DEFAULT", bake=False)
        elif self.op == "CACHE_F_ALL":
            bpy.ops.ptcache.free_bake_all(overide, "INVOKE_DEFAULT")
        return {'FINISHED'}


"""
 ███████████                                                           ████
░░███░░░░░███                                                         ░░███
 ░███    ░███   ██████  █████████████    ██████  █████ █████  ██████   ░███      ██████  ████████   █████
 ░██████████   ███░░███░░███░░███░░███  ███░░███░░███ ░░███  ░░░░░███  ░███     ███░░███░░███░░███ ███░░
 ░███░░░░░███ ░███████  ░███ ░███ ░███ ░███ ░███ ░███  ░███   ███████  ░███    ░███ ░███ ░███ ░███░░█████
 ░███    ░███ ░███░░░   ░███ ░███ ░███ ░███ ░███ ░░███ ███   ███░░███  ░███    ░███ ░███ ░███ ░███ ░░░░███
 █████   █████░░██████  █████░███ █████░░██████   ░░█████   ░░████████ █████   ░░██████  ░███████  ██████
░░░░░   ░░░░░  ░░░░░░  ░░░░░ ░░░ ░░░░░  ░░░░░░     ░░░░░     ░░░░░░░░ ░░░░░     ░░░░░░   ░███░░░  ░░░░░░
                                                                                         ░███
                                                                                         █████
                                                                                        ░░░░░
"""


class MantaProRemoveForce(bpy.types.Operator):
    bl_idname = "mantapro.remove_force"
    bl_label = "remove selected force attribute"
    bl_description = "remove force from active object \n Ctrl ------------> delete object"

    bl_options = {'REGISTER', 'UNDO'}

    # is_domain: bpy.props.BoolProperty(name="is domain", default=False)
    kind: bpy.props.StringProperty(name="type", default="NONE")

    @classmethod
    def poll(cls, context):
        if context.scene.MantaPro.active_force:
            return True
        else:
            return False

    def invoke(self, context, event):
        delete = False
        if event.ctrl:
            delete = True

        if context.scene.MantaPro.active_force.MantaPro.force_ember:
            self.report(
                {"ERROR"}, "The Active Force is Part of an Ember System\nRemove the Ember System in order to Remove the Force")
            return {'FINISHED'}

        # get current area
        area = context.area.ui_type
        context.area.ui_type = "VIEW_3D"

        override = context.copy()
        context.scene.MantaPro.active_force.field.type = "NONE"

        if not context.scene.MantaPro.active_force:
            self.report({"INFO"}, "no active force")
            return {'CANCELLED'}

        override["object"] = context.scene.MantaPro.active_force
        override["active_object"] = context.scene.MantaPro.active_force
        context.scene.MantaPro.active_force = None

        context.scene.MantaPro.forces.remove(
            context.scene.MantaPro.forces_index)

        # this is a bug that the other ones do not have?
        if len(context.scene.MantaPro.forces) > 0:
            context.scene.MantaPro.forces_index -= 1

            context.scene.MantaPro.active_force = context.scene.MantaPro.forces[
                context.scene.MantaPro.forces_index].obj

        if delete:
            override["selected_objects"] = [override["object"], ]
            bpy.ops.object.delete(override)

        context.area.ui_type = area
        return {'FINISHED'}


class MantaProRemove(bpy.types.Operator):
    bl_idname = "mantapro.remove"
    bl_label = "remove active liquid modifier"
    bl_description = "remove liquid from active object \n Ctrl ------------> delete object"

    bl_options = {'REGISTER', 'UNDO'}

    # is_domain: bpy.props.BoolProperty(name="is domain", default=False)
    kind: bpy.props.StringProperty(name="type", default="NONE")

    @classmethod
    def poll(cls, context):
        if context.scene.MantaPro.active_domain or context.scene.MantaPro.active_flow or context.scene.MantaPro.active_effector:
            return True
        else:
            return False

    def invoke(self, context, event):
        delete = False
        if event.ctrl:
            delete = True

        # get current area
        area = context.area.ui_type
        context.area.ui_type = "VIEW_3D"

        override = context.copy()

        bounds = None

        if self.kind == "DOMAIN":
            if not context.scene.MantaPro.active_domain:
                self.report({"INFO"}, "no active domain")
                return {'CANCELLED'}

            if context.scene.MantaPro.active_domain.MantaPro.particle_collection:
                bpy.data.collections.remove(
                    context.scene.MantaPro.active_domain.MantaPro.particle_collection)

            override["object"] = context.scene.MantaPro.active_domain
            override["active_object"] = context.scene.MantaPro.active_domain
            context.scene.MantaPro.active_domain = None

            context.scene.MantaPro.domains.remove(
                context.scene.MantaPro.domains_index)
            if context.scene.MantaPro.domains_index > 0:
                context.scene.MantaPro.domains_index -= 1

                context.scene.MantaPro.active_domain = context.scene.MantaPro.domains[
                    context.scene.MantaPro.domains_index].obj

            bounds = overlays.find_bounds(override["object"])

        elif self.kind == "FLOW":
            if not context.scene.MantaPro.active_flow:
                self.report({"INFO"}, "no active flow")
                return {'CANCELLED'}

            override["object"] = context.scene.MantaPro.active_flow
            override["active_object"] = context.scene.MantaPro.active_flow
            context.scene.MantaPro.active_flow = None

            context.scene.MantaPro.flows.remove(
                context.scene.MantaPro.flows_index)
            if context.scene.MantaPro.flows_index > 0:
                context.scene.MantaPro.flows_index -= 1

                context.scene.MantaPro.active_flow = context.scene.MantaPro.flows[
                    context.scene.MantaPro.flows_index].obj

        elif self.kind == "EFFECTOR":
            if not context.scene.MantaPro.active_effector:
                self.report({"INFO"}, "no active effector")
                return {'CANCELLED'}

            override["object"] = context.scene.MantaPro.active_effector
            override["active_object"] = context.scene.MantaPro.active_effector
            context.scene.MantaPro.active_effector = None

            context.scene.MantaPro.effectors.remove(
                context.scene.MantaPro.effectors_index)
            if context.scene.MantaPro.effectors_index > 0:
                context.scene.MantaPro.effectors_index -= 1

                context.scene.MantaPro.active_effector = context.scene.MantaPro.effectors[
                    context.scene.MantaPro.effectors_index].obj

        bpy.ops.object.modifier_remove(override, modifier="Fluid")

        if delete:
            override["selected_objects"] = [override["object"], ]
            bpy.ops.object.delete(override)

        if bounds:
            override["object"] = bounds
            override["selected_objects"] = [bounds, ]
            bpy.ops.object.delete(override)

        context.area.ui_type = area
        return {'FINISHED'}


"""
    ██████   ███                 █████
   ███░░███ ░░░                 ░░███
  ░███ ░░░  ████  ████████    ███████      ██████  ████████   █████
 ███████   ░░███ ░░███░░███  ███░░███     ███░░███░░███░░███ ███░░
░░░███░     ░███  ░███ ░███ ░███ ░███    ░███ ░███ ░███ ░███░░█████
  ░███      ░███  ░███ ░███ ░███ ░███    ░███ ░███ ░███ ░███ ░░░░███
  █████     █████ ████ █████░░████████   ░░██████  ░███████  ██████
 ░░░░░     ░░░░░ ░░░░ ░░░░░  ░░░░░░░░     ░░░░░░   ░███░░░  ░░░░░░
                                                   ░███
                                                   █████
                                                  ░░░░░
"""


class MantaProFindDomain(bpy.types.Operator):
    bl_idname = "mantapro.find_domain"
    bl_label = "find domains"
    bl_description = "looks through all objects and adds any domain objects to the list"

    kind: bpy.props.StringProperty(name="type")

    def execute(self, context):
        bad = []
        for obj in bpy.data.objects:
            for exists in context.scene.MantaPro.domains.values():
                if obj == exists.obj:
                    bad.append(exists.obj)
            if obj.type == "MESH":
                modifier = modifiers.find_modifier(obj)
                if modifier:
                    if modifier.fluid_type == "DOMAIN":

                        if obj in bad:
                            continue
                        if context.scene in obj.users_scene:
                            set_domain_index(context, obj)

        return {'FINISHED'}


class MantaProFindFlow(bpy.types.Operator):
    bl_idname = "mantapro.find_flow"
    bl_label = "find flows"
    bl_description = "looks through all objects and adds any flow objects to the list"

    def execute(self, context):
        bad = []
        for obj in bpy.data.objects:
            for exists in context.scene.MantaPro.flows.values():
                if obj == exists.obj:
                    bad.append(exists.obj)
            if obj.type == "MESH":
                modifier = modifiers.find_modifier(obj)
                if modifier:
                    if modifier.fluid_type == "FLOW":

                        if obj in bad:
                            continue
                        if context.scene in obj.users_scene:
                            set_flow_index(context, obj)
        return {'FINISHED'}


class MantaProFindEffector(bpy.types.Operator):
    bl_idname = "mantapro.find_effector"
    bl_label = "find effectors"
    bl_description = "looks through all objects and adds any effector objects to the list"

    def execute(self, context):
        bad = []
        for obj in bpy.data.objects:
            for exists in context.scene.MantaPro.effectors.values():
                if obj == exists.obj:
                    bad.append(exists.obj)
            if obj.type == "MESH":
                modifier = modifiers.find_modifier(obj)
                if modifier:
                    if modifier.fluid_type == "EFFECTOR":
                        if obj in bad:
                            continue
                        if context.scene in obj.users_scene:
                            set_effector_index(context, obj)
        return {'FINISHED'}


class MantaProFindForce(bpy.types.Operator):
    bl_idname = "mantapro.find_force"
    bl_label = "find forces"
    bl_description = "looks through all objects and adds any forces objects to the list"

    def execute(self, context):
        bad = []
        for obj in bpy.data.objects:
            for exists in context.scene.MantaPro.forces.values():
                if obj == exists.obj:
                    bad.append(exists.obj)
            if obj.field:
                if obj.field.type != "NONE":
                    if obj in bad:
                        continue
                    if context.scene in obj.users_scene:
                        set_force_index(context, obj)
        return {'FINISHED'}


class MantaProFindAll(bpy.types.Operator):
    bl_idname = "mantapro.find_all"
    bl_label = "Find All"
    bl_description = "looks through all objects and adds any liquid objects to the proper list"

    kind: bpy.props.StringProperty(name="type")

    def execute(self, context):
        bpy.ops.mantapro.find_domain()
        bpy.ops.mantapro.find_flow()
        bpy.ops.mantapro.find_effector()
        bpy.ops.mantapro.find_force()
        return {'FINISHED'}


"""
              ███                                                         █████                        ████                     █████     ███
             ░░░                                                         ░░███                        ░░███                    ░░███     ░░░
 █████ █████ ████   ██████  █████ ███ █████ ████████   ██████  ████████  ███████       █████   ██████  ░███   ██████   ██████  ███████   ████   ██████  ████████
░░███ ░░███ ░░███  ███░░███░░███ ░███░░███ ░░███░░███ ███░░███░░███░░███░░░███░       ███░░   ███░░███ ░███  ███░░███ ███░░███░░░███░   ░░███  ███░░███░░███░░███
 ░███  ░███  ░███ ░███████  ░███ ░███ ░███  ░███ ░███░███ ░███ ░███ ░░░   ░███       ░░█████ ░███████  ░███ ░███████ ░███ ░░░   ░███     ░███ ░███ ░███ ░███ ░███
 ░░███ ███   ░███ ░███░░░   ░░███████████   ░███ ░███░███ ░███ ░███       ░███ ███    ░░░░███░███░░░   ░███ ░███░░░  ░███  ███  ░███ ███ ░███ ░███ ░███ ░███ ░███
  ░░█████    █████░░██████   ░░████░████    ░███████ ░░██████  █████      ░░█████     ██████ ░░██████  █████░░██████ ░░██████   ░░█████  █████░░██████  ████ █████
   ░░░░░    ░░░░░  ░░░░░░     ░░░░ ░░░░     ░███░░░   ░░░░░░  ░░░░░        ░░░░░     ░░░░░░   ░░░░░░  ░░░░░  ░░░░░░   ░░░░░░     ░░░░░  ░░░░░  ░░░░░░  ░░░░ ░░░░░
                                            ░███
                                            █████
                                           ░░░░░
"""


class MantaProVSelectDomain(bpy.types.Operator):
    bl_idname = "mantapro.viewport_select_domain"
    bl_label = "vieport select domain"
    bl_description = "if the active objet is a domian is selects it"

    def execute(self, context):
        settings = context.scene.MantaPro
        selection = settings.selection

        for item in settings.domains.values():
            item.name = item.obj.name
            if context.active_object == item.obj:
                if item.obj != selection.v_domain:
                    settings.domains_index = settings.domains.find(
                        item.obj.name)
                    selection.v_domain = item.obj

        return {'FINISHED'}


class MantaProVSelectFlow(bpy.types.Operator):
    bl_idname = "mantapro.viewport_select_flow"
    bl_label = "vieport select flow"
    bl_description = "if the active objet is a flow is selects it"

    def execute(self, context):
        settings = context.scene.MantaPro
        selection = settings.selection

        for item in settings.flows.values():
            item.name = item.obj.name
            if context.active_object == item.obj:
                if item.obj != selection.v_flow:
                    settings.flows_index = settings.flows.find(item.obj.name)
                    selection.v_flow = item.obj

        return {'FINISHED'}


class MantaProVSelectEffector(bpy.types.Operator):
    bl_idname = "mantapro.viewport_select_effector"
    bl_label = "vieport select effector"
    bl_description = "if the active objet is a effector is selects it"

    def execute(self, context):
        settings = context.scene.MantaPro
        selection = settings.selection

        for item in settings.effectors.values():
            item.name = item.obj.name
            if context.active_object == item.obj:
                if item.obj != selection.v_effector:
                    settings.effectors_index = settings.effectors.find(
                        item.obj.name)
                    selection.v_effector = item.obj

        return {'FINISHED'}


class MantaProVSelectForce(bpy.types.Operator):
    bl_idname = "mantapro.viewport_select_force"
    bl_label = "vieport select force"
    bl_description = "if the active objet is a force is selects it"

    def execute(self, context):
        settings = context.scene.MantaPro
        selection = settings.selection

        for item in settings.forces.values():
            item.name = item.obj.name
            if context.active_object == item.obj:
                if item.obj != selection.v_force:
                    settings.forces_index = settings.forces.find(
                        item.obj.name)
                    selection.v_force = item.obj

        return {'FINISHED'}


"""
   █████████               █████
  ███░░░░░███             ░░███
 ░███    ░███  █████ ████ ███████    ██████      ██████  ████████   █████
 ░███████████ ░░███ ░███ ░░░███░    ███░░███    ███░░███░░███░░███ ███░░
 ░███░░░░░███  ░███ ░███   ░███    ░███ ░███   ░███ ░███ ░███ ░███░░█████
 ░███    ░███  ░███ ░███   ░███ ███░███ ░███   ░███ ░███ ░███ ░███ ░░░░███
 █████   █████ ░░████████  ░░█████ ░░██████    ░░██████  ░███████  ██████
░░░░░   ░░░░░   ░░░░░░░░    ░░░░░   ░░░░░░      ░░░░░░   ░███░░░  ░░░░░░
                                                         ░███
                                                         █████
                                                        ░░░░░
"""


class MantaProAutoOps(bpy.types.Operator):
    bl_idname = "mantapro.auto_ops"
    bl_label = "auto"
    bl_description = ""
    global auto_ops_running

    auto_ops_running = False

    @classmethod
    def poll(cls, context):
        return True

    @classmethod
    def running(cls, context):
        return

    def execute(self, context):
        return {'FINISHED'}

    def modal(self, context, event):
        global auto_ops_running
        auto_ops_running = True
        settings = context.scene.MantaPro

        if settings.auto_ops_settings.auto_remove:
            bpy.ops.mantapro.remove_all_domain()
            bpy.ops.mantapro.remove_all_flow()
            bpy.ops.mantapro.remove_all_effector()
            bpy.ops.mantapro.remove_all_force()

        if settings.auto_ops_settings.auto_find:
            bpy.ops.mantapro.find_all()

        if settings.auto_ops_settings.auto_v_selection:
            bpy.ops.mantapro.viewport_select_domain()
            bpy.ops.mantapro.viewport_select_flow()
            bpy.ops.mantapro.viewport_select_effector()
            bpy.ops.mantapro.viewport_select_force()

        if not settings.auto_ops:
            auto_ops_running = False
            self.report({"ERROR"}, "Auto Operations are no longer Running")
            return {"CANCELLED"}
        else:
            return {'PASS_THROUGH'}

    def invoke(self, context, event):
        global auto_ops_running
        settings = context.scene.MantaPro.auto_ops_settings
        prefs = preferences.get_preferences()

        if not settings.updated_preferences:
            settings.auto_find = prefs.auto_find
            settings.auto_remove = prefs.auto_remove
            settings.auto_v_selection = prefs.auto_v_selection
            settings.updated_preferences = True

        if not auto_ops_running:
            context.window_manager.modal_handler_add(self)
        return {'RUNNING_MODAL'}


"""
   █████████               █████
  ███░░░░░███             ░░███
 ░███    ░███  █████ ████ ███████    ██████     ████████   ██████  █████████████    ██████  █████ █████  ██████
 ░███████████ ░░███ ░███ ░░░███░    ███░░███   ░░███░░███ ███░░███░░███░░███░░███  ███░░███░░███ ░░███  ███░░███
 ░███░░░░░███  ░███ ░███   ░███    ░███ ░███    ░███ ░░░ ░███████  ░███ ░███ ░███ ░███ ░███ ░███  ░███ ░███████
 ░███    ░███  ░███ ░███   ░███ ███░███ ░███    ░███     ░███░░░   ░███ ░███ ░███ ░███ ░███ ░░███ ███  ░███░░░
 █████   █████ ░░████████  ░░█████ ░░██████     █████    ░░██████  █████░███ █████░░██████   ░░█████   ░░██████
░░░░░   ░░░░░   ░░░░░░░░    ░░░░░   ░░░░░░     ░░░░░      ░░░░░░  ░░░░░ ░░░ ░░░░░  ░░░░░░     ░░░░░     ░░░░░░

"""


class MantaProRemoveAllDomain(bpy.types.Operator):
    bl_idname = "mantapro.remove_all_domain"
    bl_label = "remove all domain"
    bl_description = ""

    def execute(self, context):
        settings = context.scene.MantaPro

        for i in settings.domains.keys():
            item = settings.domains.find(i)
            if item >= 0:
                obj = settings.domains.values()[item].obj

                if not obj:
                    settings.domains.remove(item)
                    # settings.active_domain = None
                    if settings.domains_index > 0:
                        settings.domains_index -= 1
                else:
                    if not obj.users_scene or not modifiers.find_modifier(obj):
                        settings.domains.remove(item)
                        bounds = overlays.find_bounds(obj)
                        if bounds:
                            bpy.data.objects.remove(bounds)
                        if obj.MantaPro.particle_collection:
                            bpy.data.collections.remove(
                                obj.MantaPro.particle_collection)
                        if settings.active_domain == obj:
                            settings.active_domain = None
                            if settings.domains_index > 0:
                                settings.domains_index -= 1
                        if not obj.users:
                            bpy.data.objects.remove(obj)
        return {'FINISHED'}


class MantaProRemoveAllFlow(bpy.types.Operator):
    bl_idname = "mantapro.remove_all_flow"
    bl_label = "remove all flow"
    bl_description = ""

    def execute(self, context):
        settings = context.scene.MantaPro

        for i in settings.flows.keys():
            item = settings.flows.find(i)
            if item >= 0:
                obj = settings.flows.values()[item].obj
                if not obj:
                    settings.flows.remove(item)
                    # settings.active_flow = None
                    if settings.flows_index > 0:
                        settings.flows_index -= 1
                else:
                    if not obj.users_scene or not modifiers.find_modifier(obj):
                        settings.flows.remove(item)
                        if settings.active_flow == obj:
                            settings.active_flow = None
                            if settings.flows_index > 0:
                                settings.flows_index -= 1
                        if not obj.users:
                            bpy.data.objects.remove(obj)
        return {'FINISHED'}


class MantaProRemoveAllEffector(bpy.types.Operator):
    bl_idname = "mantapro.remove_all_effector"
    bl_label = "remove all effectors"
    bl_description = ""

    def execute(self, context):
        settings = context.scene.MantaPro

        for i in settings.effectors.keys():
            item = settings.effectors.find(i)
            if item >= 0:
                obj = settings.effectors.values()[item].obj
                if not obj:
                    settings.effectors.remove(item)
                    #settings.active_effector = None
                    if settings.effectors_index > 0:
                        settings.effectors_index -= 1
                else:
                    if not obj.users_scene or not modifiers.find_modifier(obj):
                        settings.effectors.remove(item)
                        if settings.active_effector == obj:
                            settings.active_effector = None
                            if settings.effectors_index > 0:
                                settings.effectors_index -= 1
                        if not obj.users:
                            bpy.data.objects.remove(obj)
        return {'FINISHED'}


class MantaProRemoveAllForce(bpy.types.Operator):
    bl_idname = "mantapro.remove_all_force"
    bl_label = "remove all forces"
    bl_description = ""

    def execute(self, context):
        settings = context.scene.MantaPro

        for i in settings.forces.keys():
            item = settings.forces.find(i)
            if item >= 0:
                obj = settings.forces.values()[item].obj
                if not obj:
                    settings.forces.remove(item)
                    #settings.active_force = None
                    if settings.forces_index > 0:
                        settings.forces_index -= 1
                else:
                    if not obj.users_scene or obj.field.type == "NONE":
                        settings.forces.remove(item)
                        if settings.active_force == obj:
                            settings.active_force = None
                            if settings.forces_index > 0:
                                settings.forces_index -= 1
                        if not obj.users:
                            bpy.data.objects.remove(obj)
        return {'FINISHED'}


"""
 ███████████                      ███           █████
░░███░░░░░███                    ░░░           ░░███
 ░███    ░███   ██████   ███████ ████   █████  ███████    ██████  ████████
 ░██████████   ███░░███ ███░░███░░███  ███░░  ░░░███░    ███░░███░░███░░███
 ░███░░░░░███ ░███████ ░███ ░███ ░███ ░░█████   ░███    ░███████  ░███ ░░░
 ░███    ░███ ░███░░░  ░███ ░███ ░███  ░░░░███  ░███ ███░███░░░   ░███
 █████   █████░░██████ ░░███████ █████ ██████   ░░█████ ░░██████  █████
░░░░░   ░░░░░  ░░░░░░   ░░░░░███░░░░░ ░░░░░░     ░░░░░   ░░░░░░  ░░░░░
                        ███ ░███
                       ░░██████
                        ░░░░░░
"""

classes = (
    MantaProRemove,
    MantaProRemoveForce,
    MantaProToggleBounds,
    MantaProDisplayBounds,
    MantaProSwitchType,
    MantaProSwitchForce,
    MantaProTransparentMaterial,
    MantaProToggleVoxlesView,


    # bake
    MantaProBake,
    MantaProBakeEmber,

    # ember
    MantaProSetupEmber,
    MantaProRemoveEmber,
    MantaProEmberDomainSync,
    MantaProToggleEmberParticles,
    MantaProQuickToggleEmberParticles,

    # add
    MantaProAddDomain,
    MantaProAddForceMenu,

    MantaProAddForce,

    MantaProMakeDomain,
    MantaProMakeFlow,
    MantaProEffector,

    MantaProMakeForceMenu,
    MantaProSwitchForceMenu,
    MantaProMakeForce,

    # cache
    MantaProMoveCache,
    MantaProMoveCacheIgnore,

    # other
    MantaProReplayClear,
    MantaProFixMaterial,

    # find
    MantaProFindDomain,
    MantaProFindFlow,
    MantaProFindEffector,
    MantaProFindForce,
    MantaProFindAll,

    # remove
    MantaProRemoveAllDomain,
    MantaProRemoveAllFlow,
    MantaProRemoveAllEffector,
    MantaProRemoveAllForce,

    # selection
    MantaProVSelectDomain,
    MantaProVSelectFlow,
    MantaProVSelectEffector,
    MantaProVSelectForce,

    MantaProAutoOps,
    MantaProUiLists,
)


keys = []

key_ops = [
    "mantapro.replay_clear",
    "mantapro.ui_lists",
]


@bpy.app.handlers.persistent
def load_handler(dummy):
    global auto_ops_running
    auto_ops_running = False
    if bpy.context.scene.MantaPro.auto_ops:
        bpy.ops.mantapro.auto_ops("INVOKE_DEFAULT")


def register():
    for i in classes:
        bpy.utils.register_class(i)

    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon

    if kc:
        km1 = wm.keyconfigs.addon.keymaps.new(
            name='Object Mode')

        kmi1 = km1.keymap_items.new(
            "mantapro.replay_clear", 'R', 'PRESS', ctrl=True, shift=False, alt=False)
        keys.append((km1, kmi1))

        kmi2 = km1.keymap_items.new(
            "mantapro.ui_lists", 'C', 'PRESS', ctrl=True, shift=True, alt=False)
        keys.append((km1, kmi2))

    bpy.app.handlers.load_post.append(load_handler)


def unregister():
    bpy.app.handlers.load_post.remove(load_handler)

    for km, kmi in keys:
        km.keymap_items.remove(kmi)
    keys.clear()

    for i in classes:
        bpy.utils.unregister_class(i)
