# ##### 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,  Hidesato Ikeya, zmj100, Gert De Roost, TrumanBlending, PKHG, #
# Oscurart, Greg, Stanislav Blinov, komi3D, BlenderLab, Paul Marshall (brikbot), #
# metalliandy, macouno, CoDEmanX, dustractor, Liero, lijenstina, Germano Cavalcante #
# Pistiwique, Jimmy Hazevoet #

bl_info = {
    "name": "Edit Tools 2",
    "author": "meta-androcto",
    "version": (0, 3, 4),
    "blender": (2, 78, 0),
    "location": "View3D > Toolshelf > Tools and Specials (W-key)",
    "description": "Extra mesh edit tools - modifying meshes and selection",
    "warning": "",
    "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/"
                "Py/Scripts/Modeling/Extra_Tools",
    "category": "Mesh"}


# Import From Files
if "bpy" in locals():
    import importlib
    importlib.reload(face_inset_fillet)
    importlib.reload(mesh_filletplus)
    importlib.reload(mesh_vertex_chamfer)
    importlib.reload(mesh_mextrude_plus)
    importlib.reload(mesh_offset_edges)
    importlib.reload(pkhg_faces)
    importlib.reload(mesh_edge_roundifier)
    importlib.reload(mesh_cut_faces)
    importlib.reload(split_solidify)
    importlib.reload(mesh_edges_floor_plan)
    importlib.reload(mesh_edges_length)
    importlib.reload(random_vertices)
    importlib.reload(mesh_fastloop)
    importlib.reload(mesh_edgetools)
    importlib.reload(mesh_pen_tool)
    importlib.reload(vfe_context_menu)
    importlib.reload(mesh_help)
    importlib.reload(mesh_select_by_direction)
    importlib.reload(mesh_select_by_edge_length)
    importlib.reload(mesh_select_by_pi)
    importlib.reload(mesh_select_by_type)
    importlib.reload(mesh_select_connected_faces)
    importlib.reload(mesh_index_select)
    importlib.reload(mesh_selection_topokit)
    importlib.reload(mesh_info_select)
    importlib.reload(mesh_extrude_and_reshape)
    importlib.reload(mesh_check)
    importlib.reload(vertex_align)

else:
    from . import face_inset_fillet
    from . import mesh_filletplus
    from . import mesh_vertex_chamfer
    from . import mesh_mextrude_plus
    from . import mesh_offset_edges
    from . import pkhg_faces
    from . import mesh_edge_roundifier
    from . import mesh_cut_faces
    from . import split_solidify
    from . import mesh_edges_floor_plan
    from . import mesh_edges_length
    from . import random_vertices
    from . import mesh_fastloop
    from . import mesh_edgetools
    from . import mesh_pen_tool
    from . import vfe_context_menu
    from . import mesh_help
    from . import mesh_extrude_and_reshape
    from . import mesh_check
    from . import vertex_align

    from .mesh_select_tools import mesh_select_by_direction
    from .mesh_select_tools import mesh_select_by_edge_length
    from .mesh_select_tools import mesh_select_by_pi
    from .mesh_select_tools import mesh_select_by_type
    from .mesh_select_tools import mesh_select_connected_faces
    from .mesh_select_tools import mesh_index_select
    from .mesh_select_tools import mesh_selection_topokit
    from .mesh_select_tools import mesh_info_select

    from . icons.icons import load_icons

import bpy
import bpy_extras.keyconfig_utils
from bpy.types import (
        Menu,
        Panel,
        PropertyGroup,
        AddonPreferences,
        )
from bpy.props import (
        BoolProperty,
        BoolVectorProperty,
        EnumProperty,
        FloatProperty,
        FloatVectorProperty,
        IntVectorProperty,
        PointerProperty,
        )


# ------ MENUS ------ #

# Define the "Extras" menu
class VIEW3D_MT_edit_mesh_extras(Menu):
    bl_idname = "VIEW3D_MT_edit_mesh_extras"
    bl_label = "Edit Tools"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'
        mode = context.tool_settings.mesh_select_mode

        if mode[0]:
            split = layout.split()
            col = split.column()

            col.label(text="Vertex", icon="VERTEXSEL")
            col.separator()

            col.operator("mesh.vertex_chamfer", text="Vertex Chamfer")
            col.operator("mesh.random_vertices", text="Random Vertices")

            col = split.column()
            col.label(text="Utilities", icon="SCRIPTWIN")
            col.separator()

            col.operator("object_ot.fastloop", text="Fast loop")
            col.operator("mesh.flip_normals", text="Normals Flip")
            col.operator("mesh.remove_doubles", text="Remove Doubles")
            col.operator("mesh.subdivide", text="Subdivide")
            col.operator("mesh.dissolve_limited", text="Dissolve Limited")

        elif mode[1]:
            split = layout.split()
            col = split.column()
            col.label(text="Edge", icon="EDGESEL")
            col.separator()

            col.operator("mesh.fillet_plus", text="Edge Fillet Plus")
            col.operator("mesh.offset_edges", text="Offset Edges")
            col.operator("mesh.edge_roundifier", text="Edge Roundify")
            col.operator("object.mesh_edge_length_set", text="Set Edge Length")
            col.operator("mesh.edges_floor_plan")

            col = split.column()
            col.label(text="Utilities", icon="SCRIPTWIN")
            col.separator()

            col.operator("object_ot.fastloop", text="Fast loop")
            col.operator("mesh.flip_normals", text="Normals Flip")
            col.operator("mesh.remove_doubles", text="Remove Doubles")

            col.operator("mesh.subdivide", text="Subdivide")
            col.operator("mesh.dissolve_limited", text="Dissolve Limited")

        elif mode[2]:
            split = layout.split()
            col = split.column()
            col.label(text="Face", icon="FACESEL")
            col.separator()

            col.operator("object.mextrude", text="Multi Extrude")
            col.operator("mesh.face_inset_fillet", text="Face Inset Fillet")
            col.operator("mesh.extrude_reshape", text="Push/Pull")
            col.operator("mesh.add_faces_to_object", text="PKHG Faces")
            col.operator("mesh.ext_cut_faces", text="Cut Faces")
            col.operator("mesh.split_solidify", text="Split Solidify")

            col = split.column()
            col.label(text="Utilities", icon="SCRIPTWIN")
            col.separator()

            col.operator("object_ot.fastloop", text="Fast loop")
            col.operator("mesh.flip_normals", text="Normals Flip")
            col.operator("mesh.remove_doubles", text="Remove Doubles")
            col.operator("mesh.subdivide", text="Subdivide")
            col.operator("mesh.dissolve_limited", text="Dissolve Limited")


class EditToolsPanel(Panel):
    bl_label = "Mesh Edit Tools"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "mesh_edit"
    bl_category = "Tools"
    bl_options = {"DEFAULT_CLOSED"}

    def draw(self, context):
        scene = context.scene
        VERTDROP = scene.mesh_extra_tools.UiTabDrop[0]
        EDGEDROP = scene.mesh_extra_tools.UiTabDrop[1]
        FACEDROP = scene.mesh_extra_tools.UiTabDrop[2]
        UTILSDROP = scene.mesh_extra_tools.UiTabDrop[3]
        # Change icons depending on the bool state (compliant with the rest of the UI)
        icon_active_0 = "TRIA_RIGHT" if not VERTDROP else "TRIA_DOWN"
        icon_active_1 = "TRIA_RIGHT" if not EDGEDROP else "TRIA_DOWN"
        icon_active_2 = "TRIA_RIGHT" if not FACEDROP else "TRIA_DOWN"
        icon_active_3 = "TRIA_RIGHT" if not UTILSDROP else "TRIA_DOWN"

        layout = self.layout

        # Vert options
        box1 = self.layout.box()
        col = box1.column(align=True)
        row = col.row(align=True)
        row.prop(scene.mesh_extra_tools, "UiTabDrop", text="Vertex", index=0, icon=icon_active_0)
        if not VERTDROP:
            row.menu("mesh.vert_select_tools", icon="RESTRICT_SELECT_OFF", text="")
            row.menu("VIEW3D_MT_Select_Vert", icon="VERTEXSEL", text="")
        else:
            layout = self.layout

            row = layout.row()
            row.label(text="Vertex Tools:", icon="VERTEXSEL")

            row = layout.split(0.8, align=True)
            row.operator("mesh.vertex_chamfer", text="Chamfer")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_vertex_chamfer"

            row = layout.split(0.8, align=True)
            row.operator("mesh.random_vertices", text="Random Vertices")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "random_vertices"

            # Vertex Align Properties And Menu
            cen0 = scene.mesh_extra_tools.vert_align_to

            layout = self.layout
            layout.label(text="Vertex Align:", icon="UV_VERTEXSEL")

            # Draw the menu with 2 options
            layout.prop(scene.mesh_extra_tools, "vert_align_to", expand=False)
            if cen0 == 'vertex':
                row = layout.row(align=True)
                row.operator("vertex_align.store_id", text="Store Selected Vertex")

                row = layout.split(0.8, align=True)
                row.operator("vertex_align.align_original", text="Align to Axis")
                props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
                props.help_ids = "vertex_align"
                props.popup_size = 400
            elif cen0 == "coordinates":
                layout.prop(scene.mesh_extra_tools, "vert_align_use_stored", toggle=True)

                if scene.mesh_extra_tools.vert_align_use_stored:
                    col = layout.column(align=True)
                    col.prop(scene.mesh_extra_tools, "vert_align_store_axis", expand=True)

                row = layout.split(0.8, align=True)
                row.operator("vertex_align.coord_list_id", text="Align Coordinates")
                row.operator("mesh.extra_tools_help",
                            icon="LAYER_USED").help_ids = "vertex_align"

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

        if not EDGEDROP:
            row.menu("mesh.edge_select_tools", icon="RESTRICT_SELECT_OFF", text="")
            row.menu("VIEW3D_MT_Select_Edge", icon="EDGESEL", text="")
        else:
            layout = self.layout

            row = layout.row()
            row.label(text="Edge Tools:", icon="EDGESEL")
            row.menu("VIEW3D_MT_edit_mesh_edgetools", icon="GRID")

            row = layout.split(0.8, align=True)
            row.operator("mesh.fillet_plus", text="Fillet plus")

            props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
            props.help_ids = "mesh_filletplus"
            props.popup_size = 400

            row = layout.split(0.8, align=True)
            row.operator("mesh.offset_edges", text="Offset Edges")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_offset_edges"

            row = layout.split(0.8, align=True)
            row.operator("mesh.edge_roundifier", text="Roundify")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_edge_roundifier"

            row = layout.split(0.8, align=True)
            row.operator("object.mesh_edge_length_set", text="Set Edge Length")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_edges_length"

            row = layout.split(0.8, align=True)
            row.operator("mesh.edges_floor_plan")

            props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
            props.help_ids = "mesh_edges_floor_plan"
            props.popup_size = 400

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

        if not FACEDROP:
            row.menu("mesh.face_select_tools", icon="RESTRICT_SELECT_OFF", text="")
            row.menu("VIEW3D_MT_Select_Face", icon="FACESEL", text="")
        else:
            layout = self.layout

            row = layout.row()
            row.label(text="Face Tools:", icon="FACESEL")

            row = layout.split(0.8, align=True)
            row.operator("object.mextrude", text="Multi Extrude")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_mextrude_plus"

            row = layout.split(0.8, align=True)
            row.operator("mesh.extrude_reshape", text="Push/Pull")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_extrude_and_reshape"

            row = layout.split(0.8, align=True)
            row.operator("mesh.face_inset_fillet", text="Inset Fillet")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "face_inset_fillet"

            row = layout.split(0.8, align=True)
            row.operator("mesh.ext_cut_faces", text="Cut Faces")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "mesh_cut_faces"

            row = layout.split(0.8, align=True)
            row.operator("mesh.split_solidify", text="Split Solidify")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "split_solidify"

            row = layout.split(0.8, align=True)
            row.operator("mesh.add_faces_to_object", "Shape Extrude")
            row.operator("mesh.extra_tools_help",
                        icon="LAYER_USED").help_ids = "pkhg_faces"

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

        if not UTILSDROP:
            row.menu("mesh.utils specials", icon="SOLO_OFF", text="")
            row.menu("VIEW3D_MT_Edit_MultiMET", icon="LOOPSEL", text="")
        else:
            layout = self.layout

            row = layout.row()
            row.label(text="Utilities:")

            row = layout.row()
            row = layout.split(0.8, align=True)
            row.operator("object_ot.fastloop", text="Fast Loop")

            props = row.operator("mesh.extra_tools_help", icon="LAYER_USED")
            props.help_ids = "mesh_fastloop"
            props.popup_size = 400

            col = layout.column(align=True)
            col.operator("mesh.flip_normals", text="Normals Flip")
            col.operator("mesh.remove_doubles", text="Remove Doubles")
            col.operator("mesh.subdivide", text="Subdivide")
            col.operator("mesh.dissolve_limited", text="Dissolve Limited")

            row = layout.row(align=True)
            row.operator("mesh.select_vert_edge_face_index",
                          icon="VERTEXSEL", text="Select By Index").select_type = 'VERT'

            # Mesh Check
            layout = self.layout
            icons = load_icons()
            tris = icons.get("triangles")
            ngons = icons.get("ngons")

            mesh_check = context.window_manager.mesh_check
            icon_active_4 = "TRIA_RIGHT" if not mesh_check.mesh_check_use else "TRIA_DOWN"

            row = layout.row()
            row = layout.split(0.8, align=True)
            row.prop(mesh_check, "mesh_check_use", toggle=True, icon=icon_active_4)
            row.operator("mesh.extra_tools_help", icon="LAYER_USED").help_ids = "mesh_check"

            if mesh_check.mesh_check_use:
                layout = self.layout

                row = layout.row(align=True)
                row.operator("object.face_type_select", text="Tris",
                             icon_value=tris.icon_id).face_type = 'tris'
                row.operator("object.face_type_select", text="Ngons",
                             icon_value=ngons.icon_id).face_type = 'ngons'

                row = layout.row()
                row.prop(mesh_check, "display_faces", text="Display Faces")

                if mesh_check.display_faces:
                    col = layout.column(align=True)
                    col.prop(mesh_check, "edge_width")
                    col.prop(mesh_check, "face_opacity")

                    row = layout.row()
                    row.label(text="Custom Colors:", icon="COLOR")

                    col = layout.column().split(percentage=0.1, align=True)
                    col.label(text="", icon_value=tris.icon_id)
                    col.prop(mesh_check, "custom_tri_color", text="")

                    col = layout.column().split(percentage=0.1, align=True)
                    col.label(text="", icon_value=ngons.icon_id)
                    col.prop(mesh_check, "custom_ngons_color", text="")

                    layout.separator()

                    row = layout.row(align=True)
                    if bpy.app.debug:
                        obj_data = getattr(context.active_object, "data", None)
                        if obj_data:
                            row.prop(obj_data, "show_extra_indices",
                                     icon="LINENUMBERS_ON", toggle=True)

                    if context.mode == 'EDIT_MESH' and not context.space_data.use_occlude_geometry:
                        row.prop(mesh_check, "finer_lines_behind_use", icon="ORTHO")


# ********** Edit Multiselect **********
class VIEW3D_MT_Edit_MultiMET(Menu):
    bl_label = "Multi Select"
    bl_description = "Multi Select Modes"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        prop = layout.operator("wm.context_set_value",
                               text="Vertex Select",
                               icon='VERTEXSEL')
        prop.value = "(True, False, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Edge Select",
                               icon='EDGESEL')
        prop.value = "(False, True, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Face Select",
                               icon='FACESEL')
        prop.value = "(False, False, True)"
        prop.data_path = "tool_settings.mesh_select_mode"

        layout.separator()

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Edge Select",
                               icon='EDITMODE_HLT')
        prop.value = "(True, True, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Face Select",
                               icon='ORTHO')
        prop.value = "(True, False, True)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Edge and Face Select",
                               icon='SNAP_FACE')
        prop.value = "(False, True, True)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex, Edge and Face Select",
                               icon='SNAP_VOLUME')
        prop.value = "(True, True, True)"
        prop.data_path = "tool_settings.mesh_select_mode"


# Select Tools
class VIEW3D_MT_Select_Vert(Menu):
    bl_label = "Select Vert"
    bl_description = "Vertex Selection Modes"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        prop = layout.operator("wm.context_set_value",
                               text="Vertex Select",
                               icon='VERTEXSEL')
        prop.value = "(True, False, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Edge Select",
                               icon='EDITMODE_HLT')
        prop.value = "(True, True, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Face Select",
                               icon='ORTHO')
        prop.value = "(True, False, True)"
        prop.data_path = "tool_settings.mesh_select_mode"


class VIEW3D_MT_Select_Edge(Menu):
    bl_label = "Select Edge"
    bl_description = "Edge Selection Modes"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        prop = layout.operator("wm.context_set_value",
                               text="Edge Select",
                               icon='EDGESEL')
        prop.value = "(False, True, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Edge Select",
                               icon='EDITMODE_HLT')
        prop.value = "(True, True, False)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Edge and Face Select",
                               icon='SNAP_FACE')
        prop.value = "(False, True, True)"
        prop.data_path = "tool_settings.mesh_select_mode"


class VIEW3D_MT_Select_Face(Menu):
    bl_label = "Select Face"
    bl_description = "Face Selection Modes"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        prop = layout.operator("wm.context_set_value",
                               text="Face Select",
                               icon='FACESEL')
        prop.value = "(False, False, True)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Vertex and Face Select",
                               icon='ORTHO')
        prop.value = "(True, False, True)"
        prop.data_path = "tool_settings.mesh_select_mode"

        prop = layout.operator("wm.context_set_value",
                               text="Edge and Face Select",
                               icon='SNAP_FACE')
        prop.value = "(False, True, True)"
        prop.data_path = "tool_settings.mesh_select_mode"


class VIEW3D_MT_selectface_edit_mesh_add(Menu):
    bl_label = "Select by Face"
    bl_idname = "mesh.face_select_tools"
    bl_description = "Face Selection Tools"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        layout.label(text="Face Selection Tools", icon="RESTRICT_SELECT_OFF")
        layout.separator()

        layout.operator("mesh.select_all").action = 'TOGGLE'
        layout.operator("mesh.select_all", text="Inverse").action = 'INVERT'
        layout.operator("mesh.ext_deselect_boundary", text="Deselect Boundary")
        layout.separator()

        layout.operator("data.facetype_select", text="Triangles").face_type = "3"
        layout.operator("data.facetype_select", text="Quads").face_type = "4"
        layout.operator("data.facetype_select", text="Ngons").face_type = "5"
        layout.separator()

        layout.operator("mesh.select_vert_edge_face_index",
                        text="By Face Index").select_type = 'FACE'
        layout.operator("mesh.select_by_direction", text="By Direction")
        layout.operator("mesh.select_by_pi", text="By Pi or e")
        layout.operator("mesh.select_connected_faces", text="By Connected Faces")
        layout.operator("mesh.conway", text="By Conway's game of life")
        layout.separator()

        layout.operator("mesh.e2e_efe", text="Neighbors by Face")
        layout.operator("mesh.f2f_fvnef", text="Neighbors by Vert not Edge")


class VIEW3D_MT_selectedge_edit_mesh_add(Menu):
    bl_label = "Select by Edge"
    bl_idname = "mesh.edge_select_tools"
    bl_description = "Edge Selection Tools"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        layout.label(text="Edge Selection Tools", icon="RESTRICT_SELECT_OFF")
        layout.separator()

        layout.operator("mesh.select_all").action = 'TOGGLE'
        layout.operator("mesh.select_all", text="Inverse").action = 'INVERT'
        layout.separator()

        layout.operator("mesh.select_vert_edge_face_index",
                        text="By Edge Index").select_type = 'EDGE'
        layout.operator("mesh.select_by_direction", text="By Direction")
        layout.operator("mesh.select_by_pi", text="By Pi or e")
        layout.operator("mesh.select_by_edge_length", text="By Edge Length")
        layout.separator()

        layout.operator("mesh.e2e_eve", text="Neighbors by Vertex")
        layout.operator("mesh.e2e_evfe", text="Neighbors by Vertex and Face")
        layout.operator("mesh.e2e_efnve", text="Lateral Neighbors")
        layout.operator("mesh.e2e_evnfe", text="Longitudinal Edges")


class VIEW3D_MT_selectvert_edit_mesh_add(Menu):
    bl_label = "Select by Vert"
    bl_idname = "mesh.vert_select_tools"
    bl_description = "Vertex Selection Tools"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        layout.label(text="Vertex Selection Tools", icon="RESTRICT_SELECT_OFF")
        layout.separator()

        layout.operator("mesh.select_all").action = 'TOGGLE'
        layout.operator("mesh.select_all", text="Inverse").action = 'INVERT'
        layout.separator()

        layout.operator("mesh.select_vert_edge_face_index",
                        text="By Vert Index").select_type = 'VERT'
        layout.operator("mesh.select_by_direction", text="By Direction")
        layout.operator("mesh.select_by_pi", text="By Pi or e")
        layout.separator()

        layout.operator("mesh.v2v_by_edge", text="Neighbors by Edge")
        layout.operator("mesh.e2e_eve", text="Neighbors by Vertex")
        layout.operator("mesh.e2e_efe", text="Neighbors by Face")
        layout.operator("mesh.v2v_facewise", text="Neighbors by Face - Edge")


class VIEW3D_MT_utils_context_menu(Menu):
    bl_label = "Specials Menu"
    bl_idname = "mesh.utils specials"
    bl_description = "Utils Quick Specials"

    def draw(self, context):
        layout = self.layout
        layout.operator_context = 'INVOKE_REGION_WIN'

        layout.label(text="Fast Specials")
        layout.separator()

        layout.menu("VIEW3D_MT_edit_mesh_clean")
        layout.separator()

        layout.operator("mesh.subdivide", text="Subdivide").smoothness = 0.0
        layout.operator("mesh.merge", text="Merge...")
        layout.operator("mesh.remove_doubles")
        layout.operator("mesh.inset")
        layout.operator("mesh.bevel", text="Bevel")
        layout.operator("mesh.bridge_edge_loops")
        layout.separator()

        layout.operator("mesh.normals_make_consistent",
                        text="Recalculate Outside").inside = False
        layout.operator("mesh.normals_make_consistent",
                        text="Recalculate Inside").inside = True
        layout.operator("mesh.flip_normals")


# Define the "Extras" Menu append
class VIEW3D_MT_edit_mesh_all(Menu):
    bl_idname = "VIEW3D_MT_edit_mesh_all"
    bl_label = "Mesh Edit Tools"

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

        layout.menu("VIEW3D_MT_edit_mesh_extras")
        layout.menu("VIEW3D_MT_edit_mesh_edgetools")


def menu_func(self, context):
    self.layout.menu("VIEW3D_MT_edit_mesh_extras")
    self.layout.menu("VIEW3D_MT_edit_mesh_edgetools")


# Define "Select" Menu append
def menu_select(self, context):
    if context.tool_settings.mesh_select_mode[2]:
        self.layout.menu("mesh.face_select_tools", icon="FACESEL")
    if context.tool_settings.mesh_select_mode[1]:
        self.layout.menu("mesh.edge_select_tools", icon="EDGESEL")
    if context.tool_settings.mesh_select_mode[0]:
        self.layout.menu("mesh.vert_select_tools", icon="VERTEXSEL")


# Scene Properties
class MeshExtraToolsSceneProps(PropertyGroup):
    # Define the UI drop down prop
    UiTabDrop = BoolVectorProperty(
            name="Tab",
            description="Expand/Collapse UI elements",
            default=(False,) * 4,
            size=4,
            )
    # Vertex align
    vert_align_store_axis: FloatVectorProperty(
            name="Define Custom Coordinates",
            description="Store the values of coordinates, for repeated use\n"
                        "as a starting point",
            default=(0.0, 0.0, 0.0),
            min=-100.0, max=100.0,
            step=1, size=3,
            subtype='XYZ',
            precision=3
            )
    vert_align_use_stored: BoolProperty(
            name="Use Stored Coordinates",
            description="Use starting point coordinates for alignment",
            default=False
            )
    vert_align_to: EnumProperty(
            items=(('vertex', "Original vertex",
                    "Use the stored vertex coordinates for aligning"),
                   ('coordinates', "Custom coordinates",
                    "Use defined custom coordinates for aligning")),
            name="Align to",
            default='vertex'
            )
    vert_align_axis = BoolVectorProperty(
            name="Axis",
            description="Align to a specific Axis",
            default=(True, False, False),
            size=3,
            )
    # Mesh Info select
    mesh_info_show: BoolProperty(
            name="Show Face Info",
            description="Display the Object's Face Count information\n"
                        "Note: it can have some performance impact on dense meshes\n"
                        "Leave it closed if not needed or set the Delay to a higher value",
            default=False
            )
    mesh_info_delay: FloatProperty(
            name="Delay",
            description="Set the Update time Delay in seconds\n"
                        "Set to zero to update with the UI refresh\n"
                        "Higher values will sometimes need to hover over the cursor",
            default=2.0,
            min=0.0, max=20.0,
            step=100,
            subtype='TIME',
            precision=1
            )


# Add-on Preferences
class mesh_extra_tools_pref(AddonPreferences):
    bl_idname = __name__

    show_info: BoolProperty(
            name="Info",
            default=False,
            description="Some general information about the add-on",
            )
    show_shortcuts: BoolProperty(
            name="Hot Keys",
            default=False,
            description="List of the shortcuts used for the included various tools",
            )

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

        box.prop(self, "show_info", icon="INFO")
        if self.show_info:
            box.label(text="Collection of various extra Mesh Edit Functions",
                      icon="LAYER_ACTIVE")
            box.label("The majority of the tools can be found in"
                      "Mesh Edit Mode Toolshelf or W key Specials Menu",
                      icon="LAYER_USED")
            box.label("The Pen tool is a separate Panel in the Toolshelf",
                      icon="LAYER_USED")
            box.label("The Face Extrude tool is only available in Object Mode "
                      "as a separate panel in the Toolshelf",
                      icon="LAYER_USED")
            box.label("Face Info / Select is a separate Panel located in Properties > Data Editor",
                      icon="LAYER_USED")

        box.prop(self, "show_shortcuts", icon="KEYINGSET")
        if self.show_shortcuts:
            col = box.column()
            col.label(text="Double Right Click in Edit mode in the 3D Viewport",
                      icon="LAYER_ACTIVE")
            col.label("Used for quick access to the Vertex, Edge and Face context menus",
                      icon="LAYER_USED")
            col.separator()
            col.label(text="W-key in Edit Mode in the 3D Viewport",
                      icon="LAYER_ACTIVE")
            col.label("Tools are grouped into menus prepended to the Specials Menu",
                      icon="LAYER_USED")
            col.separator()
            col.label(text="Ctrl+D in Edit Mode in the 3D Viewport",
                      icon="LAYER_ACTIVE")
            col.label("Used by the Pen Tool to start drawing. When activated:",
                      icon="LAYER_USED")
            col.label("Shift + Mouse Move is used to draw along the X axis",
                      icon="LAYER_USED")
            col.label("Alt + Mouse Move is used to draw along the Y axis",
                      icon="LAYER_USED")
            col.separator()
            col.label(text="Note: when using Fast Loop operator, press Esc twice to finish",
                      icon="LAYER_ACTIVE")


def register():
    mesh_pen_tool.register()
    vfe_context_menu.register()
    mesh_extrude_and_reshape.register()
    mesh_check.register()

    bpy.utils.register_module(__name__)

    # Register Scene Properties
    bpy.types.Scene.mesh_extra_tools = PointerProperty(
                                            type=MeshExtraToolsSceneProps
                                            )
    # Used in mesh_selection_topokit to store cache selection data
    bpy.types.Object.tkkey = IntVectorProperty(size=4)

    # Add "Extras" menu to the "W-key Specials" menu
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.prepend(menu_func)
    bpy.types.VIEW3D_MT_select_edit_mesh.prepend(menu_select)

    try:
        bpy.types.VIEW3D_MT_Select_Edit_Mesh.prepend(menu_select)
    except:
        pass


def unregister():
    mesh_pen_tool.unregister()
    vfe_context_menu.unregister()
    mesh_extrude_and_reshape.unregister()
    mesh_check.unregister()

    del bpy.types.Scene.mesh_extra_tools
    del bpy.types.Object.tkkey

    bpy.utils.unregister_module(__name__)

    # Remove "Extras" menu from the "" menu.
    bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(menu_func)
    bpy.types.VIEW3D_MT_select_edit_mesh.remove(menu_select)

    try:
        bpy.types.VIEW3D_MT_Select_Edit_Mesh.remove(menu_select)
    except:
        pass


if __name__ == "__main__":
    register()
