import bpy
import os
import bmesh
import operator
from mathutils import Vector
from collections import defaultdict
from math import pi

from . import utilities_texel


texture_modes = ['UV_GRID','COLOR_GRID','GRAVITY','NONE']



class op(bpy.types.Operator):
	bl_idname = "uv.textools_texel_checker_map"
	bl_label = "Checker Map"
	bl_description = "Add a checker map to the selected model and UV view"
	bl_options = {'REGISTER', 'UNDO'}
	
	@classmethod
	def poll(cls, context):
		if len(get_valid_objects()) == 0:
			return False

		return True

	def execute(self, context):
		assign_checker_map(
			bpy.context.scene.texToolsSettings.size[0], 
			bpy.context.scene.texToolsSettings.size[1]
		)
		return {'FINISHED'}




def assign_checker_map(size_x, size_y):
	# Force Object mode
	if bpy.context.view_layer.objects.active != None and bpy.context.object.mode != 'OBJECT':
		bpy.ops.object.mode_set(mode='OBJECT')

	# Collect Objects
	objects = get_valid_objects()
	
	if len(objects) == 0:
		self.report({'ERROR_INVALID_INPUT'}, "No UV mapped objects selected" )

	#Change View mode to TEXTURED
	for area in bpy.context.screen.areas:
		if area.type == 'VIEW_3D':
			for space in area.spaces:
				if space.type == 'VIEW_3D':
					space.shading.type = 'MATERIAL'


	if len(objects) > 0:

		# Detect current Checker modes
		mode_count = {}
		for mode in texture_modes:
			mode_count[mode] = 0

		# Image sizes
		image_sizes_x = []
		image_sizes_y = []

		# Collect current modes in selected objects
		for obj in objects:
			image = utilities_texel.get_object_texture_image(obj)
			mode = 'NONE'
			if image:
				if "GRAVITY" in image.name.upper():
					mode = 'GRAVITY'

				elif image.generated_type in texture_modes:
					# Generated checker maps
					mode = image.generated_type

					# Track image sizes
					if image.size[0] not in image_sizes_x:
						image_sizes_x.append(image.size[0])
					if image.size[1] not in image_sizes_y:
						image_sizes_y.append(image.size[1])

			mode_count[mode]+=1


		# Sort by count (returns tuple list of key,value)
		mode_max_count = sorted(mode_count.items(), key=operator.itemgetter(1))
		mode_max_count.reverse()

		for key,val in mode_max_count:
			print("{} = {}".format(key, val))


		# Determine next mode
		mode = 'NONE'
		if mode_max_count[0][1] == 0:
			# There are no maps
			mode = texture_modes[0]

		elif mode_max_count[0][0] in texture_modes:
			if mode_max_count[-1][1] > 0:
				# There is more than 0 of another mode, complete existing mode first
				mode = mode_max_count[0][0]

			else:
				# Switch to next checker mode
				index = texture_modes.index(mode_max_count[0][0])
				
				if texture_modes[ index ] != 'NONE' and len(image_sizes_x) > 1 or len(image_sizes_y) > 1:
					# There are multiple resolutions on selected objects
					mode = texture_modes[ index ]
				elif texture_modes[ index ] != 'NONE' and (len(image_sizes_x) > 0 and image_sizes_x[0] != size_x) and (len(image_sizes_y) > 0 and image_sizes_y[0] != size_y):
					# The selected objects have a different resolution
					mode = texture_modes[ index ]
				else:
					# Next mode
					mode = texture_modes[ (index+1)%len(texture_modes) ]


		print("Mode: "+mode)

		if mode == 'NONE':
			for obj in objects:
				remove_material(obj)

		elif mode == 'GRAVITY':
			image = load_image("checker_map_gravity")
			for obj in objects:
				apply_image(obj, image)

		else:
			name = utilities_texel.get_checker_name(mode, size_x, size_y)
			image = get_image(name, mode, size_x, size_y)
			for obj in objects:
				apply_image(obj, image)
	
	# Restore object selection
	bpy.ops.object.mode_set(mode='OBJECT')
	bpy.ops.object.select_all(action='DESELECT')
	for obj in objects:				
		obj.select_set( state = True, view_layer = None)

	# Clean up images and materials
	utilities_texel.checker_images_cleanup()

	# Force redraw of viewport to update texture
	# bpy.context.scene.update()
	bpy.context.view_layer.update()




def load_image(name):
	pathTexture = icons_dir = os.path.join(os.path.dirname(__file__), "resources/{}.png".format(name))
	image = bpy.ops.image.open(filepath=pathTexture, relative_path=False)
	if "{}.png".format(name) in bpy.data.images:
		bpy.data.images["{}.png".format(name)].name = name #remove extension in name
	return bpy.data.images[name];



def get_valid_objects():
	# Collect Objects
	objects = []
	for obj in bpy.context.selected_objects:
		if obj.type == 'MESH' and obj.data.uv_layers:
				objects.append(obj)

	return objects







def remove_material(obj):
	bpy.ops.object.mode_set(mode='OBJECT')
	bpy.ops.object.select_all(action='DESELECT')
	obj.select_set( state = True, view_layer = None)
	bpy.context.view_layer.objects.active = obj
	count = len(obj.material_slots)
	for i in range(count):
		bpy.ops.object.material_slot_remove()



def apply_image(obj, image):

	bpy.ops.object.mode_set(mode='OBJECT')
	bpy.ops.object.select_all(action='DESELECT')
	obj.select_set( state = True, view_layer = None)
	bpy.context.view_layer.objects.active = obj

	# Assign Cycles material with image

	# Get Material
	material = None
	if image.name in bpy.data.materials:
		material = bpy.data.materials[image.name]
	else:
		material = bpy.data.materials.new(image.name)
		material.use_nodes = True

	# Assign material
	if len(obj.data.materials) > 0:
		obj.data.materials[0] = material
	else:
		obj.data.materials.append(material)

	# Setup Node
	tree = material.node_tree
	node = None
	if "checker" in tree.nodes:
		node = tree.nodes["checker"]
	else:
		node = tree.nodes.new("ShaderNodeTexImage")
	node.name = "checker"
	node.select = True
	tree.nodes.active = node
	node.image = image

	# LINKANDO:
	tree = obj.data.materials[0].node_tree
	links = tree.links
	nodo1 = tree.nodes['checker']
	nodo2 = tree.nodes['Principled BSDF']
	links.new(nodo1.outputs['Color'], nodo2.inputs['Base Color'])


def get_image(name, mode, size_x, size_y):
	# Image already exists?
	if name in bpy.data.images:
		# Update texture UV checker mode
		bpy.data.images[name].generated_type = mode
		return bpy.data.images[name];

	# Create new image instead
	image = bpy.data.images.new(name, width=size_x, height=size_y)
	image.generated_type = mode #UV_GRID or COLOR_GRID
	image.generated_width = int(size_x)
	image.generated_height = int(size_y)

	return image

bpy.utils.register_class(op)
