import json
import logging
import os
from typing import List, Dict

from flask import Flask

from dashboard_leaf.logic import Constants
from dashboard_leaf.logic.page.PageInstance import PageInstance, TileLayoutSettings
from dashboard_leaf.logic.tile import TileScheduler
from dashboard_leaf.logic.tile.TileRegistry import TileRegistry

LOGGER = logging.getLogger(Constants.APP_NAME)


class PageManager:
    """
    Handles the pages and provides access to the corresponding page instances.
    Creates and registers tiles according to settings.
    """

    def __init__(self, settingsFolder: str, tileScheduler: TileScheduler, flaskApp: Flask):
        self._settingsFolder = settingsFolder
        self._tileScheduler = tileScheduler
        self._flaskApp = flaskApp

        self._pageSettingsPath = os.path.join(self._settingsFolder, 'pageSettings.json')
        self._pageSettings = self.__load_settings()
        self._pageInstances = self.__create_page_instances()

    def __load_settings(self) -> List[Dict]:
        if not os.path.exists(self._pageSettingsPath):
            self.__save_settings()

        with open(self._pageSettingsPath, encoding='UTF-8') as f:
            return json.load(f)

    def __save_settings(self):
        with open(self._pageSettingsPath, 'w', encoding='UTF-8') as f:
            json.dump(self._pageSettings, f)

    def __create_page_instances(self) -> Dict:
        pageInstances = {}
        for pageSetting in self._pageSettings:
            uniquePageName = pageSetting['uniqueName']

            tileLayouts = {}
            for tileSettings in pageSetting['tiles']:
                tileLayouts[tileSettings['uniqueName']] = TileLayoutSettings(x=tileSettings['x'],
                                                                             y=tileSettings['y'],
                                                                             width=tileSettings['width'],
                                                                             height=tileSettings['height'])
                self.__register_tile(uniquePageName, tileSettings)

            pageInstance = PageInstance(uniquePageName, tileLayouts)
            pageInstances[uniquePageName] = pageInstance
        return pageInstances

    def __register_tile(self, uniquePageName: str, tileSettings: Dict):
        tileType = tileSettings['tileType']
        if tileType not in TileRegistry.get_instance().get_all_available_implementation_types():
            LOGGER.error(f'Skipping unknown tile with type "{tileType}"')
            return

        tileRegistry = TileRegistry.get_instance()
        tile = tileRegistry.get_implementation_by_type_name(tileType)(uniqueName=tileSettings['uniqueName'],
                                                                      settings=tileSettings['settings'],
                                                                      intervalInSeconds=tileSettings[
                                                                          'intervalInSeconds'])
        self._tileScheduler.register_tile(uniquePageName, tile)
        self._flaskApp.register_blueprint(tile.construct_blueprint(pageName=uniquePageName))

    def save_and_load(self):
        self.__save_settings()
        self._pageSettings = self.__load_settings()
        self._pageInstances = self.__create_page_instances()

    def add_page(self, index: int, pageName: str, uniqueName: str, settings: Dict):
        self._pageSettings.insert(index, {'pageName': pageName, 'uniqueName': uniqueName, 'settings': settings})
        self.save_and_load()

    def remove_page(self, index: int):
        del self._pageSettings[index]
        self.save_and_load()

    def get_all_available_page_names(self):
        return [page['uniqueName'] for page in self._pageSettings]

    def get_page_instance_by_name(self, name: str):
        return self._pageInstances[name]