import bpy
from bpy.props import EnumProperty

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode
from sverchok.data_structure import match_long_repeat as mlr
from sverchok.dependencies import FreeCAD

if FreeCAD is not None:
    import Part
    from FreeCAD import Base


def get_shape(mode, base_object):
    if mode == 'Solid':
        if isinstance(base_object, Part.Solid):
            shape = base_object.OuterShell
        else:
            shape = base_object
    elif mode == 'Face':
        shape = base_object.face
    else:
        shape = base_object.edge
    return shape


class SvSolidDistanceNode(SverchCustomTreeNode, bpy.types.Node):
    """
    Triggers: Closest point on Solids
    Tooltip: Distance between solids. Closest point on a solid surface, accepts also solid faces and solid edges
    """
    bl_idname = 'SvSolidDistanceNode'
    bl_label = 'Solid Distance'
    bl_icon = 'OUTLINER_OB_EMPTY'
    sv_icon = 'SV_SOLID_DISTANCE'
    sv_category = "Solid Operators"
    sv_dependencies = {'FreeCAD'}

    modes = [
        ('Solid', 'Solid', '', 0),
        ('Face', 'Face', '', 1),
        ('Edge', 'Edge', '', 2),
        ('Vertex', 'Vertex', '', 3),

    ]
    def set_sockets(self, context):
        self.update_sockets()
        updateNode(self, context)

    def update_sockets(self):
        self.inputs['Solid A'].hide_safe = self.mode != 'Solid'
        self.inputs['Solid Face A'].hide_safe = self.mode != 'Face'
        self.inputs['Solid Edge A'].hide_safe = self.mode != 'Edge'
        self.inputs['Solid B'].hide_safe = self.mode_b != 'Solid'
        self.inputs['Solid Face B'].hide_safe = self.mode_b != 'Face'
        self.inputs['Solid Edge B'].hide_safe = self.mode_b != 'Edge'
        self.inputs['Vertices'].hide_safe = self.mode_b != 'Vertex'
        self.outputs['Closest Point B'].hide_safe = self.mode_b == 'Vertex'
        self.outputs['Info B'].hide_safe = self.mode_b == 'Vertex'

    mode: EnumProperty(
        name="Mode",
        description="Algorithm used for conversion",
        items=modes[:3], default="Solid",
        update=set_sockets)

    mode_b: EnumProperty(
        name="Mode",
        description="Algorithm used for conversion",
        items=modes, default="Solid",
        update=set_sockets)

    def sv_init(self, context):
        self.inputs.new('SvSolidSocket', "Solid A")
        self.inputs.new('SvSurfaceSocket', "Solid Face A")
        self.inputs.new('SvCurveSocket', "Solid Edge A")
        self.inputs.new('SvSolidSocket', "Solid B")
        self.inputs.new('SvSurfaceSocket', "Solid Face B")
        self.inputs.new('SvCurveSocket', "Solid Edge B")
        self.inputs.new('SvVerticesSocket', "Vertices")
        self.outputs.new('SvStringsSocket', "Distance")
        self.outputs.new('SvVerticesSocket', "Closest Point A")
        self.outputs.new('SvStringsSocket', "Info A")
        self.outputs.new('SvVerticesSocket', "Closest Point B")
        self.outputs.new('SvStringsSocket', "Info B")
        self.mode = "Solid"
        self.mode_b = "Solid"
        self.update_sockets()

    def draw_buttons(self, context, layout):
        row = layout.row(align=True)
        col = row.column(align=True)
        col.label(text='From')
        col.prop(self, "mode", text="")
        col = row.column(align=True)
        col.label(text='To')
        col.prop(self, "mode_b", text="")

    def process(self):
        if not any(socket.is_linked for socket in self.outputs):
            return

        objects_a = self.inputs[self["mode"]].sv_get()
        objects_b = self.inputs[self["mode_b"] + 3].sv_get()

        distances_out = []
        closest_points_out_a = []
        infos_out_a = []
        closest_points_out_b = []
        infos_out_b = []

        for object_a, object_b, in zip(*mlr([objects_a, objects_b])):
            distances = []
            closest_points_a = []
            infos_a = []
            closest_points_b = []
            infos_b = []
            shape = get_shape(self.mode, object_a)

            if self.mode_b == 'Vertex':
                for v in object_b:
                    vertex = Part.Vertex(Base.Vector(v))

                    dist = shape.distToShape(vertex)
                    distances.append(dist[0])
                    closest_points_a.append(dist[1][0][0][:])
                    infos_a.append(dist[2][0][0:3])
            else:
                shape_b = get_shape(self.mode_b, object_b)

                dist = shape.distToShape(shape_b)
                distances.append(dist[0])
                closest_points_a.append(dist[1][0][0][:])
                infos_a.append(dist[2][0][0:3])
                closest_points_b.append(dist[1][0][1][:])
                infos_b.append(dist[2][0][3:])

                closest_points_out_b.append(closest_points_b)
                infos_out_b.append(infos_b)

            distances_out.append(distances)
            closest_points_out_a.append(closest_points_a)
            infos_out_a.append(infos_a)

        self.outputs['Distance'].sv_set(distances_out)
        self.outputs['Closest Point A'].sv_set(closest_points_out_a)
        self.outputs['Info A'].sv_set(infos_out_a)
        self.outputs['Closest Point B'].sv_set(closest_points_out_b)
        self.outputs['Info B'].sv_set(infos_out_b)


def register():
    bpy.utils.register_class(SvSolidDistanceNode)


def unregister():
    bpy.utils.unregister_class(SvSolidDistanceNode)
