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_island_align_world"
	bl_label = "Align World"
	bl_description = "Align selected UV islands to world / gravity directions"
	bl_options = {'REGISTER', 'UNDO'}

	# is_global = bpy.props.BoolProperty(
	# 	name = "Global Axis",
	# 	description = "Global or local object axis alignment",
	# 	default = False
	# )

	# def draw(self, context):
	# 	layout = self.layout
	# 	layout.prop(self, "is_global")

	@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)
		return {'FINISHED'}



def main(context):
	print("\n________________________\nis_global")

	#Store selection
	utilities_uv.selection_store()

	bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
	uv_layers = bm.loops.layers.uv.verify()

	#Only in Face or Island mode
	if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
		bpy.context.scene.tool_settings.uv_select_mode = 'FACE'

	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()

	

	for faces in islands:
		# Get average viewport normal of UV island
		avg_normal = Vector((0,0,0))
		for face in faces:
			avg_normal+=face.normal
		avg_normal/=len(faces)

		# avg_normal = (obj.matrix_world*avg_normal).normalized()

		# Which Side
		x = 0
		y = 1
		z = 2
		max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z))
		
		# Use multiple steps
		for i in range(3):
			if(abs(avg_normal.x) == max_size):
				print("x normal")
				align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False)

			elif(abs(avg_normal.y) == max_size):
				print("y normal")
				align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False)

			elif(abs(avg_normal.z) == max_size):
				print("z normal")
				align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0)

		print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size))

	

	#Restore selection
	utilities_uv.selection_restore()



def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):

	# Find lowest and highest verts
	minmax_val  = [0,0]
	minmax_vert = [None, None]

	axis_names = ['x', 'y', 'z']
	print("Align shell {}x 	at {},{} flip {},{}".format(len(faces), axis_names[x], axis_names[y], flip_x, flip_y))


		# print("  Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
		# print("  A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
		


	# 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)

	processed_edges = []
	edges = []
	for face in faces:
		for edge in face.edges:
			if edge not in processed_edges:
				processed_edges.append(edge)
				delta = edge.verts[0].co -edge.verts[1].co
				max_side = max(abs(delta.x), abs(delta.y), abs(delta.z))

				# Check edges dominant in active axis
				if( abs(delta[x]) == max_side or abs(delta[y]) == max_side):
				# if( abs(delta[y]) == max_side):
					edges.append(edge)

	print("Edges {}x".format(len(edges)))

	avg_angle = 0
	for edge in edges:
		uv0 = vert_to_uv[ edge.verts[0] ][0]
		uv1 = vert_to_uv[ edge.verts[1] ][0]
		delta_verts = Vector((
			edge.verts[1].co[x] - edge.verts[0].co[x],
			edge.verts[1].co[y] - edge.verts[0].co[y]
		))
		

		if flip_x:
			delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
		if flip_y:
			delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y]
		
		# 	delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y]
			

		delta_uvs = Vector((
			uv1.uv.x - uv0.uv.x,
			uv1.uv.y - uv0.uv.y
		))
		a0 = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
		a1 = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2

		


		a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) 
		# edge.verts[0].index, edge.verts[1].index
		# print("  turn {:.1f}	.. {:.1f} , {:.1f}".format(a_delta*180/math.pi, a0*180/math.pi,a1*180/math.pi))
		avg_angle+=a_delta


	avg_angle/=len(edges) # - math.pi/2
	print("Turn {:.1f}".format(avg_angle * 180/math.pi))
	
	bpy.ops.uv.select_all(action='DESELECT')
	for face in faces:
		for loop in face.loops:
			loop[uv_layers].select = True


	bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
	bpy.ops.transform.rotate(value=avg_angle, orient_axis='Z')
	# bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348)


	# processed = []
	

	'''
	bpy.ops.uv.select_all(action='DESELECT')
	for face in faces:

		# Collect UV to Vert
		for loop in face.loops:
			loop[uv_layers].select = True
			vert = loop.vert
			uv = loop[uv_layers]
			# vert_to_uv
			if vert not in vert_to_uv:
				vert_to_uv[vert] = [uv];
			else:
				vert_to_uv[vert].append(uv)
			# uv_to_vert
			if uv not in uv_to_vert:
				uv_to_vert[ uv ] = vert;


		for vert in face.verts:
			if vert not in processed:
				processed.append(vert)

				vert_y = (vert.co)[y] #obj.matrix_world * 

				print("idx {} = {}".format(vert.index, vert_y))

				if not minmax_vert[0] or not minmax_vert[1]:
					minmax_vert[0] = vert
					minmax_vert[1] = vert
					minmax_val[0] = vert_y
					minmax_val[1] = vert_y
					continue

				if vert_y < minmax_val[0]:
					# Not yet defined or smaller
					minmax_vert[0] = vert
					minmax_val[0] = vert_y
					
				elif vert_y > minmax_val[1]:
					minmax_vert[1] = vert
					minmax_val[1] = vert_y
					

	if minmax_vert[0] and minmax_vert[1]:
		axis_names = ['x', 'y', 'z']
		print("  Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
		# print("  A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
		
		vert_A = minmax_vert[0]
		vert_B = minmax_vert[1]
		uv_A = vert_to_uv[vert_A][0]
		uv_B = vert_to_uv[vert_B][0]

		delta_verts = Vector((
			vert_B.co[x] - vert_A.co[x],
			vert_B.co[y] - vert_A.co[y]
		))

		delta_uvs = Vector((
			uv_B.uv.x - uv_A.uv.x,
			uv_B.uv.y - uv_A.uv.y,

		))
		# Get angles
		angle_vert = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
		angle_uv = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2

		angle_delta = math.atan2(math.sin(angle_vert-angle_uv), math.cos(angle_vert-angle_uv))

		print("  Angles {:.2f} | {:.2f}".format(angle_vert*180/math.pi, angle_uv*180/math.pi))
		print("  Angle Diff {:.2f}".format(angle_delta*180/math.pi))

		bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
		bpy.ops.transform.rotate(value=angle_delta, axis='Z')
		# bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348)


		# bpy.ops.mesh.select_all(action='DESELECT')
		# vert_A.select = True
		# vert_B.select = True

		# return
	'''

bpy.utils.register_class(op)
