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

# <pep8 compliant>

bl_info = {
    "name": "Hotkey: 'Tab'",
    "description": "Switch between 3d view object/edit modes",
    "author": "pitiwazou, meta-androcto, italic",
    "version": (0, 1, 2),
    "blender": (2, 80, 0),
    "location": "3D View",
    "warning": "",
    "wiki_url": "",
    "category": "Mode Switch Pie"
}

import bpy
from bpy.types import (
    Menu,
    Operator
)


class PIE_OT_ClassObject(Operator):
    bl_idname = "class.object"
    bl_label = "Class Object"
    bl_description = "Edit/Object Mode Switch"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode == "OBJECT":
            bpy.ops.object.mode_set(mode="EDIT")
        else:
            bpy.ops.object.mode_set(mode="OBJECT")
        return {'FINISHED'}


class PIE_OT_ClassTexturePaint(Operator):
    bl_idname = "class.pietexturepaint"
    bl_label = "Class Texture Paint"
    bl_description = "Texture Paint"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode == "EDIT":
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.paint.texture_paint_toggle()
        else:
            bpy.ops.paint.texture_paint_toggle()
        return {'FINISHED'}


class PIE_OT_ClassWeightPaint(Operator):
    bl_idname = "class.pieweightpaint"
    bl_label = "Class Weight Paint"
    bl_description = "Weight Paint"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode == "EDIT":
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.paint.weight_paint_toggle()
        else:
            bpy.ops.paint.weight_paint_toggle()
        return {'FINISHED'}


class PIE_OT_ClassVertexPaint(Operator):
    bl_idname = "class.pievertexpaint"
    bl_label = "Class Vertex Paint"
    bl_description = "Vertex Paint"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode == "EDIT":
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.paint.vertex_paint_toggle()
        else:
            bpy.ops.paint.vertex_paint_toggle()
        return {'FINISHED'}


class PIE_OT_ClassParticleEdit(Operator):
    bl_idname = "class.pieparticleedit"
    bl_label = "Class Particle Edit"
    bl_description = "Particle Edit (must have active particle system)"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode == "EDIT":
            bpy.ops.object.mode_set(mode="OBJECT")
            bpy.ops.particle.particle_edit_toggle()
        else:
            bpy.ops.particle.particle_edit_toggle()
        return {'FINISHED'}


# Set Mode Operator #
class PIE_OT_SetObjectModePie(Operator):
    bl_idname = "object.set_object_mode_pie"
    bl_label = "Set the object interactive mode"
    bl_description = "I set the interactive mode of object"
    bl_options = {'REGISTER'}

    mode: bpy.props.StringProperty(name="Interactive mode", default="OBJECT")

    def execute(self, context):
        if (context.active_object):
            try:
                bpy.ops.object.mode_set(mode=self.mode)
            except TypeError:
                msg = context.active_object.name + " It is not possible to enter into the interactive mode"
                self.report(type={"WARNING"}, message=msg)
        else:
            self.report(type={"WARNING"}, message="There is no active object")
        return {'FINISHED'}


# Edit Selection Modes 
class PIE_OT_ClassVertex(Operator):
    bl_idname = "class.vertex"
    bl_label = "Class Vertex"
    bl_description = "Vert Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        if bpy.ops.mesh.select_mode != "EDGE, FACE":
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
            return {'FINISHED'}


class PIE_OT_ClassEdge(Operator):
    bl_idname = "class.edge"
    bl_label = "Class Edge"
    bl_description = "Edge Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
        if bpy.ops.mesh.select_mode != "VERT, FACE":
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            return {'FINISHED'}


class PIE_OT_ClassFace(Operator):
    bl_idname = "class.face"
    bl_label = "Class Face"
    bl_description = "Face Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
        if bpy.ops.mesh.select_mode != "VERT, EDGE":
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
            return {'FINISHED'}


class PIE_OT_VertsEdges(Operator):
    bl_idname = "verts.edges"
    bl_label = "Verts Edges"
    bl_description = "Vert/Edge Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        if bpy.ops.mesh.select_mode != "VERT, EDGE, FACE":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='EDGE')
            return {'FINISHED'}


class PIE_OT_EdgesFaces(Operator):
    bl_idname = "edges.faces"
    bl_label = "EdgesFaces"
    bl_description = "Edge/Face Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
        if bpy.ops.mesh.select_mode != "VERT, EDGE, FACE":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='FACE')
            return {'FINISHED'}


class PIE_OT_VertsFaces(Operator):
    bl_idname = "verts.faces"
    bl_label = "Verts Faces"
    bl_description = "Vert/Face Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        if bpy.ops.mesh.select_mode != "VERT, EDGE, FACE":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='FACE')
            return {'FINISHED'}


class PIE_OT_VertsEdgesFaces(Operator):
    bl_idname = "verts.edgesfaces"
    bl_label = "Verts Edges Faces"
    bl_description = "Vert/Edge/Face Select Mode"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        if context.object.mode != "EDIT":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
        if bpy.ops.mesh.select_mode != "VERT, EDGE, FACE":
            bpy.ops.object.mode_set(mode="EDIT")
            bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='EDGE')
            bpy.ops.mesh.select_mode(use_extend=True, use_expand=False, type='FACE')
            return {'FINISHED'}


# Grease Pencil Interactive Mode
class PIE_OT_InteractiveModeGreasePencil(Operator):
    bl_idname = "view3d.pie_interactive_mode_grease_pencil"
    bl_label = "Edit Strokes"
    bl_description = "Toggle Edit Strokes for Grease Pencil"

    @classmethod
    def poll(cls, context):
        return (context.gpencil_data is not None)

    def execute(self, context):
        try:
            bpy.ops.gpencil.editmode_toggle()
        except:
            self.report({'WARNING'},
                        "It is not possible to enter into the interactive mode")
        return {'FINISHED'}


# Menus
class PIE_MT_ObjectEditotherModes(Menu):
    """Edit/Object Others modes"""
    bl_idname = "MENU_MT_objecteditmodeothermodes"
    bl_label = "Edit Selection Modes"

    def draw(self, context):
        layout = self.layout
        pie = layout.menu_pie()
        box = pie.split().column()

        box.operator("class.vertex", text="Vertex", icon='VERTEXSEL')
        box.operator("class.edge", text="Edge", icon='EDGESEL')
        box.operator("class.face", text="Face", icon='FACESEL')
        box.operator("verts.faces", text="Vertex/Faces", icon='VERTEXSEL')
        box.operator("verts.edges", text="Vertex/Edges", icon='EDGESEL')
        box.operator("edges.faces", text="Edges/Faces", icon='FACESEL')
        box.operator("verts.edgesfaces", text="Vertex/Edges/Faces", icon='OBJECT_DATAMODE')


class PIE_MT_ObjectEditMode(Menu):
    """Modes Switch"""
    bl_idname = "PIE_MT_objecteditmode"
    bl_label = "Modes Menu (Tab)"

    def draw(self, context):
        layout = self.layout
        ob = context.object
        # No Object Selected #
        if not ob or not ob.select_get():
            message = "No Active Object Selected" 
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            box = pie.box()
            box.label(text=message, icon="INFO")

        elif ob and ob.type == 'MESH' and ob.mode in {'OBJECT', 'SCULPT', 'VERTEX_PAINT',
                                                    'WEIGHT_PAINT', 'TEXTURE_PAINT',
                                                    'PARTICLE_EDIT', 'GPENCIL_EDIT'}:
            pie = layout.menu_pie()
            # 4 - LEFT
            pie.operator("class.pievertexpaint", text="Vertex Paint", icon='VPAINT_HLT')
            # 6 - RIGHT
            pie.operator("class.pietexturepaint", text="Texture Paint", icon='TPAINT_HLT')
            # 2 - BOTTOM
            pie.menu("MENU_MT_objecteditmodeothermodes", text="Vert,Edge,Face Modes", icon='EDITMODE_HLT')
            # 8 - TOP
            pie.operator("class.object", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            # 7 - TOP - LEFT
            pie.operator("sculpt.sculptmode_toggle", text="Sculpt", icon='SCULPTMODE_HLT')
            # 9 - TOP - RIGHT
            pie.operator("class.pieweightpaint", text="Weight Paint", icon='WPAINT_HLT')
            # 1 - BOTTOM - LEFT
            if context.object.particle_systems:
                pie.operator("class.pieparticleedit", text="Particle Edit", icon='PARTICLEMODE')
            else:
                pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'MESH' and ob.mode in {'EDIT'}:
            pie = layout.menu_pie()
            # 4 - LEFT
            pie.operator("class.pievertexpaint", text="Vertex Paint", icon='VPAINT_HLT')
            # 6 - RIGHT
            pie.operator("class.pietexturepaint", text="Texture Paint", icon='TPAINT_HLT')
            # 2 - BOTTOM
            pie.menu("MENU_MT_objecteditmodeothermodes", text="Edit Modes", icon='EDITMODE_HLT')
            # 8 - TOP
            pie.operator("class.object", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            # 7 - TOP - LEFT
            pie.operator("sculpt.sculptmode_toggle", text="Sculpt", icon='SCULPTMODE_HLT')
            # 9 - TOP - RIGHT
            pie.operator("class.pieweightpaint", text="Weight Paint", icon='WPAINT_HLT')
            # 1 - BOTTOM - LEFT
            if context.object.particle_systems:
                pie.operator("class.pieparticleedit", text="Particle Edit", icon='PARTICLEMODE')
            else:
                pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'CURVE':
            pie = layout.menu_pie()
            # 4 - LEFT
            pie.separator()
            # 6 - RIGHT
            pie.separator()
            # 2 - BOTTOM
            pie.separator()
            # 8 - TOP
            pie.operator("object.editmode_toggle", text="Edit/Object", icon='OBJECT_DATAMODE')
            # 7 - TOP - LEFT
            pie.separator()
            # 9 - TOP - RIGHT
            pie.separator()
            # 1 - BOTTOM - LEFT
            pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'ARMATURE':
            pie = layout.menu_pie()
            # 4 - LEFT
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Object", icon="OBJECT_DATAMODE").mode = "OBJECT"
            # 6 - RIGHT
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Pose", icon="POSE_HLT").mode = "POSE"
            # 2 - BOTTOM
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Edit", icon="EDITMODE_HLT").mode = "EDIT"
            # 8 - TOP
            pie.operator("object.editmode_toggle", text="Edit Mode", icon='OBJECT_DATAMODE')
            # 7 - TOP - LEFT
            pie.separator()
            # 9 - TOP - RIGHT
            pie.separator()
            # 1 - BOTTOM - LEFT
            pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'FONT':
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            pie.separator()
            pie.separator()
            pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'SURFACE':
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            pie.separator()
            pie.separator()
            pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'META':
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            pie.separator()
            pie.separator()
            pie.separator()
            # 3 - BOTTOM - RIGHT
            if context.gpencil_data:
                pie.operator("view3d.pie_interactive_mode_grease_pencil", icon="GREASEPENCIL")

        elif ob and ob.type == 'LATTICE':
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            pie.operator("object.editmode_toggle", text="Edit/Object Toggle", icon='OBJECT_DATAMODE')
            pie.separator()
            pie.separator()
            pie.separator()

        if ob and ob.type == 'GPENCIL':
            pie = layout.menu_pie()
            # 4 - LEFT
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Sculpt", icon="SCULPTMODE_HLT").mode = "SCULPT_GPENCIL"
            # 6 - RIGHT
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Draw", icon="GREASEPENCIL").mode = "PAINT_GPENCIL"
            # 2 - BOTTOM
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Edit", icon="EDITMODE_HLT").mode = "EDIT_GPENCIL"
            # 8 - TOP
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Object", icon="OBJECT_DATAMODE").mode = "OBJECT"
            # 7 - TOP - LEFT
            pie.separator()
            # 9 - TOP - RIGHT
            pie.separator()
            # 1 - BOTTOM - LEFT
            pie.separator()
            # 3 - BOTTOM - RIGHT
            pie.operator(PIE_OT_SetObjectModePie.bl_idname, text="Weight Paint", icon="WPAINT_HLT").mode = "WEIGHT_GPENCIL"



        elif ob and ob.type in {"LIGHT", "CAMERA", "EMPTY", "SPEAKER"}:
            message = "Active Object has only Object Mode available" 
            pie = layout.menu_pie()
            pie.separator()
            pie.separator()
            pie.separator()
            box = pie.box()
            box.label(text=message, icon="INFO")


classes = (
    PIE_MT_ObjectEditMode,
    PIE_OT_ClassObject,
    PIE_OT_ClassVertex,
    PIE_OT_ClassEdge,
    PIE_OT_ClassFace,
    PIE_MT_ObjectEditotherModes,
    PIE_OT_ClassTexturePaint,
    PIE_OT_ClassWeightPaint,
    PIE_OT_ClassVertexPaint,
    PIE_OT_ClassParticleEdit,
    PIE_OT_InteractiveModeGreasePencil,
    PIE_OT_VertsEdges,
    PIE_OT_EdgesFaces,
    PIE_OT_VertsFaces,
    PIE_OT_VertsEdgesFaces,
    PIE_OT_SetObjectModePie,
    )

addon_keymaps = []


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

    wm = bpy.context.window_manager
    if wm.keyconfigs.addon:
        # Select Mode
        km = wm.keyconfigs.addon.keymaps.new(name='Object Non-modal')
        kmi = km.keymap_items.new('wm.call_menu_pie', 'TAB', 'PRESS')
        kmi.properties.name = "PIE_MT_objecteditmode"
        addon_keymaps.append((km, kmi))

        km = wm.keyconfigs.addon.keymaps.new(name='Grease Pencil Stroke Edit Mode')
        kmi = km.keymap_items.new('wm.call_menu_pie', 'TAB', 'PRESS')
        kmi.properties.name = "PIE_MT_objecteditmode"
        addon_keymaps.append((km, kmi))


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)

    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    if kc:
        for km, kmi in addon_keymaps:
            km.keymap_items.remove(kmi)
    addon_keymaps.clear()


if __name__ == "__main__":
    register()
