import yaml
import pygame
from typing import Tuple

"""
    TODO description
"""
class ConfigurationManager:
    
    CONFIG_FILE_LOCATION = "config.yaml"
    _configuration = []

    @classmethod
    def init(cls) -> None:
        with open(cls.CONFIG_FILE_LOCATION, "r") as yaml_file:
            cls._configuration = yaml.safe_load(yaml_file)

    @classmethod
    def get(cls, key: str, sub_key: str = None):
        if sub_key:
            return cls._configuration[key][sub_key]
        else:
            return cls._configuration[key]

"""
    TODO description
"""
class SoundManager:

    # Channels
    _theme_music_ch = None
    _option_change_sfx_ch = None
    _piece_rotate_sfx_ch = None
    _piece_set_sfx_ch = None
    _line_complete_sfx_ch = None
    _four_lines_complete_sfx_ch = None

    # Sounds
    _theme_music = None
    _option_change_sfx = None
    _piece_rotate_sfx = None
    _piece_set_sfx = None
    _line_complete_sfx = None
    _four_lines_complete_sfx = None

    @classmethod
    def init(cls) -> None:
        pygame.mixer.init()
        cls._theme_music_ch = pygame.mixer.Channel(0)
        cls._option_change_sfx_ch = pygame.mixer.Channel(1)
        cls._piece_rotate_sfx_ch = pygame.mixer.Channel(2)
        cls._piece_set_sfx_ch = pygame.mixer.Channel(3)
        cls._line_complete_sfx_ch = pygame.mixer.Channel(4)
        cls._four_lines_complete_sfx_ch = pygame.mixer.Channel(5)
        cls._level_up_sfx_ch = pygame.mixer.Channel(6)

        cls._theme_music = pygame.mixer.Sound(ConfigurationManager.get("sound", "theme-music"))
        cls._option_change_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "option-change"))
        cls._piece_rotate_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "piece-rotate"))
        cls._piece_set_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "piece-set"))
        cls._line_complete_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "line-complete"))
        cls._four_lines_complete_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "four-lines-complete"))
        cls._level_up_sfx = pygame.mixer.Sound(ConfigurationManager.get("sound", "level-up"))

    @classmethod
    def play_theme_music(cls) -> None:
        cls._theme_music_ch.set_volume(0.7)
        cls._theme_music_ch.play(cls._theme_music, -1)

    @classmethod
    def play_option_change_sfx(cls) -> None:
        cls._option_change_sfx_ch.set_volume(0.8)
        cls._option_change_sfx_ch.play(cls._option_change_sfx)

    @classmethod
    def play_piece_rotate_sfx(cls) -> None:
        cls._piece_rotate_sfx_ch.set_volume(0.7)
        cls._piece_rotate_sfx_ch.play(cls._piece_rotate_sfx)

    @classmethod
    def play_piece_set_sfx(cls) -> None:
        cls._piece_set_sfx_ch.play(cls._piece_set_sfx)

    @classmethod
    def play_line_complete_sfx(cls) -> None:
        cls._line_complete_sfx_ch.play(cls._line_complete_sfx)

    @classmethod
    def play_four_lines_complete_sfx(cls) -> None:
        cls._four_lines_complete_sfx_ch.play(cls._four_lines_complete_sfx)

    @classmethod
    def play_level_up_sfx(cls) -> None:
        cls._level_up_sfx_ch.play(cls._level_up_sfx)

"""
    TODO description
"""
class TextGenerator:

    _sheet = None
    _glyph_size = (-1, -1)
    _characters = { }

    @classmethod
    def init(cls, file: str, glyph_size: Tuple) -> None:
        cls._sheet = pygame.image.load(file)
        cls._glyph_size = glyph_size

        # load character positions in bitmap into the characters dictionary
        # letters
        cls._characters["A"] = (9 * glyph_size[0], 2 * glyph_size[1])
        cls._characters["B"] = (10 * glyph_size[0], 2 * glyph_size[1])
        cls._characters["C"] = (11 * glyph_size[0], 2 * glyph_size[1])
        cls._characters["D"] = (0 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["E"] = (1 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["F"] = (2 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["G"] = (3 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["H"] = (4 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["I"] = (5 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["J"] = (6 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["K"] = (7 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["L"] = (8 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["M"] = (9 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["N"] = (10 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["O"] = (11 * glyph_size[0], 3 * glyph_size[1])
        cls._characters["P"] = (0 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["Q"] = (1 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["R"] = (2 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["S"] = (3 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["T"] = (4 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["U"] = (5 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["V"] = (6 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["W"] = (7 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["X"] = (8 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["Y"] = (9 * glyph_size[0], 4 * glyph_size[1])
        cls._characters["Z"] = (10 * glyph_size[0], 4 * glyph_size[1])

        # numbers
        cls._characters["0"] = (4 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["1"] = (5 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["2"] = (6 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["3"] = (7 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["4"] = (8 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["5"] = (9 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["6"] = (10 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["7"] = (11 * glyph_size[0], 1 * glyph_size[1])
        cls._characters["8"] = (0 * glyph_size[0], 2 * glyph_size[1])
        cls._characters["9"] = (1 * glyph_size[0], 2 * glyph_size[1])

        # symbols
        cls._characters[":"] = (2 * glyph_size[0], 11 * glyph_size[1])
        cls._characters["_"] = (3 * glyph_size[0], 5 * glyph_size[1])
        cls._characters["."] = (2 * glyph_size[0], 1 * glyph_size[1])
    
    @classmethod
    def draw(cls, text: str, position: Tuple, surface: pygame.Surface) -> None:
        x_position = 0
        for char_ in text:
            if not char_.isspace():
                surface.blit(cls._sheet, (position[0] + x_position, position[1]), pygame.Rect(cls._characters[char_.upper()], (cls._glyph_size[0], cls._glyph_size[1])))
            x_position += cls._glyph_size[0]

"""
    TODO description
"""
class Controller:

    _keys_pressed = {}

    @classmethod
    def key_down(cls, key: int) -> bool:
        prior_pressed_state = False if key not in cls._keys_pressed else cls._keys_pressed[key]
        cls._keys_pressed[key] = pygame.key.get_pressed()[key]

        return cls._keys_pressed[key] and not prior_pressed_state

    @classmethod
    def key_pressed(cls, key: int) -> bool:
        return pygame.key.get_pressed()[key]