# ##### 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 #####

# Contributed to by:
# meta-androcto, Bill Currie, Jorge Hernandez - Melenedez  Jacob Morris, Oscurart  #
# Rebellion, Antonis Karvelas, Eleanor Howick, lijenstina, Daniel Schalla, Domlysz #
# Unnikrishnan(kodemax), Florian Meyer, Omar ahmed, Brian Hinton (Nichod), liero   #
# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote #

bl_info = {
    "name": "Add Advanced Object Panels",
    "author": "meta-androcto",
    "version": (1, 1, 5),
    "blender": (2, 77, 0),
    "description": "Individual Create Panel Activation List",
    "location": "Addons Preferences",
    "warning": "",
    "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
                "/Py/Scripts/Object/Add_Advanced",
    "category": "Object"
    }

import bpy
from bpy.types import (
        AddonPreferences,
        PropertyGroup,
        )
from bpy.props import (
        BoolProperty,
        BoolVectorProperty,
        EnumProperty,
        FloatProperty,
        FloatVectorProperty,
        IntProperty,
        StringProperty,
        PointerProperty,
        )

sub_modules_names = (
    "drop_to_ground",
    "object_laplace_lightning",
    "object_mangle_tools",
    "unfold_transition",
    "delaunay_voronoi",
    "oscurart_constellation",
    )


sub_modules = [__import__(__package__ + "." + submod, {}, {}, submod) for submod in sub_modules_names]
sub_modules.sort(key=lambda mod: (mod.bl_info['category'], mod.bl_info['name']))


# Add-ons Preferences
def _get_pref_class(mod):
    import inspect

    for obj in vars(mod).values():
        if inspect.isclass(obj) and issubclass(obj, PropertyGroup):
            if hasattr(obj, 'bl_idname') and obj.bl_idname == mod.__name__:
                return obj


def get_addon_preferences(name=''):
    """Acquisition and registration"""
    addons = bpy.context.preferences.addons
    if __name__ not in addons:  # wm.read_factory_settings()
        return None
    addon_prefs = addons[__name__].preferences
    if name:
        if not hasattr(addon_prefs, name):
            for mod in sub_modules:
                if mod.__name__.split('.')[-1] == name:
                    cls = _get_pref_class(mod)
                    if cls:
                        prop = PointerProperty(type=cls)
                        setattr(AdvancedObjPreferences1, name, prop)
                        bpy.utils.unregister_class(AdvancedObjPreferences1)
                        bpy.utils.register_class(AdvancedObjPreferences1)
        return getattr(addon_prefs, name, None)
    else:
        return addon_prefs


def register_submodule(mod):
    if not hasattr(mod, '__addon_enabled__'):
        mod.__addon_enabled__ = False
    if not mod.__addon_enabled__:
        mod.register()
        mod.__addon_enabled__ = True


def unregister_submodule(mod):
    if mod.__addon_enabled__:
        mod.unregister()
        mod.__addon_enabled__ = False

        prefs = get_addon_preferences()
        name = mod.__name__.split('.')[-1]
        if hasattr(AdvancedObjPreferences1, name):
            delattr(AdvancedObjPreferences1, name)
            if prefs:
                bpy.utils.unregister_class(AdvancedObjPreferences1)
                bpy.utils.register_class(AdvancedObjPreferences1)
                if name in prefs:
                    del prefs[name]


def enable_all_modules(self, context):
    for mod in sub_modules:
        mod_name = mod.__name__.split('.')[-1]
        setattr(self, 'use_' + mod_name, False)
        if not mod.__addon_enabled__:
            setattr(self, 'use_' + mod_name, True)
            mod.__addon_enabled__ = True

    return None


def disable_all_modules(self, context):
    for mod in sub_modules:
        mod_name = mod.__name__.split('.')[-1]

        if mod.__addon_enabled__:
            setattr(self, 'use_' + mod_name, False)
            mod.__addon_enabled__ = False

    return None


class AdvancedObjPreferences1(AddonPreferences):
    bl_idname = __name__

    enable_all: BoolProperty(
            name="Enable all",
            description="Enable all Advanced Objects' Panels",
            default=False,
            update=enable_all_modules
            )
    disable_all: BoolProperty(
            name="Disable all",
            description="Disable all Advanced Objects' Panels",
            default=False,
            update=disable_all_modules
            )

    def draw(self, context):
        layout = self.layout
        split = layout.split(percentage=0.5, align=True)
        row = split.row()
        row.alignment = "LEFT"
        sub_box = row.box()
        sub_box.prop(self, "enable_all", emboss=False,
                    icon="VISIBLE_IPO_ON", icon_only=True)
        row.label(text="Enable All")

        row = split.row()
        row.alignment = "RIGHT"
        row.label(text="Disable All")
        sub_box = row.box()
        sub_box.prop(self, "disable_all", emboss=False,
                    icon="VISIBLE_IPO_OFF", icon_only=True)

        for mod in sub_modules:
            mod_name = mod.__name__.split('.')[-1]
            info = mod.bl_info
            column = layout.column()
            box = column.box()

            # first stage
            expand = getattr(self, 'show_expanded_' + mod_name)
            icon = 'TRIA_DOWN' if expand else 'TRIA_RIGHT'
            col = box.column()
            row = col.row()
            sub = row.row()
            sub.context_pointer_set('addon_prefs', self)
            op = sub.operator('wm.context_toggle', text='', icon=icon,
                              emboss=False)
            op.data_path = 'addon_prefs.show_expanded_' + mod_name
            sub.label(text='{}: {}'.format(info['category'], info['name']))
            sub = row.row()
            sub.alignment = 'RIGHT'
            if info.get('warning'):
                sub.label(text='', icon='ERROR')
            sub.prop(self, 'use_' + mod_name, text='')

            # The second stage
            if expand:
                if info.get('description'):
                    split = col.row().split(percentage=0.15)
                    split.label(text='Description:')
                    split.label(text=info['description'])
                if info.get('location'):
                    split = col.row().split(percentage=0.15)
                    split.label(text='Location:')
                    split.label(text=info['location'])
                if info.get('author'):
                    split = col.row().split(percentage=0.15)
                    split.label(text='Author:')
                    split.label(text=info['author'])
                if info.get('version'):
                    split = col.row().split(percentage=0.15)
                    split.label(text='Version:')
                    split.label(text='.'.join(str(x) for x in info['version']),
                                translate=False)
                if info.get('warning'):
                    split = col.row().split(percentage=0.15)
                    split.label(text='Warning:')
                    split.label(text='  ' + info['warning'], icon='ERROR')

                tot_row = int(bool(info.get('wiki_url')))
                if tot_row:
                    split = col.row().split(percentage=0.15)
                    split.label(text='Internet:')
                    if info.get('wiki_url'):
                        op = split.operator('wm.url_open',
                                            text='Documentation', icon='HELP')
                        op.url = info.get('wiki_url')
                    for i in range(4 - tot_row):
                        split.separator()

                # Details and settings
                if getattr(self, 'use_' + mod_name):
                    prefs = get_addon_preferences(mod_name)

                    if prefs and hasattr(prefs, 'draw'):
                        box = box.column()
                        prefs.layout = box
                        try:
                            prefs.draw(context)
                        except:
                            import traceback
                            traceback.print_exc()
                            box.label(text="Error (see console)", icon="ERROR")
                        del prefs.layout

        row = layout.row()
        row.label(text="End of Advanced Object Panels Activations",
                  icon="FILE_PARENT")


for mod in sub_modules:
    info = mod.bl_info
    mod_name = mod.__name__.split('.')[-1]

    def gen_update(mod):
        def update(self, context):
            if getattr(self, 'use_' + mod.__name__.split('.')[-1]):
                if not mod.__addon_enabled__:
                    register_submodule(mod)
            else:
                if mod.__addon_enabled__:
                    unregister_submodule(mod)
        return update

    prop = BoolProperty(
            name=info['name'],
            description=info.get('description', ''),
            update=gen_update(mod),
            )
    setattr(AdvancedObjPreferences1, 'use_' + mod_name, prop)
    prop = BoolProperty()
    setattr(AdvancedObjPreferences1, 'show_expanded_' + mod_name, prop)


class AdvancedObjProperties1(PropertyGroup):

    # object_laplace_lighting props
    ORIGIN: FloatVectorProperty(
            name="Origin charge"
            )
    GROUNDZ: IntProperty(
            name="Ground Z coordinate"
            )
    HORDER: IntProperty(
            name="Secondary paths orders",
            default=1
            )
    # object_laplace_lighting UI props
    TSTEPS: IntProperty(
            name="Iterations",
            default=350,
            description="Number of cells to create\n"
                        "Will end early if hits ground plane or cloud"
            )
    GSCALE: FloatProperty(
            name="Grid unit size",
            default=0.12,
            description="scale of cells, .25 = 4 cells per blenderUnit"
            )
    BIGVAR: FloatProperty(
            name="Straightness",
            default=6.3,
            description="Straightness/branchiness of bolt, \n"
                        "<2 is mush, >12 is staight line, 6.3 is good"
            )
    GROUNDBOOL: BoolProperty(
            name="Use Ground object",
            description="Use ground plane or not",
            default=True
            )
    GROUNDC: IntProperty(
            name="Ground charge",
            default=-250,
            description="Charge of the ground plane"
            )
    CLOUDBOOL: BoolProperty(
            name="Use Cloud object",
            default=False,
            description="Use cloud object - attracts and terminates like ground but\n"
                        "any obj instead of z plane\n"
                        "Can slow down loop if obj is large, overrides ground"
            )
    CLOUDC: IntProperty(
            name="Cloud charge",
            default=-1,
            description="Charge of a cell in cloud object\n"
                        "(so total charge also depends on obj size)"
            )
    VMMESH: BoolProperty(
            name="Multi mesh",
            default=True,
            description="Output to multi-meshes for different materials on main/sec/side branches"
            )
    VSMESH: BoolProperty(
            name="Single mesh",
            default=False,
            description="Output to single mesh for using build modifier and particles for effects"
            )
    VCUBE: BoolProperty(
            name="Cubes",
            default=False,
            description="CTRL-J after run to JOIN\n"
                        "Outputs a bunch of cube objects, mostly for testing"
            )
    VVOX: BoolProperty(
            name="Voxel (experimental)",
            default=False,
            description="Output to a voxel file to bpy.data.filepath\FSLGvoxels.raw\n"
                        "(doesn't work well right now)"
            )
    IBOOL: BoolProperty(
            name="Use Insulator object",
            default=False,
            description="Use insulator mesh object to prevent growth of bolt in areas"
            )
    OOB: StringProperty(
            name="Select",
            default="",
            description="Origin of bolt, can be an Empty\n"
                        "if object is a mesh will use all verts as charges")
    GOB: StringProperty(
            name="Select",
            default="",
            description="Object to use as ground plane, uses z coord only"
            )
    COB: StringProperty(
            name="Select",
            default="",
            description="Object to use as cloud, best to use a cube"
            )
    IOB: StringProperty(
            name="Select",
            default="",
            description="Object to use as insulator, 'voxelized'\n"
                        "before generating bolt (can be slow)"
            )
    # object_mangle_tools properties
    mangle_constraint_vector = BoolVectorProperty(
            name="Mangle Constraint",
            default=(True, True, True),
            subtype='XYZ',
            description="Constrains Mangle Direction"
            )
    mangle_random_magnitude: IntProperty(
            name="Mangle Severity",
            default=5,
            min=1, max=30,
            description="Severity of mangling"
            )
    mangle_name: StringProperty(
            name="Shape Key Name",
            default="mangle",
            description="Name given for mangled shape keys"
            )
    # unfold_transition properties
    unfold_arm_name: StringProperty(
            default=""
            )
    unfold_modo: EnumProperty(
            name="",
            items=[("cursor", "3D Cursor", "Use the Distance to 3D Cursor"),
                   ("weight", "Weight Map", "Use a Painted Weight map"),
                   ("index", "Mesh Indices", "Use Faces and Vertices index")],
            description="How to Sort Bones for animation", default="cursor"
            )
    unfold_flip: BoolProperty(
            name="Flipping Faces",
            default=False,
            description="Rotate faces around the Center and skip Scaling - "
                        "keep checked for both operators"
            )
    unfold_fold_duration: IntProperty(
            name="Total Time",
            min=5, soft_min=25,
            max=10000, soft_max=2500,
            default=200,
            description="Total animation length"
            )
    unfold_sca_time: IntProperty(
            name="Scale Time",
            min=1,
            max=5000, soft_max=500,
            default=10,
            description="Faces scaling time"
            )
    unfold_rot_time: IntProperty(
            name="Rotation Time",
            min=1, soft_min=5,
            max=5000, soft_max=500,
            default=15,
            description="Faces rotation time"
            )
    unfold_rot_max: IntProperty(
            name="Angle",
            min=-180,
            max=180,
            default=135,
            description="Faces rotation angle"
            )
    unfold_fold_noise: IntProperty(
            name="Noise",
            min=0,
            max=500, soft_max=50,
            default=0,
            description="Offset some faces animation"
            )
    unfold_bounce: FloatProperty(
            name="Bounce",
            min=0,
            max=10, soft_max=2.5,
            default=0,
            description="Add some bounce to rotation"
            )
    unfold_from_point: BoolProperty(
            name="Point",
            default=False,
            description="Scale faces from a Point instead of from an Edge"
            )
    unfold_wiggle_rot: BoolProperty(
            name="Wiggle",
            default=False,
            description="Use all Axis + Random Rotation instead of X Aligned"
            )
    # oscurart_constellation
    constellation_limit: FloatProperty(
            name="Initial Threshold",
            description="Edges will be created only if the distance\n"
                        "between vertices is smaller than this value\n"
                        "This is a starting value on Operator Invoke",
            default=2,
            min=0
            )


# Class list
classes = (
    AdvancedObjPreferences1,
    AdvancedObjProperties1,
    )


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

    bpy.types.Scene.advanced_objects1 = PointerProperty(
                                            type=AdvancedObjProperties1
                                            )

    prefs = get_addon_preferences()
    for mod in sub_modules:
        if not hasattr(mod, '__addon_enabled__'):
            mod.__addon_enabled__ = False
        name = mod.__name__.split('.')[-1]
        if getattr(prefs, 'use_' + name):
            register_submodule(mod)


def unregister():
    for mod in sub_modules:
        if mod.__addon_enabled__:
            unregister_submodule(mod)
    del bpy.types.Scene.advanced_objects1

    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
