from ..DatabaseOms import DatabaseOms

from ....core.InstanceKey import InstanceKey
from ....core.InstanceReference import InstanceReference

import sqlite3

from framework import Guid

from ....definitions import KnownClassGuids, KnownInstanceGuids, KnownAttributeGuids, KnownRelationshipGuids

class SQLiteDatabaseOms (DatabaseOms):

	def __init__(self):
		self.conn = None
		self.__is_open = False

	def open(self, filename : str):
		"""
		Opens the SQLite database with the given filename.
		"""
		self.conn = sqlite3.Connection(filename)
		self.__is_open = True

	def is_open(self):
		return self.__is_open
	
	def get_next_instance_index(self, class_inst : InstanceReference):
		cursor = self.conn.execute("SELECT MAX(inst_id) + 1 FROM instances WHERE class_id = ?", [ class_inst.get_instance_key().get_instance_index() ])
		result = cursor.fetchone()[0]
		if result is None:
			result = 1
			
		return result

	def _create_instance_of(self, class_inst : InstanceReference, global_identifier : Guid = None):

		if global_identifier is None:
			global_identifier = Guid.create()

		next_iid = self.get_next_instance_index(class_inst)
		result = self.conn.execute("INSERT INTO instances (class_id, inst_id, global_identifier, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [ class_inst.get_instance_key().get_instance_index(), next_iid, global_identifier.strip() ])
		lastrowid = result.lastrowid
		
		# self.conn.execute("INSERT INTO attributes (src_inst_id, att_inst_id, att_value, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [ result.lastrowid, 5, name ])		
		self.conn.commit()

		cursor = self.conn.execute("SELECT * FROM instances WHERE id = ?", [ lastrowid ])
		results = cursor.fetchall()
		result = results[0]
		
		return self.get_instance_from_database(result)
		

	def create_class(self, class_id : int, inst_id : int, name : str, global_identifier : Guid):
		print ("create class : " + str(class_id) + "$" + str(inst_id) + " [" + name + "]     " + str(global_identifier) + ";")

		result = self.conn.execute("INSERT INTO instances (class_id, inst_id, global_identifier, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [ class_id, inst_id, global_identifier.strip() ])
		lastrowid = result.lastrowid

		print ("lastrowid: " + str(result.lastrowid))
		
		self.conn.execute("INSERT INTO attributes (src_inst_id, att_inst_id, att_value, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [ result.lastrowid, 5, name ])
		self.conn.commit()
		
		cursor = self.conn.execute("SELECT * FROM instances WHERE id = ?", [ lastrowid ])
		results = cursor.fetchall()
		result = results[0]
		
		return self.get_instance_from_database(result)

	def get_attributes(self, instance : InstanceReference):
		if instance is None:
			return [ ]

		cursor = self.conn.execute("SELECT DISTINCT att_inst_id FROM attributes WHERE src_inst_id = ? AND effective_date <= CURRENT_TIMESTAMP ORDER BY effective_date DESC", [ instance.dbid ])
		results = cursor.fetchall()
		retval = [ ]
		for result in results:
			retval.append(self.get_instance_by_dbid(result[0]))
			
		return retval

	def get_relationships(self, instance : InstanceReference):
		if instance is None:
			return [ ]
			
		cursor = self.conn.execute("SELECT DISTINCT rel_inst_id FROM relationships WHERE src_inst_id = ? AND effective_date <= CURRENT_TIMESTAMP ORDER BY effective_date DESC", [ instance.dbid ])
		results = cursor.fetchall()
		retval = [ ]
		for result in results:
			retval.append(self.get_instance_by_dbid(result[0]))
			
		return retval

	def get_related_instances(self, instance : InstanceReference, relationship : InstanceReference):
		if instance is None:
			return [ ]
		if relationship is None:
			return [ ]
			
		cursor = self.conn.execute("SELECT dst_inst_id, remove_flag FROM relationships WHERE src_inst_id = ? AND rel_inst_id = ? AND effective_date <= CURRENT_TIMESTAMP ORDER BY effective_date DESC", [ instance.dbid, relationship.dbid ])
		results = cursor.fetchall()
		retval_ids = [ ]
		retval = [ ]
		for result in results:
			if result[0] in retval_ids:
				if result[1] == 1:
					# remove flag
					retval_ids.remove(result[0])
			else:
				retval_ids.append(result[0])

		for id in retval_ids:
			retval.append(self.get_instance_by_dbid(id))
			
		return retval

	def assign_attribute(self, instance : InstanceReference, attribute : InstanceReference, value : str):
		
		self.conn.execute("INSERT INTO attributes (src_inst_id, att_inst_id, att_value, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [ instance.get_dbid(), attribute.get_dbid(), value ])
		self.conn.commit()

	def assign_relationship(self, instance : InstanceReference, relationship : InstanceReference, related_instances : InstanceReference|list):

		if relationship is None:
			raise ValueError("relationship for assign_relationship is None")

		if related_instances is None:
			raise ValueError("related_instances must not be None")

		if isinstance(related_instances, InstanceReference):
			related_instances = [ related_instances ]

		if isinstance(related_instances, list):
			for inst in related_instances:
				if not inst is None:
					self.conn.execute("INSERT INTO relationships (src_inst_id, rel_inst_id, dst_inst_id, remove_flag, effective_date) VALUES (?, ?, ?, 0, CURRENT_TIMESTAMP)", [ instance.get_dbid(), relationship.get_dbid(), inst.get_dbid() ])
				else:
					print("error: target inst for assign_relationship is None")

		else:
			print("related_instances must be either InstanceReference or list, was " + str(type(inst)))
			return
		
		self.conn.commit()

	def get_instance_by_dbid(self, dbid : int) -> InstanceReference:
		if self.conn is None:
			return None
			
		cursor = self.conn.execute("SELECT * FROM instances WHERE id = ? AND (effective_date IS NULL OR effective_date <= CURRENT_TIMESTAMP)", [ dbid ])
		results = cursor.fetchall()
		if len(results) > 0:
			return self.get_instance_from_database(results[0])
		
		return None

	def get_instance_by_key(self, key : InstanceKey) -> InstanceReference:
		if self.conn is None:
			return None
			
		qry = "SELECT * FROM instances WHERE class_id = " + str(key.get_class_index()) + " AND inst_id = " + str(key.get_instance_index()) + " AND (effective_date IS NULL OR effective_date <= CURRENT_TIMESTAMP)"
		result = self.conn.execute(qry)
		results = result.fetchall()
		if len(results) > 0:
			return self.get_instance_from_database(results[0])
		
		return None

		# retval = [ ]

		# for res in results:
		# 	retval.append(InstanceReference(int(res[0]), InstanceKey(int(res[1]), int(res[2])), res[3]))

		# if len(retval) == 1:
		# 	return retval[0]
		# elif len(retval) == 0:
		# 	return None

		# return retval

	def get_attribute_value_internal(self, inst : InstanceReference, att : InstanceReference, default_value = None):
		if inst is None or att is None:
			return None
			
		cursor = self.conn.execute("SELECT att_value FROM attributes WHERE src_inst_id = " + str(inst.dbid) + " AND att_inst_id = " + str(att.dbid) + " AND effective_date <= CURRENT_TIMESTAMP ORDER BY effective_date DESC, id DESC")
		results = cursor.fetchall()
		if (len(results) > 0):
			return results[0][0]
		return None

	def set_attribute_value_internal(self, inst : InstanceReference, att : InstanceReference, value):
		cursor = self.conn.execute("INSERT INTO attributes (src_inst_id, att_inst_id, att_value, effective_date) VALUES (?, ?, ?, CURRENT_TIMESTAMP)", [inst.dbid, att.dbid, value])
		self.conn.commit()

	def before_init(self):
		self.conn.execute("DROP TABLE IF EXISTS instances")
		self.conn.execute("DROP TABLE IF EXISTS attributes")
		self.conn.execute("DROP TABLE IF EXISTS relationships")
		self.conn.execute("DROP TABLE IF EXISTS relationship_instances")
		
		self.conn.execute("CREATE TABLE instances (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, class_id INT, inst_id INT, global_identifier CHAR(32), effective_date DATETIME)")
		self.conn.execute("CREATE TABLE attributes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, src_inst_id INTEGER NOT NULL, att_inst_id INTEGER NOT NULL, att_value TEXT, effective_date DATETIME)")
		# self.conn.execute("CREATE TABLE relationships (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, src_inst_id INTEGER NOT NULL, rel_inst_id INTEGER NOT NULL, effective_date DATETIME)")
		# self.conn.execute("CREATE TABLE relationship_instances (rel_id INTEGER NOT NULL, inst_id INTEGER NOT NULL, remove_flag INTEGER NOT NULL, effective_date DATETIME)")
		self.conn.execute("CREATE TABLE relationships (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, src_inst_id INTEGER NOT NULL, rel_inst_id INTEGER NOT NULL, dst_inst_id INTEGER NOT NULL, remove_flag INTEGER NOT NULL, effective_date DATETIME)")
		
	def after_init(self):
		self.conn.commit()


	def get_instances(self, of_class : InstanceReference = None):

		if self.conn is None:
			return None
		
		if of_class is not None:

			cursor = self.conn.execute("SELECT * FROM instances WHERE class_id = ?", [ of_class.get_instance_key().get_instance_index() ])
			results = cursor.fetchall()
			insts = [ ]

			for result in results:
				inst = self.get_instance_from_database(result)
				insts.append(inst)

			return insts
			
		else:

			cursor = self.conn.execute("SELECT * FROM instances")
			results = cursor.fetchall()
			insts = [ ]

			for result in results:
				inst = self.get_instance_from_database(result)
				insts.append(inst)

			return insts


	def get_instance_from_database(self, result):
		return InstanceReference(int(result[0]), InstanceKey(int(result[1]), int(result[2])), Guid.parse(result[3]))

	def get_instance_by_global_identifier(self, global_identifier : Guid):
		if self.conn is None:
			return None

		cursor = self.conn.execute("SELECT * FROM instances WHERE global_identifier = ?", [ global_identifier.strip() ])
		results = cursor.fetchall()
		if len(results) > 0:
			return self.get_instance_from_database(results[0])

		return None

	def close(self):
		self.conn.close()
		self.__is_open = False
	