#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.

from fenrirscreenreader.core.i18n import _


class SpeechHistoryManager:
    def __init__(self):
        self.env = None
        self.history = []
        self.curr_index = -1
        self.active = False
        self.bindings_backup = None
        self.raw_bindings_backup = None

    def initialize(self, environment):
        self.env = environment

    def shutdown(self):
        self.set_active(False)

    def is_active(self):
        return self.active

    def add_text(self, text):
        if self.active:
            return False
        if not isinstance(text, str):
            return False
        if text == "":
            return False
        text_key = self._get_history_key(text)
        if text_key == "":
            return False
        if text_key in [self._get_history_key(item) for item in self.history]:
            return False
        history_size = self._get_history_size()
        if history_size <= 0:
            return False
        self.history.insert(0, text)
        del self.history[history_size:]
        if self.curr_index >= len(self.history):
            self.curr_index = len(self.history) - 1
        return True

    def open_history(self):
        if not self.history:
            self.env["runtime"]["OutputManager"].present_text(
                _("speech history empty"), interrupt=True
            )
            return False
        self.curr_index = -1
        self.set_active(True)
        self.env["runtime"]["OutputManager"].present_text(
            _("Speech history"), interrupt=True
        )
        return True

    def close_history(self, announce=True):
        if announce:
            self.env["runtime"]["OutputManager"].present_text(
                _("speech history closed"), interrupt=True
            )
        self.set_active(False)

    def next_entry(self):
        if not self._has_history():
            return
        if self.curr_index == -1:
            self.curr_index = 0
            self.present_current()
            return
        if self.curr_index <= 0:
            self.curr_index = 0
            self.env["runtime"]["OutputManager"].present_text(
                _("First speech history item"), interrupt=True
            )
            self.present_current(interrupt=False)
            return
        self.curr_index -= 1
        self.present_current()

    def prev_entry(self):
        if not self._has_history():
            return
        if self.curr_index == -1:
            self.curr_index = 0
            self.present_current()
            return
        if self.curr_index >= len(self.history) - 1:
            self.curr_index = len(self.history) - 1
            self.env["runtime"]["OutputManager"].present_text(
                _("Last speech history item"), interrupt=True
            )
            self.present_current(interrupt=False)
            return
        self.curr_index += 1
        self.present_current()

    def present_current(self, interrupt=True):
        if not self._has_history():
            self.env["runtime"]["OutputManager"].present_text(
                _("speech history empty"), interrupt=True
            )
            return
        self.env["runtime"]["OutputManager"].present_text(
            self.history[self.curr_index], interrupt=interrupt
        )

    def copy_current_to_clipboard(self):
        if not self._has_history():
            self.close_history()
            return
        text = self.history[self.curr_index]
        self.env["runtime"]["MemoryManager"].add_value_to_first_index(
            "clipboardHistory", text
        )
        self.env["runtime"]["OutputManager"].present_text(
            _("copied to clipboard"),
            sound_icon="CopyToClipboard",
            interrupt=True,
        )
        self.set_active(False)

    def set_active(self, active):
        if active == self.active:
            return
        self.active = active
        if self.active:
            self._install_bindings()
        else:
            self._restore_bindings()

    def _has_history(self):
        if not self.history:
            self.curr_index = -1
            return False
        if self.curr_index >= len(self.history):
            self.curr_index = len(self.history) - 1
        return True

    def _get_history_size(self):
        try:
            return self.env["runtime"]["SettingsManager"].get_setting_as_int(
                "speech", "history_size"
            )
        except Exception:
            return 50

    def _get_history_key(self, text):
        return " ".join(text.split())

    def _install_bindings(self):
        self.bindings_backup = self.env["bindings"].copy()
        self.raw_bindings_backup = self.env["rawBindings"].copy()
        self.env["bindings"] = {
            str([1, ["KEY_UP"]]): "SPEECH_HISTORY_PREV",
            str([1, ["KEY_DOWN"]]): "SPEECH_HISTORY_NEXT",
            str([1, ["KEY_SPACE"]]): "SPEECH_HISTORY_CURRENT",
            str([1, ["KEY_ENTER"]]): "SPEECH_HISTORY_COPY",
            str([1, ["KEY_KPENTER"]]): "SPEECH_HISTORY_COPY",
            str([1, ["KEY_ESC"]]): "SPEECH_HISTORY_CLOSE",
        }
        modal_raw_bindings = {
            str([1, ["KEY_UP"]]): [1, ["KEY_UP"]],
            str([1, ["KEY_DOWN"]]): [1, ["KEY_DOWN"]],
            str([1, ["KEY_SPACE"]]): [1, ["KEY_SPACE"]],
            str([1, ["KEY_ENTER"]]): [1, ["KEY_ENTER"]],
            str([1, ["KEY_KPENTER"]]): [1, ["KEY_KPENTER"]],
            str([1, ["KEY_ESC"]]): [1, ["KEY_ESC"]],
        }
        self.env["rawBindings"] = self.raw_bindings_backup.copy()
        self.env["rawBindings"].update(modal_raw_bindings)
        self._refresh_input_bindings()

    def _restore_bindings(self):
        if self.bindings_backup is not None:
            self.env["bindings"] = self.bindings_backup
        if self.raw_bindings_backup is not None:
            self.env["rawBindings"] = self.raw_bindings_backup
        self.bindings_backup = None
        self.raw_bindings_backup = None
        self._reset_input_state()
        self._refresh_input_bindings()

    def _reset_input_state(self):
        try:
            self.env["runtime"]["InputManager"].reset_input_state()
        except Exception:
            pass

    def _refresh_input_bindings(self):
        try:
            refresh_grabs = getattr(
                self.env["runtime"]["InputDriver"], "refresh_grabs", None
            )
            if refresh_grabs:
                refresh_grabs(force=True)
        except Exception:
            pass
