import bpy
import os
import bmesh
import math
import operator

from mathutils import Vector
from collections import defaultdict
from itertools import chain # 'flattens' collection of iterables

from . import utilities_uv




class op(bpy.types.Operator):
	bl_idname = "uv.textools_edge_split_bevel"
	bl_label = "Split Bevel"
	bl_description = "..."
	bl_options = {'REGISTER', 'UNDO'}

	radius : bpy.props.FloatProperty(
		name = "Space",
		description = "Space for split bevel",
		default = 0.015,
		min = 0,
		max = 0.35
	)


	@classmethod
	def poll(cls, context):
		if not bpy.context.active_object:
			return False

		#Only in Edit mode
		if bpy.context.active_object.mode != 'EDIT':
			return False

		#Requires UV map
		if not bpy.context.object.data.uv_layers:
			return False

		if bpy.context.scene.tool_settings.use_uv_select_sync:
			return False

		#Only in UV editor mode
		if bpy.context.area.type != 'IMAGE_EDITOR':
			return False

		return True


	def execute(self, context):
		main(self, self.radius)
		return {'FINISHED'}



def main(self, radius):

	#Store selection
	utilities_uv.selection_store()

	print("____________\nedge split UV sharp edges {}".format(radius))


	obj  = bpy.context.object
	bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
	uv_layers = bm.loops.layers.uv.verify();
	
	islands = utilities_uv.getSelectionIslands()
	

	# Collect UV to Vert
	vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers)
	uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers)

	# Collect hard edges
	edges = []
	for edge in bm.edges:
		if edge.select and not edge.smooth:
			# edge.link_faces
			# print("Hard edge: {} - {}".format(edge.verts[0].index, edge.verts[1].index))
			edges.append(edge)

	# Get vert rails to slide		
	vert_rails = get_vert_edge_rails(edges)

	# Get left and right faces
	edge_face_pairs = get_edge_face_pairs(edges)


	print("Vert rails: {}x".format(len(vert_rails)))
	# for vert in vert_rails:
	# 	print(".. v.idx {} = {}x".format(vert.index, len(vert_rails[vert]) ))



	vert_processed = []
	vert_uv_pos = []

	for edge in edges:
		if len(edge_face_pairs[edge]) == 2:
			v0 = edge.verts[0]
			v1 = edge.verts[1]
			
			f0 = edge_face_pairs[edge][0]
			f1 = edge_face_pairs[edge][1]

			# v0
			if v0 not in vert_processed:
				vert_processed.append(v0)
				faces, origin, delta = slide_uvs(v0, edge, f0, edges, vert_rails, vert_to_uv)
				vert_uv_pos.append( {"v":v0, "f":f0, "origin":origin, "delta":delta, "faces":faces} )

				faces, origin, delta = slide_uvs(v0, edge, f1, edges, vert_rails, vert_to_uv)
				vert_uv_pos.append( {"v":v0, "f":f1, "origin":origin, "delta":delta, "faces":faces} )

			# V1
			if v1 not in vert_processed:
				vert_processed.append(v1)
				faces, origin, delta = slide_uvs(v1, edge, f0, edges, vert_rails, vert_to_uv)
				vert_uv_pos.append( {"v":v1, "f":f0, "origin":origin, "delta":delta, "faces":faces} )
				
				faces, origin, delta = slide_uvs(v1, edge, f1, edges, vert_rails, vert_to_uv)
				vert_uv_pos.append( {"v":v1, "f":f1, "origin":origin, "delta":delta, "faces":faces} )
	
	# ...
	for item in vert_uv_pos:
		v = item["v"]

		
		for face in item["faces"]:
			if v in face.verts:
				for loop in face.loops:
					if loop.vert == v:
						loop[uv_layers].uv= item["origin"] + item["delta"] * (radius/2)
		# for f in faces:
		# 	for loop in f.loops:
		# 		if loop.vert == vert:
		# 			loop[uv_layers].uv= vert_to_uv[vert][0].uv + item["delta"] * radius/2

	



	# for loop in face.loops:
	# 	if loop.vert == vert:
	# 		loop[uv_layers].uv+= avg_uv_delta



	#Restore selection
	utilities_uv.selection_restore()


def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
	
	def IS_DEBUG():
		return vert.index == 64 and edge.verts[0].index == 64 and edge.verts[1].index == 63


	A = edge.verts[0]
	B = edge.verts[1]
	A_links, B_links = get_edge_prev_next(edge, edges)
	
	verts_edges = {edge.verts[0], edge.verts[1]}
	for v in A_links:
		verts_edges.add( v )
	for v in B_links:
		verts_edges.add( v )

	if IS_DEBUG():
		print("\r")

	print("Edge {} <--> {}  ({})".format(edge.verts[0].index, edge.verts[1].index , vert.index))

	# Collect faces of this side

	'''
	faces = [face]
	face_edges_used = [e for e in face.edges if e in edges]
	for e in face.edges:
		if e not in face_edges_used:
			for f in e.link_faces:
				if f != face:
					faces.append(f)
	'''

	faces = [face]
	edges_main_used = [edge]
	for i in range(2):
		append = []

		for f in faces:
			for e in f.edges:
				if e not in edges_main_used:
					if e in edges:
						edges_main_used.append(e)

					for f_link in e.link_faces:
						if f_link not in faces:
							append.append(f_link)
		faces.extend(append)

	if IS_DEBUG():
		print("  Faces {}x = {}".format(len(faces), [f.index for f in faces]))


	# Get all face edges that could be valid rails
	face_edges = list(set([e for f in faces for e in f.edges if e not in edges]))

	# The verts influencing the offset
	verts = [A,B]
	if vert == A:
		verts.extend(B_links)
	elif vert == B:
		verts.extend(A_links)
	# verts = [vert]

	if IS_DEBUG():
		print("  Verts: {}x = {}".format(len(verts), [v.index for v in verts]))
		print("  Rails:")


	delta = Vector((0,0))
	count = 0.0
	for v in verts:
		rails = [e for e in vert_rails[v] if e in face_edges]

		if IS_DEBUG():
			print("    #{}  rails = {}".format(v.index, [("{} - {}".format(e.verts[0].index, e.verts[1].index)) for e in rails]))


		for e in rails:
			# determine order
			v0 = None
			v1 = None
			if e.verts[0] in verts_edges:
				v0 = e.verts[0]
				v1 = e.verts[1]
			elif e.verts[1] in verts_edges:
				v0 = e.verts[1]
				v1 = e.verts[0]
			uv0 = vert_to_uv[v0][0].uv
			uv1 = vert_to_uv[v1][0].uv
			delta += (uv1-uv0).normalized()
			count += 1.0

	delta/=count

	if IS_DEBUG():
		print("\r")

	return faces, vert_to_uv[vert][0].uv.copy(), delta.normalized()
	# print("	V{} = {}".format(v.index, avg_uv_delta))

	# for loop in face.loops:
	# 	if loop.vert == vert:
	# 		loop[uv_layers].uv+= avg_uv_delta



'''
def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
	avg_target = Vector((0,0))
	avg_count = 0

	for e in face.edges:
		if e != edge and vert in e.verts:
			vert_B = e.verts[0]
			if vert == e.verts[0]:
				vert_B = e.verts[1]
			A = vert_to_uv[vert][0].uv
			B = vert_to_uv[vert_B][0].uv

			avg_target+= A +(B - A).normalized() * radius
			avg_count+=1

	avg_target/=avg_count
	avg_target = vert_to_uv[vert][0].uv +(avg_target - vert_to_uv[vert][0].uv).normalized() * radius

	for loop in face.loops:
		if loop.vert == vert:
			loop[uv_layers].uv = avg_target
'''








'''
	# Get all rails (max 3x: current, before and after)
	rails = [e for v in verts_edges for e in vert_rails[v]]

	for x in rails:
		print("       raail: {} x {}".format(x.verts[0].index, x.verts[1].index))

	# Keep only rails shared with faces
	rails = [e for e in rails if e in face_edges]


	# print("...... v{} with {}x rails ".format(vert.index, len(rails)))

	# Filter rails on same side
'''




def get_edge_prev_next(edge, edges):
	A = edge.verts[0]
	B = edge.verts[1]

	# print("  get_edge_prev_next {}x edges".format(len(edges)))
	# v0_extends = []	
	# v0_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v0]
	# v1_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v1]
	# v0_extends = [v_nest for v in edge.verts for e in v.link_edges for v_nest in e.verts if e != edge and if e in edges]
	
	A_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e != edge and e in edges and v2 not in edge.verts and v1 != A]
	B_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e != edge and e in edges and v2 not in edge.verts and v1 != B]

	return A_extends, B_extends


def get_edge_face_pairs(edges):
	edge_faces = {}
	for edge in edges:
		v0 = edge.verts[0]
		v1 = edge.verts[1]
		faces = []
		for face in edge.link_faces:
			if v0 in face.verts and v1 in face.verts:
				faces.append(face)
		edge_faces[edge] = faces

	return edge_faces



def get_vert_edge_rails(edges):

	vert_rails = {}
	for edge in edges:
		v0 = edge.verts[0]
		v1 = edge.verts[1]

		faces = []
		for face in edge.link_faces:
			if v0 in face.verts and v1 in face.verts:
				faces.append(face)

		for face in faces:
			for e in face.edges:
				if e not in edges and len(e.link_faces) > 0:
					if v0 not in vert_rails:
						vert_rails[ v0 ] = []
					if v1 not in vert_rails:
						vert_rails[ v1 ] = []

					if v0 in e.verts and e not in vert_rails[v0]:
						vert_rails[v0].append(e)

					if v1 in e.verts and e not in vert_rails[v1]:
						vert_rails[v1].append(e)

	return vert_rails


bpy.utils.register_class(op)
