# space_view_3d_display_tools.py Copyright (C) 2014, Jordi Vall-llovera
# Multiple display tools for fast navigate/interact with the viewport

# ##### 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 LICENCE BLOCK #####
# Contributed to by:
# Jasperge, Pixaal, Meta-androcto, Lapineige, lijenstina,
# Felix Schlitter, Ales Sidenko, Jakub Belcik

bl_info = {
    "name": "Display Tools",
    "author": "Jordi Vall-llovera Medina, Jhon Wallace",
    "version": (1, 6, 4),
    "blender": (2, 70, 0),
    "location": "Toolshelf",
    "description": "Display tools for fast navigation/interaction with the viewport",
    "warning": "",
    "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
                "Py/Scripts/3D_interaction/Display_Tools",
    "category": "3D View"}


# Import From Files
if "bpy" in locals():
    import importlib
    importlib.reload(display)
    importlib.reload(fast_navigate)
    importlib.reload(modifier_tools)

    importlib.reload(shading_menu)
    importlib.reload(select_tools)
    importlib.reload(useless_tools)
    importlib.reload(selection_restrictor)

else:
    from . import display
    from . import fast_navigate
    from . import modifier_tools

    from . import shading_menu
    from . import select_tools
    from . import useless_tools
    from . import selection_restrictor

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


class DisplayToolsPanel(Panel):
    bl_label = "Display Tools"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = "Display"
    bl_options = {'DEFAULT_CLOSED'}

    display_type_icons = {
            'BOUNDS': 'BBOX',
            'WIRE': 'WIRE',
            'SOLID': 'SOLID',
            'TEXTURED': 'POTATO'
            }
    bounds_icons = {
            'BOX': 'MESH_CUBE',
            'SPHERE': 'MATSPHERE',
            'CYLINDER': 'MESH_CYLINDER',
            'CONE': 'MESH_CONE'
            }

    def draw(self, context):
        scene = context.scene
        display_tools = scene.display_tools
        render = scene.render
        view = context.space_data
        gs = scene.game_settings
        obj = context.object
        obj_type = obj.type if obj else None
        fx_settings = view.fx_settings

        DISPLAYDROP = display_tools.UiTabDrop[0]
        SHADINGDROP = display_tools.UiTabDrop[1]
        SCENEDROP = display_tools.UiTabDrop[2]
        MODIFIERDROP = display_tools.UiTabDrop[3]
        SELECT2DROP = display_tools.UiTabDrop[4]
        FASTNAVDROP = display_tools.UiTabDrop[5]
        icon_active_0 = "TRIA_RIGHT" if not DISPLAYDROP else "TRIA_DOWN"
        icon_active_1 = "TRIA_RIGHT" if not SHADINGDROP else "TRIA_DOWN"
        icon_active_2 = "TRIA_RIGHT" if not SCENEDROP else "TRIA_DOWN"
        icon_active_3 = "TRIA_RIGHT" if not MODIFIERDROP else "TRIA_DOWN"
        icon_active_4 = "TRIA_RIGHT" if not SELECT2DROP else "TRIA_DOWN"
        icon_active_5 = "TRIA_RIGHT" if not FASTNAVDROP else "TRIA_DOWN"
        icon_wt_handler = "X" if display_tools.WT_handler_enable else "MOD_WIREFRAME"

        layout = self.layout

        # Display Scene options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=2, text="Display", icon=icon_active_2)

        if not SCENEDROP:
            if obj:
                row.prop(obj, "show_texture_space", text="", icon="FACESEL_HLT")
                row.prop(obj, "show_name", text="", icon="SORTALPHA")
                row.prop(obj, "show_axis", text="", icon="AXIS_TOP")
        else:
            col = layout.column()
            col.prop(view, "show_manipulator")

            col = layout.column(align=True)
            col.alignment = 'EXPAND'
            col.prop(view, "show_only_render", toggle=True)
            col.prop(view, "show_world", toggle=True)
            col.prop(view, "show_outline_selected", toggle=True)
            col.prop(view, "show_all_objects_origin", toggle=True)
            col.prop(view, "show_backface_culling", toggle=True)
            if obj:
                col.prop(obj, "show_in_front", text="X-Ray", toggle=True)

            if obj and obj_type == 'MESH':
                col.prop(obj, "show_transparent", text="Transparency", toggle=True)

            col = layout.column()
            col.prop(render, "use_simplify", "Simplify", toggle=True)

            if render.use_simplify is True:
                col = layout.column(align=True)
                col.label(text="Settings :")
                col.prop(render, "simplify_subdivision", "Subdivision")
                col.prop(render, "simplify_shadow_samples", "Shadow Samples")
                col.prop(render, "simplify_child_particles", "Child Particles")
                col.prop(render, "simplify_ao_sss", "AO and SSS")

        # Draw Type options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=0, text="Draw Type", icon=icon_active_0)

        if not DISPLAYDROP:
            hide_wires = row.operator("ut.wire_show_hide", icon="MATSPHERE", text="")
            hide_wires.show = False
            hide_wires.selected = False
            show_wires = row.operator("ut.wire_show_hide", icon="MESH_UVSPHERE", text="")
            show_wires.show = True
            show_wires.selected = False
            row.operator("ut.all_edges", icon="MESH_GRID", text="").on = True
        else:
            if obj:
                col = layout.column(align=True)
                col.alignment = 'EXPAND'
                col.label(text="Display As:")
                col.prop(obj, "display_type", text="", icon=self.display_type_icons[obj.display_type])

            col = layout.column(align=True)
            col.alignment = 'CENTER'
            col.label(text="Selected Object(s):")
            row = col.row(align=True)
            row.operator("view3d.display_draw_change", text="Wire",
                         icon='WIRE').drawing = 'WIRE'
            row.operator("view3d.display_draw_change", text="Solid",
                        icon='SOLID').drawing = 'SOLID'
            row = col.row()
            row = col.row(align=True)
            row.operator("view3d.display_draw_change", text="Textured",
                         icon="TEXTURE_SHADED").drawing = 'TEXTURED'
            row.operator("view3d.display_draw_change", text="Bounds",
                         icon="BBOX").drawing = 'BOUNDS'

            col = layout.column(align=True)
            col.alignment = 'CENTER'
            col.label(text="Wire Overlay:")

            row = col.row()
            row.operator("object.wt_selection_handler_toggle", icon=icon_wt_handler)

            col = layout.column(align=True)
            col.alignment = 'CENTER'
            row = col.row(align=True)
            row.operator("object.wt_hide_all_wire", icon="SOLID", text="Hide All")
            row.operator("af_ops.wire_all", text="Toggle", icon="WIRE")

            row = col.row()
            row1 = col.row(align=True)
            hide_wire = row1.operator("ut.wire_show_hide", icon="MATSPHERE", text="Hide")
            hide_wire.show = False
            hide_wire.selected = True
            show_wire = row1.operator("ut.wire_show_hide", icon="MESH_UVSPHERE", text="Show")
            show_wire.show = True
            show_wire.selected = True

            col = layout.column(align=True)
            col.alignment = 'CENTER'
            row = col.row()
            row3 = col.row(align=True)
            row3.alignment = 'CENTER'
            row3.label(text="All Edges:")
            row3.operator("ut.all_edges", icon="MESH_PLANE", text="Off").on = False
            row3.operator("ut.all_edges", icon="MESH_GRID", text="On").on = True

            col = layout.column(align=True)
            col.alignment = 'EXPAND'
            col.label(text="Bounding Box:")
            row = col.row()
            row.prop(display_tools, "BoundingMode", text="Type")
            row = col.row()
            col.separator()
            col.operator("view3d.display_bounds_switch", "Bounds On",
                        icon='BBOX').bounds = True
            col.operator("view3d.display_bounds_switch", "Bounds Off",
                        icon='BBOX').bounds = False

        # Shading options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=1, text="Shading", icon=icon_active_1)

        if not SHADINGDROP:
            row.operator("object.shade_smooth", icon="SMOOTH", text="")
            row.operator("object.shade_flat", icon="MESH_ICOSPHERE", text="")
            row.menu("VIEW3D_MT_Shade_menu", icon='SOLID', text="")
        else:
            col = layout.column(align=True)
            col.alignment = 'EXPAND'

            if not scene.render.use_shading_nodes:
                col.prop(gs, "material_mode", text="", toggle=True)

            if view.viewport_shade == 'SOLID':
                col.prop(view, "show_textured_solid", toggle=True)
                col.prop(view, "use_matcap", toggle=True)
                if view.use_matcap:
                    col.template_icon_view(view, "matcap_icon")
            if view.viewport_shade == 'TEXTURED' or context.mode == 'PAINT_TEXTURE':
                if scene.render.use_shading_nodes or gs.material_mode != 'GLSL':
                    col.prop(view, "show_textured_shadeless", toggle=True)

            col.prop(view, "show_backface_culling", toggle=True)

            if view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'}:
                if obj and obj.mode == 'EDIT':
                    col.prop(view, "show_occlude_wire", toggle=True)
                if obj and obj_type == 'MESH' and obj.mode in {'EDIT'}:
                    col = layout.column(align=True)
                    col.label(text="Faces:")
                    row = col.row(align=True)
                    row.operator("mesh.faces_shade_smooth", text="Smooth")
                    row.operator("mesh.faces_shade_flat", text="Flat")
                    col.label(text="Edges:")
                    row = col.row(align=True)
                    row.operator("mesh.mark_sharp", text="Smooth").clear = True
                    row.operator("mesh.mark_sharp", text="Sharp")
                    col.label(text="Vertices:")
                    row = col.row(align=True)
                    props = row.operator("mesh.mark_sharp", text="Smooth")
                    props.use_verts = True
                    props.clear = True
                    row.operator("mesh.mark_sharp", text="Sharp").use_verts = True

                    col = layout.column(align=True)
                    col.label(text="Normals:")
                    col.operator("mesh.normals_make_consistent", text="Recalculate")
                    col.operator("mesh.flip_normals", text="Flip Direction")
                    col.operator("mesh.set_normals_from_faces", text="Set From Faces")
                    col.separator()

            if view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'}:
                sub = col.column()
                sub.active = view.region_3d.view_perspective == 'CAMERA'
                sub.prop(fx_settings, "use_dof", toggle=True)
                col.prop(fx_settings, "use_ssao", text="Ambient Occlusion", toggle=True)
                if fx_settings.use_ssao:
                    ssao_settings = fx_settings.ssao
                    subcol = col.column(align=True)
                    subcol.prop(ssao_settings, "factor")
                    subcol.prop(ssao_settings, "distance_max")
                    subcol.prop(ssao_settings, "attenuation")
                    subcol.prop(ssao_settings, "samples")
                    subcol.prop(ssao_settings, "color")

        # Modifier options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=3, text="Modifiers", icon=icon_active_3)

        if not MODIFIERDROP:
            mod_all_hide = row.operator("ut.subsurf_show_hide", icon="MOD_SOLIDIFY", text="")
            mod_all_hide.show = False
            mod_all_hide.selected = False
            mod_all_show = row.operator("ut.subsurf_show_hide", icon="MOD_SUBSURF", text="")
            mod_all_show.show = True
            mod_all_show.selected = False
            mod_optimal = row.operator("ut.optimaldisplay", icon="MESH_PLANE", text="")
            mod_optimal.on = True
            mod_optimal.selected = False
        else:
            col = layout.column(align=True)
            col.alignment = 'EXPAND'

            row = col.row(align=True)
            row.label(text="Viewport Visibility:", icon="RESTRICT_VIEW_OFF")
            row = col.row(align=True)
            row.operator("view3d.toggle_apply_modifiers_view", text="Viewport Vis")
            col.separator()

            row = col.row()
            row.label(text="Render Visibility:", icon="RENDER_STILL")
            row = col.row(align=True)
            row.operator("view3d.display_modifiers_render_switch", text="On").mod_render = True
            row.operator("view3d.display_modifiers_render_switch", text="Off").mod_render = False
            col.separator()

            row = col.row()
            row.label(text="Subsurf Visibility:", icon="ALIASED")

            col = layout.column(align=True)
            row1 = col.row(align=True)
            mod_all2_hide = row1.operator("ut.subsurf_show_hide", icon="MOD_SOLIDIFY", text="Hide")
            mod_all2_hide.show = False
            mod_all2_hide.selected = True
            mod_all2_show = row1.operator("ut.subsurf_show_hide", icon="MOD_SUBSURF", text="Show")
            mod_all2_show.show = True
            mod_all2_show.selected = True

            row2 = col.row(align=True)
            mod_sel_hide = row2.operator("ut.subsurf_show_hide", icon="MOD_SOLIDIFY", text="Hide All")
            mod_sel_hide.show = False
            mod_sel_hide.selected = False
            mod_sel_show = row2.operator("ut.subsurf_show_hide", icon="MOD_SUBSURF", text="Show All")
            mod_sel_show.show = True
            mod_sel_show.selected = False
            col.separator()

            col = layout.column()
            row = col.row(align=True)
            row.label(text="Edit Mode:", icon="EDITMODE_HLT")
            row = col.row(align=True)
            row.operator("view3d.display_modifiers_edit_switch", text="On").mod_edit = True
            row.operator("view3d.display_modifiers_edit_switch", text="Off").mod_edit = False
            col.separator()

            row = col.row()
            row.label(text="Modifier Cage:", icon="MOD_LATTICE")
            row = col.row(align=True)
            row.operator("view3d.display_modifiers_cage_set", text="On").set_cage = True
            row.operator("view3d.display_modifiers_cage_set", text="Off").set_cage = False
            col.separator()

            row = col.row(align=True)
            row.label(text="Subdivision Level:", icon="MOD_SUBSURF")

            row = col.row(align=True)
            row.operator("view3d.modifiers_subsurf_level_set", text="0").level = 0
            row.operator("view3d.modifiers_subsurf_level_set", text="1").level = 1
            row.operator("view3d.modifiers_subsurf_level_set", text="2").level = 2
            row.operator("view3d.modifiers_subsurf_level_set", text="3").level = 3
            row.operator("view3d.modifiers_subsurf_level_set", text="4").level = 4
            row.operator("view3d.modifiers_subsurf_level_set", text="5").level = 5
            row.operator("view3d.modifiers_subsurf_level_set", text="6").level = 6

        # Selection options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=4, text="Selection", icon=icon_active_4)

        if not SELECT2DROP:
            row.operator("view3d.select_box", text="", icon="MESH_PLANE")
            row.operator("view3d.select_circle", text="", icon="MESH_CIRCLE")
            row.label(text="", icon="BLANK1")
        else:
            if obj and obj.mode == 'OBJECT':
                col = layout.column(align=True)
                col.label(text="Render Visibility:")
                col.operator("op.render_show_all_selected", icon="RESTRICT_VIEW_OFF")
                col.operator("op.render_hide_all_selected", icon="RESTRICT_VIEW_ON")
                col.label(text="Show/Hide:")
                col.operator("opr.show_hide_object", text="Show/Hide", icon="GHOST_ENABLED")
                col.operator("opr.show_all_objects", text="Show All", icon="RESTRICT_VIEW_OFF")
                col.operator("opr.hide_all_objects", text="Hide Inactive", icon="RESTRICT_VIEW_ON")

            if obj:
                if obj.mode == 'OBJECT':
                    col = layout.column(align=True)
                    col.operator_menu_enum("object.show_by_type", "type", text="Show By Type")
                    col.operator_menu_enum("object.hide_by_type", "type", text="Hide By Type")
                    layout.label(text="Selection:")
                    col = layout.column(align=True)
                    col.operator_menu_enum("object.select_by_type", "type",
                                           text="Select All by Type...")

                if obj_type == 'MESH' and obj.mode == 'EDIT':
                    col = layout.column(align=True)
                    col.operator("mesh.select_linked", icon="ROTATECOLLECTION")
                    col.operator("opr.loop_multi_select", icon="OUTLINER_DATA_MESH")

            col = layout.column(align=True)
            col.operator("opr.select_all", icon="MOD_MESHDEFORM")
            col.operator("opr.inverse_selection", icon="MOD_REMESH")

        # fast nav options
        box1 = layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(display_tools, "UiTabDrop", index=5, text="Fast Nav", icon=icon_active_5)

        if not FASTNAVDROP:
            row.operator("view3d.fast_navigate_operator", text="", icon="NEXT_KEYFRAME")
            row.operator("view3d.fast_navigate_stop", text="", icon="PANEL_CLOSE")
            row.label(text="", icon="BLANK1")
        else:
            col = layout.column(align=True)
            col.operator("view3d.fast_navigate_operator", icon="NEXT_KEYFRAME")
            col.operator("view3d.fast_navigate_stop", icon="PANEL_CLOSE")

            layout.label(text="Settings:")
            layout.prop(display_tools, "OriginalMode")
            layout.prop(display_tools, "FastMode")
            layout.prop(display_tools, "EditActive", "Edit mode")

            layout.prop(display_tools, "Delay")
            col = layout.column(align=True)
            col.active = display_tools.Delay
            col.prop(display_tools, "DelayTimeGlobal", "Delay time")

            layout.prop(display_tools, "ShowParticles")
            col = layout.column(align=True)
            col.active = display_tools.ShowParticles
            col.prop(display_tools, "InitialParticles")
            col.prop(display_tools, "ParticlesPercentageDisplay")

            col = layout.column(align=True)
            col.label(text="Screen Active Area:")
            col.prop(display_tools, "ScreenStart")
            col.prop(display_tools, "ScreenEnd")


# define scene props
class display_tools_scene_props(PropertyGroup):
    # Init delay variables
    Delay: BoolProperty(
            default=False,
            description="Activate delay return to normal viewport mode"
            )
    DelayTime: IntProperty(
            default=30,
            min=0,
            max=500,
            soft_min=10,
            soft_max=250,
            description="Delay time to return to normal viewport"
                        "mode after move your mouse cursor"
            )
    DelayTimeGlobal: IntProperty(
            default=30,
            min=1,
            max=500,
            soft_min=10,
            soft_max=250,
            description="Delay time to return to normal viewport"
                        "mode after move your mouse cursor"
            )
    # Init variable for fast navigate
    EditActive: BoolProperty(
            default=True,
            description="Activate for fast navigate in edit mode too"
            )
    # Init properties for scene
    FastNavigateStop: BoolProperty(
            name="Fast Navigate Stop",
            description="Stop fast navigate mode",
            default=False
            )
    OriginalMode: EnumProperty(
            items=[('TEXTURED', 'Texture', 'Texture display mode'),
                   ('SOLID', 'Solid', 'Solid display mode')],
            name="Normal",
            default='SOLID'
            )
    BoundingMode: EnumProperty(
            items=[('BOX', 'Box', 'Box shape'),
                   ('SPHERE', 'Sphere', 'Sphere shape'),
                   ('CYLINDER', 'Cylinder', 'Cylinder shape'),
                   ('CONE', 'Cone', 'Cone shape')],
            name="BB Mode"
            )
    FastMode: EnumProperty(
            items=[('WIREFRAME', 'Wireframe', 'Wireframe display'),
                   ('BOUNDBOX', 'Bounding Box', 'Bounding Box display')],
            name="Fast"
            )
    ShowParticles: BoolProperty(
            name="Show Particles",
            description="Show or hide particles on fast navigate mode",
            default=True
            )
    ParticlesPercentageDisplay: IntProperty(
            name="Fast Display",
            description="Display only a percentage of particles when active",
            default=25,
            min=0,
            max=100,
            soft_min=0,
            soft_max=100,
            subtype='FACTOR'
            )
    InitialParticles: IntProperty(
            name="Normal Display",
            description="When idle, how much particles are displayed\n"
                        "Overrides the Particles settings",
            default=100,
            min=0,
            max=100,
            soft_min=0,
            soft_max=100
            )
    Symplify: IntProperty(
            name="Integer",
            description="Enter an integer"
            )
    ScreenStart: IntProperty(
            name="Left Limit",
            default=0,
            min=0,
            max=1024,
            subtype='PIXEL',
            description="Limit the screen active area width from the left side\n"
                        "Changed values will take effect on the next run",
            )
    ScreenEnd: IntProperty(
            name="Right Limit",
            default=0,
            min=0,
            max=1024,
            subtype='PIXEL',
            description="Limit the screen active area width from the right side\n"
                        "Changed values will take effect on the next run",
            )
    # Define the UI drop down prop
    UiTabDrop = BoolVectorProperty(
            name="Tab",
            description="Expand/Collapse UI elements",
            default=(False,) * 6,
            size=6,
            )
    WT_handler_enable: BoolProperty(
            default=False
            )
    WT_handler_previous_object: StringProperty(
            default=""
            )


# Addons Preferences Update Panel
# Define Panels for updating
panels = (
    DisplayToolsPanel,
    )


def update_panel(self, context):
    message = "Display Tools: Updating Panel locations has failed"
    try:
        for panel in panels:
            if "bl_rna" in panel.__dict__:
                bpy.utils.unregister_class(panel)

        for panel in panels:
            panel.bl_category = context.preferences.addons[__name__].preferences.category
            bpy.utils.register_class(panel)

    except Exception as e:
        print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
        pass


class DisplayToolsPreferences(AddonPreferences):
    # this must match the addon name, use '__package__'
    # when defining this in a submodule of a python package.
    bl_idname = __name__

    category: StringProperty(
            name="Tab Category",
            description="Choose a name for the category of the panel",
            default="Display",
            update=update_panel
            )

    def draw(self, context):
        layout = self.layout
        row = layout.row()
        col = row.column()
        col.label(text="Tab Category:")
        col.prop(self, "category", text="")


def DRAW_hide_by_type_MENU(self, context):
    self.layout.operator_menu_enum(
        "object.hide_by_type",
        "type", text="Hide By Type"
        )
    self.layout.operator_menu_enum(
        "object.show_by_type",
        "type", text="Show By Type"
        )


# register the classes and props
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object_showhide.append(DRAW_hide_by_type_MENU)
    # Register Scene Properties
    bpy.types.Scene.display_tools = PointerProperty(
                                        type=display_tools_scene_props
                                        )
    update_panel(None, bpy.context)
    selection_restrictor.register()


def unregister():
    selection_restrictor.unregister()
    bpy.types.VIEW3D_MT_object_showhide.remove(DRAW_hide_by_type_MENU)
    bpy.utils.unregister_module(__name__)
    del bpy.types.Scene.display_tools


if __name__ == "__main__":
    register()
