import constants
from db import MySQLObject

# models.py - Python representation of database table records

class Programme(MySQLObject):
    def __init__(self, id, name):
        # initialize the id, name and modules attributes.
        self.__id = id # a non-public string to store the id of the programme.
        self.__name = name # a non-public string so store the name of the programme.
        self.__modules = {} # a non-public dictionary to store Module objects, grouped by semester.

    def __eq__(self, other):
        # returns true if the id attribute is equal to the other object’s  	id attribute.
        return self.__id == other.get_id()

    def get_id(self):
        # returns the id for this programme.
        return self.__id

    def get_name(self):
        # returns the name for this programme.
        return self.__name

    def get_modules(self):
        # returns Module dictionary object which contains module list of all semesters
        return self.__modules

    def add_module(self, semester, module):
        # append module into list inside modules’ dictionary,
        # using  semester as key.
        if semester in self.__modules:
            # module already contain semester, fall to end of function to add module into semester
            pass
        else:
            # If the semester key is not found in the  dictionary,
            # an empty list will be created to store the module object, init an empty semester list literally
            self.__modules[semester]= []

        # the semester will be available as a key after this line, append module into semester
        self.__modules[semester].append(module)

    def get_modules_by_semester(self, semester):
        # returns Module list object from modules dictionary, using semester as key
        return self.__modules[semester]

    @staticmethod
    def fetchall(db):
        # overrides the parent class fetchall abstract static method to
        # create a list of Programme objects retrieved from database table programme.
        # For each of the created Programme objects,
        # The modules belongs to the particular programme

        output = []
        temp= {}

        with db.cursor() as cursor:
            cursor.execute(f"USE {constants.DB_NAME};")
            query = '''
SELECT programme_id, mapping_semester, module_id, programme_name, module_name, module_credit
FROM programmes
INNER JOIN mappings ON programmes.programme_id = mappings.mapping_programme_id
INNER JOIN modules ON mappings.mapping_module_id = modules.module_id;
            '''
            cursor.execute(query)
            records = cursor.fetchall()

            # row => module, bloat modules
            for row in records:
                programme_id = row[0]
                mapping_semester = row[1]
                module_id = row[2]
                programme_name = row[3]
                module_name = row[4]
                module_credit = row[5]
                new_module = Module(module_id, module_name, module_credit)

                if not(programme_id in temp):
                    temp[programme_id] = {'_name': programme_name}
                if not(mapping_semester in temp[programme_id] ):
                    temp[programme_id][mapping_semester] = []

                temp[programme_id][mapping_semester].append(new_module)

            for programme_id in temp.keys():
                new_program = Programme(programme_id, temp[programme_id]['_name'])
                # semester
                for semester in temp[programme_id].keys():
                    if (type(semester) == type(1)):
                        modules_in_semester = temp[programme_id][semester]
                        for module in modules_in_semester:
                            new_program.add_module(semester, module)

                output.append(new_program)

        return output

class Module(MySQLObject):
    def __init__(self, id, name, credit):
        # initialize the id, name and credit attributes
        self.__id = id # a non-public string to store the id of the module
        self.__name = name # a non-public string to store the name of the module
        self.__credit = credit # a non-public integer to store the credit of the module

    def __eq__(self, other):
        # returns true of the id attribute is equal to the other object’s id attribute
        if (hasattr(other,'get_id')):
            return self.__id == other.get_id()
        else:
            return False

    def get_id(self):
        # returns the id for this module
        return self.__id

    def get_name(self):
        # returns the name for this module
        return self.__name

    def get_credit(self):
        # returns the credit for this module
        return self.__credit

    @staticmethod
    def fetchall(db):
        # fetchall overrides the parent class fetchall abstract static method to
        #   return a list of Module objects retrieved from database table module.
        # Module objects will be created and appended into the
        #   returned list for every records retrieved  from database.
        query = "select module_id, module_name, module_credit from modules"
        output = []

        with db.cursor() as cursor:
            cursor.execute(f"USE {constants.DB_NAME};")
            cursor.execute(query)
            records = cursor.fetchall()
            for row in records:
                module_id = row[0]
                module_name = row[1]
                module_credit = row[2]
                output.append(Module(module_id, module_name, module_credit))

        return output
