# author Aaron Moser

from typing import List
from enum import Enum
import constants

from heater_instance_interface import HeaterInstanceInterface
from heater_instance_worker_thread import HeaterInstanceWorkerThread
from database_interface import DatabaseInterface
from global_outcome import GlobalOutcome
from heater.host import Host
from simulation import Simulation
from datetime import datetime

class HeaterInstanceInterfaceManager:

    class HeaterInstanceInterfaceManagerErrorCode(Enum):
        HEATER_INSTANCE_ALREADY_EXISTS = 1
        HEATER_INSTANCE_ADDED = 2
        HEATER_INSTANCE_ADDED_AND_STARTED = 3
        HEATER_INSTANCE_REMOVED = 4

    def __init__(self, global_outcome: GlobalOutcome, mode: Simulation.Mode, start_date: datetime, end_date: datetime, database_interface: DatabaseInterface):
        self.global_outcome = global_outcome
        self.heater_instances: List[HeaterInstanceInterface] = []
        self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
        self._mode: Simulation.Mode = mode
        self.database_interface = database_interface
        self.start_date = start_date
        self.end_date = end_date

    # Instance management --------------------------------------------------------------------------------------

    # Adds new heater instance to list of instances
    def add_heater_instance_and_start_thread_if_running(self, host: Host, simulation_state: constants.State) -> HeaterInstanceInterfaceManagerErrorCode:
        heater_instance_exists = False
        for heater_instance in self.heater_instances:
            if heater_instance.get_host().equals(host):
                heater_instance_exists = True
                return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ALREADY_EXISTS
        if not heater_instance_exists:
            new_heater_instance_interface = HeaterInstanceInterface(host)
            self.heater_instances.append(new_heater_instance_interface)
            if simulation_state == constants.State.Running:
                new_thread = HeaterInstanceWorkerThread(self.global_outcome, self._mode, new_heater_instance_interface, self.start_date, self.end_date, self.database_interface)
                self.__worker_thread_pool.append(new_thread)
                new_thread.run()
                return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED_AND_STARTED
            else:
                return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED

    # Removes heater instance from list of instances
    def remove_heater_instance(self, host) -> HeaterInstanceInterfaceManagerErrorCode:
        heater_instance_exists = False
        for heater_instance in self.heater_instances:
            if heater_instance.get_host().equals(host):
                heater_instance_exists = True
        if heater_instance_exists:
            self.heater_instances.remove(HeaterInstanceInterface(host))
            for thread in self.__worker_thread_pool:
                if thread.get_instance_interface().get_host().equals(host):
                    thread.stop()
            return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_REMOVED

    # Returns list of all heater instances
    def get_heater_instances(self):
        return self.heater_instances
    
    def show_heater_instances(self):
        if len(self.heater_instances) == 0:
            print("No instances registered.")
        else:
            for heater_instance in self.heater_instances:
                print("Hostname: " + heater_instance.get_host().hostname + ", Username: " + heater_instance.get_host().username + ", Seed: " + str(heater_instance.get_seed()))
     
    def get_heater_instances_count(self) -> int:
        return len(self.heater_instances)

    def set_start_and_end_date(self, start_date: datetime, end_date: datetime):
        self.start_date = start_date
        self.end_date = end_date

    # Threading-------------------------------------------------------------------------------------------------

    # Changes simulation mode, by restarting all threads with new mode
    def change_simulation_mode(self, mode: Simulation.Mode):
        self._mode = mode
        self.stop_threads()
        self.instantiate_threads()
        self.start_threads()

    # Stops all running threads
    def stop_threads(self):
        for worker_thread in self.__worker_thread_pool:
            if worker_thread.get_stop_event_state() == False:
                worker_thread.stop()
    
    # Clears list of threads and creates new threads which are then appended to cleared list
    def instantiate_threads(self):
        self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
        for heater_instance_interface in self.heater_instances:
            self.__worker_thread_pool.append(HeaterInstanceWorkerThread(self.global_outcome, self._mode, heater_instance_interface, self.start_date, self.end_date, self.database_interface))

    # Starts threads in list of threads if state of instance is 'INITIALIZED'
    def start_threads(self):
        for worker_thread in self.__worker_thread_pool:
            if worker_thread.get_stop_event_state() == False:
                worker_thread.start()