import bpy
import bmesh
from mathutils import Vector, Matrix
from bpy_extras import mesh_utils
from .geometry_utils import bound_box_center
from .addon_utils import get_object_color



def border_over_shape(border,verts,loops):
    for loop in loops:
        for i,p in enumerate(loop):
            a = Vector(verts[loop[i-1]])
            b = Vector(verts[p])

            for j in range(0,4):
                c = border[j-1]
                d = border[j]
                if intersect_line_line_2d(a,b,c,d):
                    return True

    for point in verts:
        if point_inside_rect(point,border):
            return True

    for point in border:
        if point_over_shape(point,verts,loops):
            return True


def border_loop(vert, loop):
    border_edge =[e for e in vert.link_edges if e.is_rectary]

    if border_edge:
        for edge in border_edge:
            other_vert = edge.other_vert(vert)

            if not other_vert in loop:
                loop.append(other_vert)
                border_loop(other_vert, loop)

        return loop
    else:
        return [vert]

def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
    loops = loops or []
    vert_indices = vert_indices or [v.index for v in bm.verts]

    bm.verts.ensure_lookup_table()

    loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
    if len(loop) >1:
        loops.append(loop)

    for v in loop:
        vert_indices.remove(v.index)

    if len(vert_indices):
        contour_loops(bm, vert_indices[0], loops, vert_indices)

    return loops


def get_shape_data(ob, matrix=None, depsgraph=None):
    if not matrix:
        matrix = Matrix()
    if not depsgraph:
        depsgraph = bpy.context.evaluated_depsgraph_get()

    gamma = 1 / 2.2
    eval_ob = ob.evaluated_get(depsgraph)
    mesh = eval_ob.to_mesh()

    bm = bmesh.new()
    bm.from_mesh(mesh)


    bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
    bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)

    bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
    bmesh.ops.triangulate(bm, faces=bm.faces)

    edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
    bm.to_mesh(mesh)
    mesh.update()
    bm.clear()

    points = []
    polygons = []
    depths = []

    for vert in mesh.vertices:
        co = matrix @ (ob.matrix_world @ Vector(vert.co))
        points.append([round(co[0], 1), round(co[1], 1)])

        depths += [co[2]]
    
    if depths:
        depth = max(depths)
    else:
        print(f'{ob.name} has no vertices')
        depth = 0

    for face in mesh.polygons:
        polygons.append([v for v in face.vertices])

    color = get_object_color(ob)
    if color:
        color = [round(pow(c, gamma), 4) for c in color]
    else:
        color = [0.5, 0.5, 0.5]

    shape = {
        'source_name': ob.name,
        'tooltip': ob.rig_picker.name,
        'depth': depth, 
        'points': points, 
        'polygons': polygons,
        'edges': edges,
        'color': color,
        'type': ob.rig_picker.shape_type
    }

    if shape['type'] =='OPERATOR':
        shape['operator'] =  ob.rig_picker.operator
        shape['shortcut'] = ob.rig_picker.shortcut

    elif shape['type'] =='BONE':
        shape['bone'] =  ob.rig_picker.name
    
    eval_ob.to_mesh_clear()
    
    return shape


def get_picker_data(collection):
    picker_data = []
    
    depsgraph = bpy.context.evaluated_depsgraph_get()
    canvas = collection.rig_picker.canvas

    if canvas.type == 'CURVE':
        canvas_points = canvas.data.splines[0].points
    else:
        canvas_points = canvas.data.vertices
    
    canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]

    height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
    width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))

    center = sum(canvas_coords, Vector()) / len(canvas_coords)
    scale = 2048 / max(height, width)# Reference height for the canvas

    matrix = Matrix.Translation(-center)
    matrix = Matrix.Scale(scale, 4) @ matrix

    #sorted by their z axes
    for ob in collection.all_objects:
        if ob.instance_collection:
            for shape in ob.instance_collection.all_objects:
                picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph))
        
        elif ob.type in ('MESH', 'CURVE', 'FONT'):
            picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))

        else:
            #print(f'{ob.name} of type {ob.type} not supported')
            continue
                

    picker_data.sort(key=lambda x : x['depth'])

    #print(picker_datas)

    return picker_data
    #rig.data.rig_picker['shapes'] = picker_datas
