# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

from typing import List, Optional

import aqt
from anki.errors import DeckIsFilteredError
from aqt import gui_hooks
from aqt.qt import *
from aqt.utils import (
    TR,
    HelpPage,
    HelpPageArgument,
    disable_help_button,
    getOnlyText,
    openHelp,
    restoreGeom,
    saveGeom,
    shortcut,
    showInfo,
    showWarning,
    tr,
)


class StudyDeck(QDialog):
    def __init__(
        self,
        mw: aqt.AnkiQt,
        names: Callable = None,
        accept: str = None,
        title: str = None,
        help: HelpPageArgument = HelpPage.KEYBOARD_SHORTCUTS,
        current: Optional[str] = None,
        cancel: bool = True,
        parent: Optional[QWidget] = None,
        dyn: bool = False,
        buttons: Optional[List[Union[str, QPushButton]]] = None,
        geomKey: str = "default",
    ) -> None:
        QDialog.__init__(self, parent or mw)
        self.mw = mw
        self.form = aqt.forms.studydeck.Ui_Dialog()
        self.form.setupUi(self)
        self.form.filter.installEventFilter(self)
        self.cancel = cancel
        gui_hooks.state_did_reset.append(self.onReset)
        self.geomKey = f"studyDeck-{geomKey}"
        restoreGeom(self, self.geomKey)
        disable_help_button(self)
        if not cancel:
            self.form.buttonBox.removeButton(
                self.form.buttonBox.button(QDialogButtonBox.Cancel)
            )
        if buttons is not None:
            for button_or_label in buttons:
                self.form.buttonBox.addButton(
                    button_or_label, QDialogButtonBox.ActionRole
                )
        else:
            b = QPushButton(tr(TR.ACTIONS_ADD))
            b.setShortcut(QKeySequence("Ctrl+N"))
            b.setToolTip(shortcut(tr(TR.DECKS_ADD_NEW_DECK_CTRLANDN)))
            self.form.buttonBox.addButton(b, QDialogButtonBox.ActionRole)
            qconnect(b.clicked, self.onAddDeck)
        if title:
            self.setWindowTitle(title)
        if not names:
            names_ = [
                d.name
                for d in self.mw.col.decks.all_names_and_ids(
                    include_filtered=dyn, skip_empty_default=True
                )
            ]
            self.nameFunc = None
            self.origNames = names_
        else:
            self.nameFunc = names
            self.origNames = names()
        self.name: Optional[str] = None
        self.ok = self.form.buttonBox.addButton(
            accept or tr(TR.DECKS_STUDY), QDialogButtonBox.AcceptRole
        )
        self.setWindowModality(Qt.WindowModal)
        qconnect(self.form.buttonBox.helpRequested, lambda: openHelp(help))
        qconnect(self.form.filter.textEdited, self.redraw)
        qconnect(self.form.list.itemDoubleClicked, self.accept)
        self.show()
        # redraw after show so position at center correct
        self.redraw("", current)
        self.exec_()

    def eventFilter(self, obj: QObject, evt: QEvent) -> bool:
        if isinstance(evt, QKeyEvent) and evt.type() == QEvent.KeyPress:
            new_row = current_row = self.form.list.currentRow()
            rows_count = self.form.list.count()
            key = evt.key()

            if key == Qt.Key_Up:
                new_row = current_row - 1
            elif key == Qt.Key_Down:
                new_row = current_row + 1
            elif (
                int(evt.modifiers()) & Qt.ControlModifier
                and Qt.Key_1 <= key <= Qt.Key_9
            ):
                row_index = key - Qt.Key_1
                if row_index < rows_count:
                    new_row = row_index

            if rows_count:
                new_row %= rows_count  # don't let row index overflow/underflow
            if new_row != current_row:
                self.form.list.setCurrentRow(new_row)
                return True
        return False

    def redraw(self, filt: str, focus: Optional[str] = None) -> None:
        self.filt = filt
        self.focus = focus
        self.names = [n for n in self.origNames if self._matches(n, filt)]
        l = self.form.list
        l.clear()
        l.addItems(self.names)
        if focus in self.names:
            idx = self.names.index(focus)
        else:
            idx = 0
        l.setCurrentRow(idx)
        l.scrollToItem(l.item(idx), QAbstractItemView.PositionAtCenter)

    def _matches(self, name: str, filt: str) -> bool:
        name = name.lower()
        filt = filt.lower()
        if not filt:
            return True
        for word in filt.split(" "):
            if word not in name:
                return False
        return True

    def onReset(self) -> None:
        # model updated?
        if self.nameFunc:
            self.origNames = self.nameFunc()
        self.redraw(self.filt, self.focus)

    def accept(self) -> None:
        saveGeom(self, self.geomKey)
        gui_hooks.state_did_reset.remove(self.onReset)
        row = self.form.list.currentRow()
        if row < 0:
            showInfo(tr(TR.DECKS_PLEASE_SELECT_SOMETHING))
            return
        self.name = self.names[self.form.list.currentRow()]
        QDialog.accept(self)

    def reject(self) -> None:
        saveGeom(self, self.geomKey)
        gui_hooks.state_did_reset.remove(self.onReset)
        QDialog.reject(self)

    def onAddDeck(self) -> None:
        row = self.form.list.currentRow()
        if row < 0:
            default = self.form.filter.text()
        else:
            default = self.names[self.form.list.currentRow()]
        n = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=default)
        n = n.strip()
        if n:
            try:
                did = self.mw.col.decks.id(n)
            except DeckIsFilteredError as err:
                showWarning(str(err))
                return
            # deck name may not be the same as user input. ex: ", ::
            self.name = self.mw.col.decks.name(did)
            # make sure we clean up reset hook when manually exiting
            gui_hooks.state_did_reset.remove(self.onReset)
            if self.mw.state == "deckBrowser":
                self.mw.deckBrowser.refresh()
            gui_hooks.sidebar_should_refresh_decks()
            QDialog.accept(self)
