# -*- coding: utf-8 -*-
# ***************************************************************************
# *   Copyright (c) 2015 Dan Falck <ddfalck@gmail.com>                      *
# *   Copyright (c) 2021 Schildkroet                                        *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   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 Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

"""Selection gates and observers to control selectability while building Path operations """

import FreeCAD
import FreeCADGui
import PathScripts.PathLog as PathLog
import PathScripts.PathPreferences as PathPreferences
import PathScripts.drillableLib as drillableLib
import math

PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
# PathLog.trackModule(PathLog.thisModule())


class PathBaseGate(object):
    pass


class EGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        return sub and sub[0:4] == "Edge"


class MESHGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        return obj.TypeId[0:4] == "Mesh"


class VCARVEGate:
    def allow(self, doc, obj, sub):
        try:
            shape = obj.Shape
        except Exception:
            return False

        if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
            return True

        if shape.ShapeType == "Face":
            return True

        elif shape.ShapeType == "Solid":
            if sub and sub[0:4] == "Face":
                return True

        elif shape.ShapeType == "Compound":
            if sub and sub[0:4] == "Face":
                return True

        if sub:
            subShape = shape.getElement(sub)
            if subShape.ShapeType == "Edge":
                return False

        return False


class ENGRAVEGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        try:
            shape = obj.Shape
        except Exception:
            return False

        if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
            return True

        if shape.ShapeType == "Edge":
            return True

        if sub:
            subShape = shape.getElement(sub)
            if subShape.ShapeType == "Edge":
                return True

        return False


class CHAMFERGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        try:
            shape = obj.Shape
        except Exception:
            return False

        if math.fabs(shape.Volume) < 1e-9 and len(shape.Wires) > 0:
            return True

        if "Edge" == shape.ShapeType or "Face" == shape.ShapeType:
            return True

        if sub:
            subShape = shape.getElement(sub)
            if subShape.ShapeType == "Edge":
                return True
            elif subShape.ShapeType == "Face":
                return True

        return False


class DRILLGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        PathLog.debug("obj: {} sub: {}".format(obj, sub))
        if not hasattr(obj, "Shape"):
            return False
        shape = obj.Shape
        subobj = shape.getElement(sub)
        if subobj.ShapeType not in ["Edge", "Face"]:
            return False
        return drillableLib.isDrillable(shape, subobj, vector=None)


class FACEGate(
    PathBaseGate
):  # formerly PROFILEGate class using allow_ORIG method as allow()
    def allow(self, doc, obj, sub):
        profileable = False

        try:
            obj = obj.Shape
        except Exception:
            return False

        if obj.ShapeType == "Compound":
            if sub and sub[0:4] == "Face":
                profileable = True

        elif obj.ShapeType == "Face":  # 3D Face, not flat, planar?
            profileable = True  # Was False

        elif obj.ShapeType == "Solid":
            if sub and sub[0:4] == "Face":
                profileable = True

        return profileable

    def allow_ORIG(self, doc, obj, sub):

        profileable = False
        try:
            obj = obj.Shape
        except Exception:
            return False

        if obj.ShapeType == "Edge":
            profileable = False

        elif obj.ShapeType == "Compound":
            if sub and sub[0:4] == "Face":
                profileable = True

            if sub and sub[0:4] == "Edge":
                profileable = False

        elif obj.ShapeType == "Face":
            profileable = False

        elif obj.ShapeType == "Solid":
            if sub and sub[0:4] == "Face":
                profileable = True

            if sub and sub[0:4] == "Edge":
                profileable = False

        elif obj.ShapeType == "Wire":
            profileable = False

        return profileable


class PROFILEGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        if sub and sub[0:4] == "Edge":
            return True

        try:
            obj = obj.Shape
        except Exception:
            return False

        if obj.ShapeType == "Compound":
            if sub and sub[0:4] == "Face":
                return True

        elif obj.ShapeType == "Face":
            return True

        elif obj.ShapeType == "Solid":
            if sub and sub[0:4] == "Face":
                return True

        elif obj.ShapeType == "Wire":
            return True

        return False


class POCKETGate(PathBaseGate):
    def allow(self, doc, obj, sub):

        pocketable = False
        try:
            obj = obj.Shape
        except Exception:
            return False

        if obj.ShapeType == "Edge":
            pocketable = False

        elif obj.ShapeType == "Face":
            pocketable = True

        elif obj.ShapeType == "Solid":
            if sub and sub[0:4] == "Face":
                pocketable = True

        elif obj.ShapeType == "Compound":
            if sub and sub[0:4] == "Face":
                pocketable = True

        return pocketable


class ADAPTIVEGate(PathBaseGate):
    def allow(self, doc, obj, sub):

        adaptive = True
        try:
            obj = obj.Shape
        except Exception:
            return False

        return adaptive


class CONTOURGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        pass


class PROBEGate:
    def allow(self, doc, obj, sub):
        pass


class TURNGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        PathLog.debug("obj: {} sub: {}".format(obj, sub))
        if hasattr(obj, "Shape") and sub:
            shape = obj.Shape
            subobj = shape.getElement(sub)
            return drillableLib.isDrillable(shape, subobj, vector=None)
        else:
            return False


class ALLGate(PathBaseGate):
    def allow(self, doc, obj, sub):
        if sub and sub[0:6] == "Vertex":
            return True
        if sub and sub[0:4] == "Edge":
            return True
        if sub and sub[0:4] == "Face":
            return True
        return False


def contourselect():
    FreeCADGui.Selection.addSelectionGate(CONTOURGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Contour Select Mode\n")


def eselect():
    FreeCADGui.Selection.addSelectionGate(EGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Edge Select Mode\n")


def drillselect():
    FreeCADGui.Selection.addSelectionGate(DRILLGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Drilling Select Mode\n")


def engraveselect():
    FreeCADGui.Selection.addSelectionGate(ENGRAVEGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Engraving Select Mode\n")


def fselect():
    FreeCADGui.Selection.addSelectionGate(FACEGate())  # Was PROFILEGate()
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Profiling Select Mode\n")


def chamferselect():
    FreeCADGui.Selection.addSelectionGate(CHAMFERGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Deburr Select Mode\n")


def profileselect():
    FreeCADGui.Selection.addSelectionGate(PROFILEGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Profiling Select Mode\n")


def pocketselect():
    FreeCADGui.Selection.addSelectionGate(POCKETGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Pocketing Select Mode\n")


def adaptiveselect():
    FreeCADGui.Selection.addSelectionGate(ADAPTIVEGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Adaptive Select Mode\n")


def slotselect():
    FreeCADGui.Selection.addSelectionGate(ALLGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Slot Cutter Select Mode\n")


def surfaceselect():
    gate = False
    if MESHGate() or FACEGate():
        gate = True
    FreeCADGui.Selection.addSelectionGate(gate)
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")


def vcarveselect():
    FreeCADGui.Selection.addSelectionGate(VCARVEGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Vcarve Select Mode\n")


def probeselect():
    FreeCADGui.Selection.addSelectionGate(PROBEGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Probe Select Mode\n")


def customselect():
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Custom Select Mode\n")


def turnselect():
    FreeCADGui.Selection.addSelectionGate(TURNGate())
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Turning Select Mode\n")


def select(op):
    opsel = {}
    opsel["Contour"] = contourselect  # deprecated
    opsel["Deburr"] = chamferselect
    opsel["Drilling"] = drillselect
    opsel["Engrave"] = engraveselect
    opsel["Helix"] = drillselect
    opsel["MillFace"] = pocketselect
    opsel["Pocket"] = pocketselect
    opsel["Pocket 3D"] = pocketselect
    opsel["Pocket3D"] = pocketselect  # deprecated
    opsel["Pocket Shape"] = pocketselect
    opsel["Profile Edges"] = eselect  # deprecated
    opsel["Profile Faces"] = fselect  # deprecated
    opsel["Profile"] = profileselect
    opsel["Slot"] = slotselect
    opsel["Surface"] = surfaceselect
    opsel["Waterline"] = surfaceselect
    opsel["Adaptive"] = adaptiveselect
    opsel["Vcarve"] = vcarveselect
    opsel["Probe"] = probeselect
    opsel["Custom"] = customselect
    opsel["ThreadMilling"] = drillselect
    opsel["TurnFace"] = turnselect
    opsel["TurnProfile"] = turnselect
    opsel["TurnPartoff"] = turnselect
    opsel["TurnRough"] = turnselect
    return opsel[op]


def clear():
    FreeCADGui.Selection.removeSelectionGate()
    if not PathPreferences.suppressSelectionModeWarning():
        FreeCAD.Console.PrintWarning("Free Select\n")
