import MySQLdb

from .libraryparser import XMLLibraryParser, JSONLibraryParser, YAMLLibraryParser

from .LibraryOperation import LibraryOperation
from .operations.AssignAttributeOperation import AssignAttributeOperation
from .operations.AssignRelationshipOperation import AssignRelationshipOperation
from .operations.CreateClassOperation import CreateClassOperation
from .operations.CreateInstanceOperation import CreateInstanceOperation
from .operations.PrepareInstanceOperation import PrepareInstanceOperation
from .operations.StoredProcedureOperation import StoredProcedureOperation

from .Guid import Guid

class MochaLibraryManager:

	def __init__(self):
		self.entityReferences = dict()
		self.db = None
		self._instances = []
		self._operations = []
		self._relOps = []

	def connect(self, hostname, database, username, password):
		self.db = MySQLdb.connect(host=hostname, user=username, passwd=password, db=database)

	def commit(self):
		for (class_gid, inst_gid, class_index, index) in self._instances:
			if class_index is None and class_gid is not None:
				# class_index = self.db.query("SELECT inst_id FROM mocha_instances WHERE tenant_id = 1 AND id = mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + class_gid + "'))")

				# class_index = (SELECT inst_id FROM mocha_instances WHERE tenant_id = p_tenant_id AND id = mocha_get_instance_by_global_identifier(p_class_global_identifier));
				op = PrepareInstanceOperation(Guid(class_gid), Guid(inst_gid), class_index, index)
				op.execute(self.db)

		for op in self._operations:
			op.execute(self.db)
		for op in self._relOps:
			op.execute(self.db)

		hasSiblingRelationships = dict()
		for op in self._relOps:
			if op.relationshipInstanceId.get_value() == "{656110FF-4502-48B8-A7F3-D07F017AEA3F}":
				# Relationship.has sibling Relationship
				hasSiblingRelationships[op.instanceId.get_value()] = op.targetInstanceId

		for op in self._relOps:
			if op.relationshipInstanceId.get_value() in hasSiblingRelationships:
				siblingOp = AssignRelationshipOperation(op.targetInstanceId, hasSiblingRelationships[op.relationshipInstanceId.get_value()], op.instanceId)
				print ("assigning sibling relationship " + siblingOp.instanceId.get_value() + " . " + siblingOp.relationshipInstanceId.get_value() + " = " + siblingOp.instanceId.get_value() + "\n")
				siblingOp.execute(self.db)

		cur = self.db.cursor()
		# cur.execute("UPDATE mocha_instances SET user_inst_id = mocha_get_instance_by_global_identifier(mocha_normalize_uuid('{B066A54B-B160-4510-A805-436D3F90C2E6}'))")
		cur.execute("UPDATE mocha_attributes SET usr_inst_id = mocha_get_instance_by_global_identifier(mocha_normalize_uuid('{B066A54B-B160-4510-A805-436D3F90C2E6}'))")
		cur.execute("UPDATE mocha_relationships SET user_inst_id = mocha_get_instance_by_global_identifier(mocha_normalize_uuid('{B066A54B-B160-4510-A805-436D3F90C2E6}'))")
		# rows = cur.fetchall()
		# print(rows)

		# print ("ok , applying `Class.has Instance`...")
		
		# for row in rows:
		# 	print(row)
		# 	id = row[0]
		# 	tenant_id = row[1]
		# 	class_id = row[2]
		# 	inst_id = row[3]
		# 	global_identifier = row[4]

		# 	if class_id is not None and id is not None:

		# 		# Class.has Instance
		# 		query2 = ("CALL mocha_assign_relationship (" +
		# 			"mocha_get_instance_by_key(1, " + str(class_id) + "), " +
		# 			"mocha_get_instance_by_global_identifier(mocha_normalize_uuid('7EB41D3C-2AE9-4884-83A4-E59441BCAEFB'))" + ", " +
		# 			str(id) + ", NULL, NULL)")
		# 		cur.execute(query2)

		# 		# Instance.for Class
		# 		query3 = ("CALL mocha_assign_relationship (" +
		# 			str(id) + ", " +
		# 			"mocha_get_instance_by_global_identifier(mocha_normalize_uuid('494D5A6D-04BE-477B-8763-E3F57D0DD8C8'))" + ", " +
		# 			"mocha_get_instance_by_key(1, " + str(class_id) + "), NULL, NULL)")
		# 		cur.execute(query3)

		self.db.commit()
			
	def close(self):
		self.db.close()

	def print_error(self, cur):
		rows = cur.fetchall()
		for row in rows:
			if 'error_description' in row:
				print (row['error_description'])

	def select_tenant(self, tenantName):
		cur = self.db.cursor()
		cur.execute("CALL mocha_select_tenant(mocha_get_tenant_by_name('" + tenantName + "'))")
		self.print_error(cur)

	def release_tenant(self):
		cur = self.db.cursor()
		cur.execute("CALL mocha_release_tenant()")
		self.print_error(cur)
		
	def assign_relationship(self, instanceId : Guid, relationshipInstanceId : Guid, targetInstanceId : Guid):
		print("-- assigning relationship " + relationshipInstanceId.get_value() + " = '" + targetInstanceId.get_value() + "'")
		self._relOps.append(AssignRelationshipOperation(instanceId, relationshipInstanceId, targetInstanceId))
		# self._operations.append(AssignRelationshipOperation(instanceId, relationshipInstanceId, targetInstanceId))
		# query = "CALL mocha_assign_relationship(mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + instanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + relationshipInstanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + targetInstanceId.get_value() + "')), NULL, NULL);"
		# print(query)
		# cur.execute(query)
		# self.print_error(cur)

	def set_attribute_value(self, instanceId : Guid, attributeInstanceId : Guid, value):
		print("-- assigning attribute " + attributeInstanceId.get_value() + " = '" + str(value) + "'")
		self._operations.append(AssignAttributeOperation(instanceId, attributeInstanceId, value))

	def install_from_path(self, path):

		from glob import glob

		xl = XMLLibraryParser(self)
		jl = JSONLibraryParser(self)
		yl = YAMLLibraryParser(self)
		
		#xml_files = glob(path + "/**/*.xml", recursive=True)
		#for xml_file in xml_files:
		#s	xl.load_file(xml_file)

		
		#json_files = glob(path + "/**/*.json", recursive=True)
		#for json_file in json_files:
		#	jl.load_file(json_file)
		
		yaml_files = sorted(glob(path + "/**/*.yaml", recursive=True))
		if len(yaml_files) == 0:
			print ("no files found ; does the path exist?")
			return 3

		# first, load the entity defs
		for yaml_file in yaml_files:
			yl.load_entity_definitions_from_file(yaml_file)

		try:
			# then, load instance definitions (also loads sugar elements into memory for later use)
			for yaml_file in yaml_files:
				yl.load_instances_from_file(yaml_file)
					
			# finally, apply syntactic sugar
			yl.apply_sugar()

			return True

		except NameError as ex:

			print (ex)

			rgx = "&(.*);"
			
			# go through and get all entity references across all files
			import re
			import fileinput

			stuff = []
			
			for yaml_file in yaml_files:
				f = open(yaml_file, "r")
				text = f.read()
				matches = re.findall(rgx, text)
				for match in matches:
					stuff.append(match)
				f.close()

			missingEntities = []
			for stuf in stuff:
				if not stuf in self.entityReferences:
					if not stuf in missingEntities:
						missingEntities.append(stuf)

			if len(missingEntities) > 0:
				print("\nNOTE: there were undefined referenced entities:\n")
				for missingEntity in missingEntities:
					print("\t" + missingEntity)
				print("\n")

			return False
			
			
	def register_entity_reference(self, name, value):
		self.entityReferences[name] = value
	def define_entity_reference(self, name):
		return self.entityReferences[name]
	def expand_entity_references(self, value):
		insideName = False
		name = ""
		retval = ""
		for i in range(0, len(value)):
			if value[i] == "&":
				insideName = True
			elif value[i] == ';':
				insideName = False

				if name in self.entityReferences:
					retval += self.define_entity_reference(name)
				else:
					raise NameError("unknown entity ref '" + name + "'")

				name = ""
			elif insideName:
				name += value[i]
			else:
				retval += value[i]
		return retval

	def add_instance(self, classGid : Guid, instGid : Guid, classIndex : int, index : int):
		print("adding instance for class '" + classGid.get_value() + "' with globalid '" + instGid.get_value() + "' [" + str(index) + "]")
		self._instances.append((classGid.get_value(), instGid.get_value(), classIndex, index))

		# assign relationship `Instance.for Class`
		self.assign_relationship(instGid, Guid('494D5A6D04BE477B8763E3F57D0DD8C8'), classGid)

		# assign relationship `Class.has Instance`
		self.assign_relationship(classGid, Guid('7EB41D3C2AE9488483A4E59441BCAEFB'), instGid)
		