from typing import Optional, List

from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import QFrame, QMessageBox, QToolBox

from rare.models.game import RareGame
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
from rare.widgets.image_widget import ImageWidget, ImageSize
from rare.widgets.side_tab import SideTabContents
from rare.utils.misc import widget_object_name, icon


class GameDlcWidget(QFrame):
    def __init__(self, rgame: RareGame, rdlc: RareGame, parent=None):
        super(GameDlcWidget, self).__init__(parent=parent)
        self.ui = Ui_GameDlcWidget()
        self.ui.setupUi(self)
        self.setObjectName(widget_object_name(self, rdlc.app_name))
        self.rgame = rgame
        self.rdlc = rdlc

        self.image = ImageWidget(self)
        self.image.setFixedSize(ImageSize.Icon)
        self.ui.dlc_layout.insertWidget(0, self.image)

        self.ui.dlc_name.setText(rdlc.app_title)
        self.ui.version.setText(rdlc.version)
        self.ui.app_name.setText(rdlc.app_name)

        self.image.setPixmap(rdlc.pixmap)

        self.__update()
        rdlc.signals.widget.update.connect(self.__update)

    @pyqtSlot()
    def __update(self):
        self.ui.action_button.setEnabled(self.rdlc.is_idle)


class InstalledGameDlcWidget(GameDlcWidget):
    uninstalled = pyqtSignal(RareGame)

    def __init__(self, rgame: RareGame, rdlc: RareGame, parent=None):
        super(InstalledGameDlcWidget, self).__init__(rgame=rgame, rdlc=rdlc, parent=parent)
        # lk: set object names for CSS properties
        self.ui.action_button.setObjectName("UninstallButton")
        self.ui.action_button.clicked.connect(self.uninstall_dlc)
        self.ui.action_button.setText(self.tr("Uninstall DLC"))
        self.ui.action_button.setIcon(icon("ri.uninstall-line"))
        # lk: don't reference `self.rdlc` here because the object has been deleted
        rdlc.signals.game.uninstalled.connect(self.__uninstalled)

    @pyqtSlot()
    def __uninstalled(self):
        self.uninstalled.emit(self.rdlc)

    def uninstall_dlc(self):
        self.rdlc.uninstall()


class AvailableGameDlcWidget(GameDlcWidget):
    installed = pyqtSignal(RareGame)

    def __init__(self, rgame: RareGame, rdlc: RareGame, parent=None):
        super(AvailableGameDlcWidget, self).__init__(rgame=rgame, rdlc=rdlc, parent=parent)
        # lk: set object names for CSS properties
        self.ui.action_button.setObjectName("InstallButton")
        self.ui.action_button.clicked.connect(self.install_dlc)
        self.ui.action_button.setText(self.tr("Install DLC"))
        self.ui.action_button.setIcon(icon("ri.install-line"))

        # lk: don't reference `self.rdlc` here because the object has been deleted
        rdlc.signals.game.installed.connect(self.__installed)

    @pyqtSlot()
    def __installed(self):
        self.installed.emit(self.rdlc)

    def install_dlc(self):
        if not self.rgame.is_installed:
            QMessageBox.warning(
                self,
                self.tr("Error"),
                self.tr("Base Game is not installed. Please install {} first").format(self.rgame.app_title),
            )
            return
        self.rdlc.install()


class GameDlc(QToolBox, SideTabContents):

    def __init__(self, parent=None):
        super(GameDlc, self).__init__(parent=parent)
        self.implements_scrollarea = True
        self.ui = Ui_GameDlc()
        self.ui.setupUi(self)
        self.core = LegendaryCoreSingleton()
        self.signals = GlobalSignalsSingleton()

        self.rgame: Optional[RareGame] = None

    def list_installed(self) -> List[InstalledGameDlcWidget]:
        return self.ui.installed_dlc_container.findChildren(InstalledGameDlcWidget, options=Qt.FindDirectChildrenOnly)

    def list_available(self) -> List[AvailableGameDlcWidget]:
        return self.ui.available_dlc_container.findChildren(AvailableGameDlcWidget, options=Qt.FindDirectChildrenOnly)

    def get_installed(self, app_name: str) -> Optional[InstalledGameDlcWidget]:
        return self.ui.installed_dlc_container.findChild(
            InstalledGameDlcWidget,
            name=widget_object_name(InstalledGameDlcWidget, app_name),
            options=Qt.FindDirectChildrenOnly
        )

    def get_available(self, app_name: str) -> Optional[AvailableGameDlcWidget]:
        return self.ui.available_dlc_container.findChild(
            AvailableGameDlcWidget,
            name=widget_object_name(AvailableGameDlcWidget, app_name),
            options=Qt.FindDirectChildrenOnly
        )

    def update_installed_page(self):
        have_installed = bool(self.list_installed())
        self.ui.installed_dlc_label.setVisible(not have_installed)
        self.ui.installed_dlc_container.setVisible(have_installed)
        if not have_installed:
            self.setCurrentWidget(self.ui.available_dlc_page)

    def update_available_page(self):
        have_available = bool(self.list_available())
        self.ui.available_dlc_label.setVisible(not have_available)
        self.ui.available_dlc_container.setVisible(have_available)
        if not have_available:
            self.setCurrentWidget(self.ui.installed_dlc_page)

    def append_installed(self, rdlc: RareGame):
        self.ui.installed_dlc_label.setVisible(False)
        self.ui.installed_dlc_container.setVisible(True)
        a_widget: AvailableGameDlcWidget = self.get_available(rdlc.app_name)
        if a_widget is not None:
            self.ui.available_dlc_container.layout().removeWidget(a_widget)
            a_widget.deleteLater()
        i_widget: InstalledGameDlcWidget = InstalledGameDlcWidget(
            self.rgame, rdlc, self.ui.installed_dlc_container
        )
        i_widget.destroyed.connect(self.update_installed_page)
        i_widget.uninstalled.connect(self.append_available)
        self.ui.installed_dlc_container.layout().addWidget(i_widget)

    def append_available(self, rdlc: RareGame):
        self.ui.available_dlc_label.setVisible(False)
        self.ui.available_dlc_container.setVisible(True)
        i_widget: InstalledGameDlcWidget = self.get_installed(rdlc.app_name)
        if i_widget is not None:
            self.ui.available_dlc_container.layout().removeWidget(i_widget)
            i_widget.deleteLater()
        a_widget: AvailableGameDlcWidget = AvailableGameDlcWidget(
            self.rgame, rdlc, self.ui.available_dlc_container
        )
        a_widget.destroyed.connect(self.update_available_page)
        a_widget.installed.connect(self.append_installed)
        self.ui.available_dlc_container.layout().addWidget(a_widget)

    def update_dlcs(self, rgame: RareGame):
        self.rgame = rgame
        self.set_title.emit(self.rgame.app_title)

        for i_widget in self.list_installed():
            self.ui.installed_dlc_container.layout().removeWidget(i_widget)
            i_widget.deleteLater()

        for a_widget in self.list_available():
            self.ui.available_dlc_container.layout().removeWidget(a_widget)
            a_widget.deleteLater()

        for dlc in sorted(self.rgame.owned_dlcs, key=lambda x: x.app_title):
            if dlc.is_installed:
                self.append_installed(rdlc=dlc)
            else:
                self.append_available(rdlc=dlc)

        if not self.list_available():
            self.setCurrentWidget(self.ui.installed_dlc_page)
        if not self.list_installed():
            self.setCurrentWidget(self.ui.available_dlc_page)
