import bpy
import bmesh

from mathutils import Vector



# Find a mesh that contains UV mesh shape keys 
def find_uv_mesh(objects, insideModifiers=True):
	for obj in objects:
		# Requires mesh & UV channel
		if obj and obj.type == 'MESH':

			# Contains shape keys?
			if obj.data.shape_keys and len(obj.data.shape_keys.key_blocks) == 2:
				if "uv" in obj.data.shape_keys.key_blocks and "model" in obj.data.shape_keys.key_blocks:
					return obj

			if insideModifiers:
				# Find via mesh deform modifier
				if len(obj.modifiers) > 0:
					for modifier in obj.modifiers:
						if modifier.type == 'SURFACE_DEFORM':
							if modifier.target:
								if modifier.target.data.shape_keys and len(modifier.target.data.shape_keys.key_blocks) == 2:
									return modifier.target
	return None



# # Find meshes that can be wrapped aka texture meshes
def find_texture_meshes(objects):
	obj_textures = []

	for obj in objects:
		if obj and obj.type == 'MESH':
			if find_uv_mesh([obj], insideModifiers=False) == None:
				if obj not in obj_textures:
					obj_textures.append(obj)

	return obj_textures



def uv_mesh_clear(obj_uv):
	# Remove Previous Modifiers
	if "Solidify" in obj_uv.modifiers:
		obj_uv.modifiers.remove( obj_uv.modifiers["Solidify"] )
	# Remove Solidify and push modifiers



def uv_mesh_fit(obj_uv, obj_textures):
	# Clear first shape transition
	bpy.context.scene.texToolsSettings.meshtexture_wrap = 1

	# Clear modifiers first
	uv_mesh_clear(obj_uv)

	# Add solidify modifier
	modifier_solid = obj_uv.modifiers.new(name="Solidify", type='SOLIDIFY')
	modifier_solid.offset = 1
	modifier_solid.thickness = 0 #scale*0.1 #10% height
	modifier_solid.use_even_offset = True
	modifier_solid.thickness_clamp = 0
	modifier_solid.use_quality_normals = True

	min_z = obj_uv.location.z
	max_z = obj_uv.location.z
	for i in range(len(obj_textures)):
		obj = obj_textures[i]
		
		# Min Max Z
		if i == 0:
			min_z = get_bbox(obj)['min'].z
			max_z = get_bbox(obj)['max'].z
		else:
			min_z = min(min_z, get_bbox(obj)['min'].z)
			max_z = max(max_z, get_bbox(obj)['max'].z)
	
	# Set thickness
	size = max(0.1, (max_z - min_z))
	min_z-= size*0.25 #Padding
	max_z+= size*0.25
	size = (max_z - min_z)
	
	modifier_solid.thickness = size

	# Set offset
	if size > 0:
		p_z = (obj_uv.location.z - min_z) / (max_z - min_z)
		modifier_solid.offset = -(p_z-0.5)/0.5
	else:
		modifier_solid.offset = 0



def get_bbox(obj):
	corners = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]

	# Get world space Min / Max
	box_min = Vector((corners[0].x, corners[0].y, corners[0].z))
	box_max = Vector((corners[0].x, corners[0].y, corners[0].z))
	for corner in corners:
		# box_min.x = -8
		box_min.x = min(box_min.x, corner.x)
		box_min.y = min(box_min.y, corner.y)
		box_min.z = min(box_min.z, corner.z)
		
		box_max.x = max(box_max.x, corner.x)
		box_max.y = max(box_max.y, corner.y)
		box_max.z = max(box_max.z, corner.z)

	return {
		'min':box_min, 
		'max':box_max, 
		'size':(box_max-box_min),
		'center':box_min+(box_max-box_min)/2
	}
