import sys

from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QFileDialog, QDesktopWidget
from PyQt5.QtWidgets import *
from PyQt5 import uic

from functools import partial
import os

from ui.prompt import Prompt
from ui.dialogs import dialog_error
from ui.keys import KeysView, KeysError

from ui.ui_panel import Ui_Panel
from ui.ui_panelkumite import Ui_PanelKumite
from ui.ui_panelkata import Ui_PanelKata

from ui.bars import MainMenubar, StatusBar
from ui.widgets import StartStopButton, TimerLabel
from ui.settings import Settings as UiSettings
from sql import competition_load
from settings import Settings
from ui.common import PenaltyGroup, change_color, string_abbrev

from ui.players import PlayerSound, PlayerError
from ui.buttons import Buttons

import logging
logger = logging.getLogger('panel')


class FightData(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)


class PanelSounds(PlayerSound):
    def __init__(self, panel, *args, **kwargs):
        self.panel = panel
        panel.signalTimer.connect(self.__timer)
        super().__init__(*args, **kwargs)
        self.__sounds = {}

    @pyqtSlot()
    def start(self):
        self.__sounds = {}
        for sound in Settings().sounds:
            v = sound.get()
            if (not type(v[0]) is int and not v[0].isnumeric()) or not v[1]:
                logger.error('Invalid sound setting, %s' % repr(v))
                continue
            logger.info('Loaded sound setting, %s' % repr(v))
            self.__sounds[float(v[0])] = v[1]

    def __timer(self, time, value):
        if self.__sounds.__contains__(time):
            fpath = self.__sounds[time]
            try:
                self.play(fpath)
            except PlayerError as err:
                # -- XXX show this error in the status bar
                logger.error('Player error, %s' % str(err))


class PanelControlBase:

    signalStarted = pyqtSignal(bool)
    signalTimer = pyqtSignal(float, str)
    signalPoints = pyqtSignal(str, int)
    signalSenshu = pyqtSignal(str, bool)
    signalPenalty = pyqtSignal(str, str, list)

    signalWinner = pyqtSignal(object, int)
    signalWinnerAnimation = pyqtSignal(str, bool)

    def __init__(self, panel, name, *args, **kwargs):
        super().__init__(*args, **kwargs)

        panel.signalCleanup.connect(self.cleanup)
        panel.signalReloadTime.connect(self.__reload_time)

        self.start_stop = StartStopButton(self.StartStopButton)
        self.start_stop.signalStatus.connect(self.start_stop_changed)

        self.timer_label = TimerLabel(self.tiempo_actual)
        self.timer_label.signalFinished.connect(self._select_winner)
        self.timer_label.signalTimer.connect(self.signalTimer)
        self.TimeUpButton.clicked.connect(partial(self.timer_label.increase, 1))
        self.TimeDownButton.clicked.connect(partial(self.timer_label.increase, -1))

        self.__timer = QTimer(self)
        self.__timer.timeout.connect(self.__ganadorAnimation)
        self.__toggle = False
        self.__ganador = None
        self.__ganadorAnimation(reset=True)
        self.__fight = None
        self.total_tiempo = 0

        self._aka = PenaltyGroup(self, 'aka')
        self._ao = PenaltyGroup(self, 'ao')
        self._aka.signalPenalty.connect(self._handle_penalty)
        self._ao.signalPenalty.connect(self._handle_penalty)

        self._enable_controls(False)
        self._log = logging.getLogger(name)

    @property
    def has_winner(self):
        return self.__ganador is not None

    @property
    def use_timer(self):
        """
        Per default always use the timer, but overwrite for other case
        """
        return True

    @property
    def isRunning(self):
        return not self.start_stop.isStopped

    @pyqtSlot(object, object)
    def start_stop_changed(self, old, new):
        self._log.debug("Button changed: %s => %s" % (repr(old), repr(new)))
        if old == self.start_stop.Status.STOPPED:
            if self.total_tiempo == 0:
                dialog_error(self, "Error de inicio", "Aún no ha configurado el tiempo.")
                return

            if self.use_timer:
                self.timer_label.start()

            self.__ganador = None
            self._enable_controls(True)
            self.signalStarted.emit(True)
        elif new == self.start_stop.Status.PAUSED:
            if self.use_timer:
                self.timer_label.setPaused(True)
        elif new == self.start_stop.Status.RUNNING:
            if self.use_timer:
                self.timer_label.setPaused(False)

    @pyqtSlot()
    def __ganadorAnimation(self, reset=False):
        if reset:
            self.__toggle = False
            change_color(self.AkaBackground, Settings().panel['aka'].get())
            change_color(self.AoBackground, Settings().panel['ao'].get())
            return

        self.__toggle ^= True

        if self.__ganador == "aka":
            widget = self.AkaBackground
            color = Settings().panel['aka_off' if self.__toggle else 'aka'].get()
        else:
            widget = self.AoBackground
            color = Settings().panel['ao_off' if self.__toggle else 'ao'].get()

        change_color(widget, color)

        if self.has_winner:
            self.signalWinnerAnimation.emit(self.__ganador, self.__toggle)

    def __reload_time(self, minutes, seconds):
        self.total_tiempo = 60 * minutes + seconds
        # -- If the match was already stopped, then enable to start a new one here
        # -- but if is paused or running, dont touch anything. Also stop the animation.
        if self.start_stop.isStopped:
            self.start_stop.enable()
            self.__timer.stop()
            self.__ganadorAnimation(reset=True)

        self.timer_label.reload(self.total_tiempo)

    @pyqtSlot(bool)
    def cleanup(self, full):
        self.puntos_aka.setText("0")
        self.puntos_ao.setText("0")

        self._cleanup(full)
        self.__timer.stop()
        self.__ganadorAnimation(reset=True)
        self._aka.reset()
        self._ao.reset()

        if full:
            self.AoMemberLabel.setText('---')
            self.AoDojoLabel.setText('---')
            self.AkaMemberLabel.setText('---')
            self.AkaDojoLabel.setText('---')

    def load_fight(self, fight):
        first = fight.first.record
        second = fight.second.record
        category = first.category
        self._log.info('Loaded new fight for category %s' % category.label)
        self.AkaMemberLabel.setText(first.item.label)
        self.AkaDojoLabel.setText(first.item.dojo.name)
        self.AoMemberLabel.setText(second.item.label)
        self.AoDojoLabel.setText(second.item.dojo.name)
        self.cleanup(False) # Dont reset the member labels
        self.__fight = fight
        self.__ganador = None

    def _set_winner(self, nombre):
        self.__ganador = nombre

        # -- When no winner, means tie
        if nombre is None:
            self.signalWinner.emit(None, 0)
            return

        self.__timer.start(800)
        winner = 1 if nombre == 'aka' else 2
        if self.__fight:
            self.signalWinner.emit(self.__fight, winner)
        else:
            self.signalWinner.emit(nombre, winner)

    def set_manual_winner(self, team):
        self._set_winner(team)

    def fight_data(self):
        return FightData(aka=(self.AkaMemberLabel.text(), self.AkaDojoLabel.text()),
                         ao=(self.AoMemberLabel.text(), self.AoDojoLabel.text()))

    def get_points(self):
        return {'aka': int(self.puntos_aka.text()), 'ao': int(self.puntos_ao.text())}


class PanelControlKumite(QWidget, Ui_PanelKumite, PanelControlBase):

    def __init__(self, panel, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        Ui_PanelKumite.setupUi(self, self)
        PanelControlBase.__init__(self, panel, 'kumite')

        # -- DONT connect the signal due concurrency with Panel()
        #keys.signalFight.connect(self.load_fight)

        self.senshu_aka.stateChanged.connect(lambda: self.cb_senshu("aka"))
        self.senshu_ao.stateChanged.connect(lambda: self.cb_senshu("ao"))

        self.tresAo.clicked.connect(lambda: self.cb_puntaje("ao", 3))
        self.dosAo.clicked.connect(lambda: self.cb_puntaje("ao", 2))
        self.unoAo.clicked.connect(lambda: self.cb_puntaje("ao", 1))

        self.tresAka.clicked.connect(lambda: self.cb_puntaje("aka", 3))
        self.dosAka.clicked.connect(lambda: self.cb_puntaje("aka", 2))
        self.unoAka.clicked.connect(lambda: self.cb_puntaje("aka", 1))

        self.Aoquitar.clicked.connect(lambda: self.cb_quitar(self.puntos_ao, "ao"))
        self.Akaquitar.clicked.connect(lambda: self.cb_quitar(self.puntos_aka, "aka"))

    def _cleanup(self, full):
        self.senshu_ao.setChecked(False)
        self.senshu_aka.setChecked(False)

    def cb_puntaje(self, team, value):
        # Si el oponente no tiene puntos, significa que este es el primer
        # puntaje, y por esta razon se asigna el senshu
        oponent = self.puntos_aka if team == "ao" else self.puntos_ao
        if int(oponent.text()) == 0:
            if team == "ao":
                self.senshu_ao.setChecked(True)
            else:
                self.senshu_aka.setChecked(True)
            self.signalSenshu.emit(team, 1)

        widget = self.puntos_ao if team == "ao" else self.puntos_aka
        points = int(widget.text()) + value
        widget.setText(str(points))
        self.signalPoints.emit(team, points)

        if points >= 8:
            total = list(self.get_points().values())
            if abs(total[0] - total[1]) >= 8:
                self._select_winner()

    def cb_quitar(self, widget, team):
        resta_actual = int(widget.text()) - 1
        if resta_actual >= 0:
            widget.setText(str(resta_actual))
            self.signalPoints.emit(team, resta_actual)

    def _enable_controls(self, enable):
        items = ['tresAo', 'dosAo', 'unoAo', 'senshu_ao', 'Aoquitar',
                 'tresAka', 'dosAka', 'unoAka', 'senshu_aka', 'Akaquitar',
                 'start_stop', 'TimeDownButton', 'TimeUpButton', 'KOButton']
        for i in items:
            widget = getattr(self, i)
            widget.setEnabled(enable)
        self._aka.setEnabled(enable)
        self._ao.setEnabled(enable)

    def _handle_penalty(self, name, widget, team, active):
        if name == 'h' and active:
            self._select_winner()

        actives = self._aka.status if team == 'aka' else self._ao.status
        self.signalPenalty.emit(widget, team, actives)

    def cb_senshu(self, team):
        widget = self.senshu_ao if team == "ao" else self.senshu_aka
        self.signalSenshu.emit(team, widget.isChecked())

    def _select_winner(self):
        if self.has_winner:
            self._log.warning('Winner already computed, skipping new one')
            return

        self.timer_label.setPaused(True)
        self.start_stop.disable()
        self.signalStarted.emit(False)
        self._enable_controls(False)

        if self.h_ao.isChecked():
            self._set_winner("aka")
        elif self.h_aka.isChecked():
            self._set_winner("ao")
        else:
            ao = (int(self.puntos_ao.text()))
            aka = (int(self.puntos_aka.text()))
            if aka > ao:
                self._set_winner("aka")
            elif ao > aka:
                self._set_winner("ao")
            else:
                # -- Check cual competidor tiene el seshu en caso de empata
                seshu_ao = self.senshu_ao.isChecked()
                seshu_aka = self.senshu_aka.isChecked()
                if seshu_ao == True:
                    self._set_winner("ao")
                if seshu_aka == True:
                    self._set_winner("aka")

        # -- If no winner, assume it's tie
        if not self.has_winner:
            logger.debug('Tie detected')
            self._set_winner(None)


class PanelControlKata(QWidget, Ui_PanelKata, PanelControlBase):

    def __init__(self, panel, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        Ui_PanelKata.setupUi(self, self)
        PanelControlBase.__init__(self, panel, 'kata')

        self.RefereesButton.clicked.connect(self.__referees_cb)
        self.BunkaiButton.clicked.connect(self.__bunkai_cb)
        self.signalStarted.connect(self.__started_cb)
        self.signalWinner.connect(self.__winner_cb)

    @property
    def use_timer(self):
        return self.BunkaiButton.isChecked()

    @pyqtSlot(object, int)
    def __winner_cb(self, *args):
        self.RefereesButton.setEnabled(True)
        self.BunkaiButton.setEnabled(True)

    @pyqtSlot(bool)
    def __started_cb(self, started):
        if started:
            self.RefereesButton.setEnabled(False)
            self.BunkaiButton.setEnabled(False)

    @pyqtSlot()
    def __bunkai_cb(self):
        yes = self.BunkaiButton.isChecked()
        self.BunkaiButton.setText('Sí' if yes else 'No')

    @pyqtSlot()
    def __referees_cb(self):
        count = self.referees_count
        self.hc_ao.setVisible(count == 5)
        self.h_ao.setVisible(count == 5)
        self.hc_aka.setVisible(count == 5)
        self.h_aka.setVisible(count == 5)

        self.RefereesButton.setText(str(count))

    @property
    def referees_count(self):
        return 3 if self.RefereesButton.isChecked() else 5

    def _enable_controls(self, enable):
        self._aka.setEnabled(enable)
        self._ao.setEnabled(enable)
        # IMPORTANT dont touch the RefereesButton in this callback

    def _handle_penalty(self, name, widget, team, active):
        self._select_winner(team)

    def _select_winner(self, team=None):
        if self.has_winner:
            self._log.warning('Winner already computed, skipping new one')
            return

        self.timer_label.setPaused(True)
        self.start_stop.disable()
        self.signalStarted.emit(False)
        self._enable_controls(False)

        if team is not None:
            actives = self._aka.status if team == 'aka' else self._ao.status
            actives = actives[0:self.referees_count]
            actives.reverse()
            points = self.referees_count - actives.index(True)
            threshold = int(self.referees_count / 2)
            self._log.info("Team %s actives %s points %i threshold %i" % (team, repr(actives),
                                                                          points, threshold))

            winner = team if points > threshold else self.__get_loser(team)
            self.__set_points(team, points)
            self.__set_points(self.__get_loser(team), self.referees_count - points)
            self._set_winner(winner)

        # -- If no winner, assume it's tie
        if not self.has_winner:
            self._log.debug('Tie detected')
            self._set_winner(None)

    def __get_loser(self, winner):
        return 'ao' if winner == 'aka' else 'aka'

    def __set_points(self, team, value):
        w = self.puntos_aka if team == 'aka' else self.puntos_ao
        w.setText(str(value))
        self.signalPoints.emit(team, value)

    def _cleanup(self, full):
        pass


class _Panel(QMainWindow, Ui_Panel):

    signalStarted = pyqtSignal(bool)
    # -- Emitted when a new time is reloaded through ResetTimeButton
    signalReloadTime = pyqtSignal(int, int)
    signalPoints = pyqtSignal(str, int)
    signalSenshu = pyqtSignal(str, bool)
    signalPenalty = pyqtSignal(str, str, list)
    signalCleanup = pyqtSignal(bool)
    signalTatami = pyqtSignal(int)

    signalFightData = pyqtSignal(object)
    signalDatabase = pyqtSignal(str, object)
    signalTimer = pyqtSignal(float, str)
    signalWinner = pyqtSignal(object, int)
    signalWinnerAnimation = pyqtSignal(str, bool)

    def __init__(self, *args, **kwargs):
        QMainWindow.__init__(self)
        Ui_Panel.setupUi(self, self)
        self.setWindowIcon(QIcon(":images/logo.png"))

        self.ResetTimeButton.clicked.connect(self.__reset_time)
        self.ResetMatchButton.clicked.connect(self.__reset_match)
        self.TatamiButton.clicked.connect(self.tatami_changed)

        self.cerrarprograma.clicked.connect(self.close_program)

        self.AoWinnerButton.clicked.connect(partial(self.__manual_winner,
                                                    self.AoWinnerButton, 'ao'))
        self.AkaWinnerButton.clicked.connect(partial(self.__manual_winner,
                                                     self.AkaWinnerButton, 'aka'))

        self.signalDatabase.connect(self.changed_database)

        self.keys = KeysView(self, parent=None)
        self.keys.signalFight.connect(self.load_fight)
        self.keys.ResetTimeButton.clicked.connect(self.__reset_time)
        self.KeysButton.clicked.connect(self.show_keys)

        self.prompt = Prompt(self)

        self.statusbar = StatusBar(self, self.StatusBar, self)
        self.sounds = PanelSounds(self)

        self.__controls = {}
        self.__build_control('kata', PanelControlKata)
        self.__build_control('kumite', PanelControlKumite)
        # -- Per default view the kumite control
        self.__switch_control('kumite')

        self.__fight = None
        self.__settings = None
        self.__init_prompt()
        self.changed_database(None, None)

        self.__buttons = Buttons(self)

    def __build_control(self, name, constructor):
        control = constructor(self, parent=self.ControlWidget)
        control.signalTimer.connect(self.signalTimer)
        control.signalStarted.connect(self.__fight_started)
        control.signalSenshu.connect(self.signalSenshu)
        control.signalPoints.connect(self.signalPoints)
        control.signalPenalty.connect(self.signalPenalty)
        control.signalWinner.connect(self.__cb_winner)
        control.signalWinnerAnimation.connect(self.signalWinnerAnimation)
        self.__controls[name] = control

    @pyqtSlot(object, int)
    def __cb_winner(self, item, team):
        if item is not None:
            self.signalWinner.emit(item, team)
        else:
            # -- Tie situation, enable manual selection
            logger.info('No winner, enabling manual buttons')
            self.AoWinnerButton.setEnabled(True)
            self.AkaWinnerButton.setEnabled(True)

    def __manual_winner(self, widget, team):
        logger.warning('Selected manual winner %s' % team)
        self.AoWinnerButton.setEnabled(False)
        self.AkaWinnerButton.setEnabled(False)
        self.control.set_manual_winner(team)

    @pyqtSlot(bool)
    def __fight_started(self, started):
        """
        When the fight is started, do these operations:
        - start the sounds widget
        - enable the ResetMatchButtono
        - and emit signal for other widgets
        """
        if started:
            self.sounds.start()

        self.ResetMatchButton.setEnabled(started)
        self.signalStarted.emit(started)

    @pyqtSlot(str)
    def load_database(self, fpath, migrate=True):
        if not os.path.exists(fpath):
            dialog_error(self, 'Error abriendo', 'La base de datos no existe.')
            return

        try:
            competition = competition_load(opts={'db':fpath})
            self.keys.load_categories()
            self.signalDatabase.emit(fpath, competition)
        except Exception as err:
            dialog_error(self, 'Error base datos', 'La base de datos no pudo ser leída.')
            logger.critical('Failed database read, %s' % err)

    def changed_database(self, fpath, competition):
        self.KeysButton.setEnabled(True if fpath else False)
        self.CategoryEdit.setReadOnly(True if fpath else False)
        self.CategoryEdit.setText('')
        self.CategoryEdit.setFocusPolicy(Qt.NoFocus if fpath else Qt.StrongFocus)

        self.ModalityEdit.setReadOnly(True if fpath else False)
        self.ModalityEdit.setText('')
        self.ModalityEdit.setFocusPolicy(Qt.NoFocus if fpath else Qt.StrongFocus)
        self.__emit_fight_data()

        # -- Change window title
        if competition:
            self.setWindowTitle("Kumite App - %s" % competition.name)
        else:
            self.setWindowTitle("Kumite App")

        self.__fight = None

    @pyqtSlot()
    def __reset_match(self, full=False):
        self.AoWinnerButton.setEnabled(False)
        self.AkaWinnerButton.setEnabled(False)
        self.signalCleanup.emit(full)

    @pyqtSlot()
    def open_dialog(self):
        EXTENSIONS = "SQLite file (*.db *.sqlite *.sqlite3)"
        options = QFileDialog.Options() | QFileDialog.DontUseNativeDialog
        fpath, _ = QFileDialog.getOpenFileName(self, "Open Database", "",
                                               EXTENSIONS, options=options)
        if fpath:
            self.load_database(fpath)

    def open_settings(self):
        if not self.__settings:
            self.__settings = UiSettings(self)
            self.__settings.signalChanged.connect(self.prompt.cb_refresh)
        self.__settings.show()
        self.__settings.activateWindow()

    @pyqtSlot()
    def close_database(self):
        logger.warning('Request to close database')
        self.keys.close()
        self.signalDatabase.emit('', None)

    @pyqtSlot()
    def __reset_time(self):
        if error := self.fields_error():
            dialog_error(self, 'Error de inicio', error)
            return

        logger.info('Reset time, database %i running %i' % (self.has_database, self.control.isRunning))
        if self.has_database and not self.control.isRunning:
            if Settings().keys['automatic'].get():
                logger.info('Loading next fight due automatic selection')
                try:
                    self.keys.load_fight()
                except KeysError as err:
                    dialog_error(self, 'Error en llaves',
                             'No se puedo seleccionar nueva llave:\n%s.' % err)
                    return
            else:
                if self.control.has_winner:
                    dialog_error(self, 'Error de inicio',
                                 'Falta seleccionar una nueva pelea en la pantalla de llaves.')
                    return

        try:
            minutes = int(self.TimeMinutes.text())
            seconds = int(self.TimeSeconds.text())
        except:
            dialog_error(self, "Error de inicio", "No se pudo convertir valores de tiempo.")
            return

        self.signalReloadTime.emit(minutes, seconds)

        # -- This is needed when the data is loaded from keys and have to emit the
        # -- signal to load the new data for the prompt
        self.__emit_fight_data()

        if self.control.has_winner:
            # -- Cleanup but dont remove the already set members
            self.signalCleanup.emit(False)

        if not self.control.isRunning:
            self.__switch_control(self.ModalityEdit.text())

        if seconds < 10:
            self.TimeSeconds.setText(f'0{seconds}')

    def __emit_fight_data(self):
        data = self.control.fight_data()
        data.category = self.CategoryEdit.text()
        data.modality = self.ModalityEdit.text()
        logger.info(data.__dict__)
        self.signalFightData.emit(data)

    @pyqtSlot()
    def show_prompt(self, screen=None):
        if screen is None:
            display = self.PromptScreenCombo.currentData() - 1
        else:
            if (display := self.PromptScreenCombo.findData(screen)) == -1:
                dialog_error(self, 'Error table de puntuación', 'No existe monitor con el index %i.' % screen)
                return

            self.PromptScreenCombo.setCurrentIndex(screen - 1)

        logger.info("Selected prompt for display %i" % display)
        monitor = QDesktopWidget().screenGeometry(display)
        self.prompt.move(monitor.left(), monitor.top())
        self.prompt.showFullScreen()
        # This functions should be called once the full screen is loaded otherwise the calculations of
        # the widgets geometry might be wrong!
        self.prompt.cb_load_settings()
        self.prompt.refresh()

    def __hide_prompt(self):
      self.prompt.close()

    def __load_prompt_screens(self):
        # Load the number of screens
        self.PromptScreenCombo.clear()
        for nr in range(1, QDesktopWidget().screenCount() + 1):
            self.PromptScreenCombo.addItem(str(nr), nr)

    def __init_prompt(self):
        self.PromptShowButton.clicked.connect(self.show_prompt)
        self.PromptHideButton.clicked.connect(self.__hide_prompt)
        self.PromptRefreshButton.clicked.connect(self.__load_prompt_screens)
        self.__load_prompt_screens()

    def tatami_changed(self):
      tatami = self.TatamiEdit.text()
      if not tatami.isdigit():
          dialog_error(self, 'Tatami invalido',
                       'Valor ingresado %s es invalido.' % tatami)
          self.TatamiEdit.setText(getattr(self, 'last_tatami', None))
      else:
          self.last_tatami = tatami
          self.signalTatami.emit(int(tatami))

    def close_program(self):
        self.prompt.close()
        QCoreApplication.quit()

    def __switch_control(self, modality):
        control = self.__controls[modality.lower()]
        control.raise_()
        self.control = control

    def load_fight(self, fight):
        category = fight.first.record.category
        self.CategoryEdit.setText(category.label)
        self.ModalityEdit.setText(category.modality_text)

        self.__switch_control(category.modality_text)
        self.control.load_fight(fight)
        self.__emit_fight_data()

        # Only cleanup the match data if the fight was already done
        if not self.control.isRunning:
            self.__reset_match(full=False)

        # This slot is called when a new fight is loaded in automatic and manual mode.
        # In automatic mode this slot is called after resettime button was pressed
        # In manual mode have to call resettime function to clean all fields
        if self.has_database and not Settings().keys['automatic'].get():
            logger.debug('Cleaning up for new fight due manual mode')
            self.__reset_time()

    def fields_error(self):
        error = None
        if self.TatamiEdit.text() == "":
            # -- Tatami is always mandatory
            error = 'Falta ingresar Tatami'

        if not Settings().keys['automatic'].get() or not self.has_database:
            if self.CategoryEdit.text() == "":
                error = 'Falta ingresar Categoría o seleccionar llave'
            elif self.ModalityEdit.text() == "":
                error = 'Falta ingresar Modalidad o seleccionar llave'
            elif self.ModalityEdit.text().lower() not in ('kata', 'kumite'):
                error = 'Modalidad es invalido. Valores permitidos: kata o kumite'

        return error

    @property
    def has_database(self):
        """
        Returns true if a database was loaded by checking if keys has categories
        """
        return self.keys.has_categories

    def show_keys(self):
        if self.has_database:
            self.keys.show()
            self.keys.activateWindow()

    def closeEvent(self, ev):
        self.close_program()

    def resizeEvent(self, ev):
        self.setFixedSize(self.width(), self.height())
        super().resizeEvent(ev)


class Panel(object):

    _instance = None

    def __new__(cls, *args, **kwargs):
        if not Panel._instance:
            Panel._instance = _Panel(*args, **kwargs)
        return Panel._instance
