import json
from typing import Type, Dict, List

from xvc_util import Point, Rectangle, Door, Room, Area


class Configuration(object):
    """
    Class to store configuration.
    """

    class TelegramBotSettings(object):
        """
        Class to store configuration for telegram bot.
        """
        token = None
        users = {}

    class XiaomiVacuumCleanerSettings(object):
        """
        Class to store configuration for Xiaomi Vacuum Cleaner.
        """
        simulation = None
        token = None
        ip_address = None


class ConfigurationParser(object):
    """
    Class to parse configuration.
    """

    def __init__(self, path: str) -> None:
        """
        Initialize an object of class ConfigurationParser.

        :param path: Path to configuration file.
        """
        self.__path = path
        self.__root = None
        self.reload()

    def reload(self) -> None:
        """
        Reloads the configuration file.
        """
        self.__root = json.load(open(self.__path))

    def parse_telegram_bot(self) -> Configuration.TelegramBotSettings:
        """
        Parses the telegram bot settings.

        :return: Telegram bot settings.
        """
        result = Configuration.TelegramBotSettings()
        result.token = self.__root['telegram_bot']['token']
        users = self.__root['telegram_bot']['users']
        for user in users:
            result.users[user['name']] = user['id']
        return result

    def parse_xiaomi_vacuum_cleaner_settings(self) -> Configuration.XiaomiVacuumCleanerSettings:
        """
        Parses the Xiaomi Vacuum Cleaner settings.

        :return: Xiaomi Vacuum Cleaner settings.
        """
        result = Configuration.XiaomiVacuumCleanerSettings()
        result.simulation = self.__root['xiaomi_vacuum_cleaner']['settings']['simulation']
        result.token = self.__root['xiaomi_vacuum_cleaner']['settings']['token']
        result.ip_address = self.__root['xiaomi_vacuum_cleaner']['settings']['ip_address']
        return result

    def parse_offset(self) -> Point:
        """
        Parses the x and y offset.

        :return: Offset point.
        """
        zero_point = self.__root['xiaomi_vacuum_cleaner']['zone_cleaning']['zero_point_offset']

        x = zero_point['x']
        y = zero_point['y']

        return Point(x, y)

    def __parse_rectangle(self, type_name: str, _type: Type[Rectangle]) -> Dict[str, Rectangle]:
        """
        Parses a rectangle type from the configuration.

        :param type_name: Name of the rectangle type.
        :param _type: Rectangle type.
        :return: Dictionary with the rectangles.
        """
        offset = self.parse_offset()

        result = dict()
        elements = self.__root['xiaomi_vacuum_cleaner']['zone_cleaning'][type_name]

        for element in elements:
            name = element['name']
            config_bottom_left = element['bottom_left']
            bottom_left = Point(config_bottom_left['x'] + offset.x,
                                config_bottom_left['y'] + offset.y)
            config_top_right = element['top_right']
            top_right = Point(config_top_right['x'] + offset.x,
                              config_top_right['y'] + offset.y)
            result[str(name.upper())] = _type(bottom_left, top_right, name)

        return result

    def parse_doors(self) -> Dict[str, Rectangle]:
        """
        Parses the doors from the configuration.

        :return: Dictionary with doors.
        """
        return self.__parse_rectangle('doors', Door)

    def parse_rooms(self) -> Dict[str, Rectangle]:
        """
        Parses the rooms from the configuration.

        :return: Dictionary with rooms.
        """
        return self.__parse_rectangle('rooms', Room)

    def parse_areas(self) -> Dict[str, Rectangle]:
        """
        Parses the areas from the configuration.

        :return: Dictionary with areas.
        """
        return self.__parse_rectangle('areas', Area)

    def parse_zones(self) -> Dict[str, List[Rectangle]]:
        """
        Parses the cleaning zones from the configuration.

        :return: Dictionary with name of zone and list of cleaning areas.
        """
        doors = self.parse_doors()
        rooms = self.parse_rooms()
        areas = self.parse_areas()

        zones = dict()
        config_zones = self.__root['xiaomi_vacuum_cleaner']['zone_cleaning']['zones']
        for config_zone in config_zones:
            name = config_zone['name']
            elements = list()

            for key in config_zone.keys():
                if key == 'doors':
                    for door in config_zone[key]:
                        reference = door['name']
                        if reference.upper() in doors:
                            elements.append(doors[reference.upper()])
                        else:
                            raise Exception('Door "{}" does not exist!'.format(reference))
                if key == 'rooms':
                    for room in config_zone[key]:
                        reference = room['name']
                        if reference.upper() in rooms:
                            elements.append(rooms[reference.upper()])
                        else:
                            raise Exception('Room "{}" does not exist!'.format(reference))
                if key == 'areas':
                    for area in config_zone[key]:
                        reference = area['name']
                        if reference.upper() in areas:
                            elements.append(areas[reference.upper()])
                        else:
                            raise Exception('Area "{}" does not exist!'.format(reference))

            zones[name.upper()] = elements

        return zones
