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

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

import inspect
import os
import re
import time

from fenrirscreenreader.core import debug
from fenrirscreenreader.core.eventData import FenrirEventType
from fenrirscreenreader.core.i18n import _

currentdir = os.path.dirname(
    os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))
)
fenrir_path = os.path.dirname(currentdir)


class ByteManager:
    def __init__(self):
        self.switchCtrlModeOnce = 0
        self.controlMode = True
        self.repeat = 1
        self.lastInputTime = time.time()
        self.lastByteKey = b""

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

    def shutdown(self):
        pass

    def unify_escape_seq(self, escapeSequence):
        converted_escape_sequence = escapeSequence
        if converted_escape_sequence[0] == 27:
            converted_escape_sequence = b"^[" + converted_escape_sequence[1:]
        if len(converted_escape_sequence) > 1:
            if (
                converted_escape_sequence[0] == 94
                and converted_escape_sequence[1] == 91
            ):
                converted_escape_sequence = (
                    b"^[" + converted_escape_sequence[2:]
                )
        return converted_escape_sequence

    def handle_byte_stream(self, event_data, sep=b"\x1b"):
        buffer = event_data
        # handle prefix
        end_index = buffer.find(sep)
        if end_index > 0:
            curr_sequence = buffer[:end_index]
            buffer = buffer[end_index:]
            self.handle_single_byte_sequence(curr_sequence)
        # special handlig for none found (performance)
        elif end_index == -1:
            self.handle_single_byte_sequence(buffer)
            return
        # handle outstanding sequence
        while buffer != b"":
            end_index = buffer[len(sep) :].find(sep)
            if end_index == -1:
                curr_sequence = buffer
                buffer = b""
            else:
                curr_sequence = buffer[: end_index + len(sep)]
                buffer = buffer[end_index + len(sep) :]
            self.handle_single_byte_sequence(curr_sequence)

    def handle_byte_input(self, event_data):
        if not event_data:
            return
        if event_data == b"":
            return
        try:
            self.env["runtime"]["DebugManager"].write_debug_out(
                "handle_byte_input " + event_data.decode("utf8"),
                debug.DebugLevel.INFO,
            )
        except Exception as e:
            self.env["runtime"]["DebugManager"].write_debug_out(
                "ByteManager handle_byte_input: Error decoding byte data: "
                + str(e),
                debug.DebugLevel.ERROR,
            )
        self.handle_byte_stream(event_data)

    def handle_single_byte_sequence(self, event_data):
        converted_escape_sequence = self.unify_escape_seq(event_data)

        if self.switchCtrlModeOnce > 0:
            self.switchCtrlModeOnce -= 1

        is_control_mode = False
        if (
            self.controlMode
            and not self.switchCtrlModeOnce == 1
            or not self.controlMode
        ):
            is_control_mode = self.handle_control_mode(event_data)

        is_command = False
        if (
            self.controlMode
            and not self.switchCtrlModeOnce == 1
            or not self.controlMode
            and self.switchCtrlModeOnce == 1
        ):
            if self.lastByteKey == converted_escape_sequence:
                if time.time() - self.lastInputTime <= self.env["runtime"][
                    "SettingsManager"
                ].get_setting_as_float("keyboard", "doubleTapTimeout"):
                    self.repeat += 1
            shortcut_data = b""
            for i in range(self.repeat):
                shortcut_data = shortcut_data + converted_escape_sequence
            is_command = self.detect_byte_command(shortcut_data)
            # fall back to single stroke - do we want this?
            if not is_command:
                is_command = self.detect_byte_command(
                    converted_escape_sequence
                )
                self.repeat = 1
        if not (is_command or is_control_mode):
            self.env["runtime"]["ScreenManager"].inject_text_to_screen(
                event_data
            )
        if not is_command:
            self.repeat = 1
        self.lastByteKey = converted_escape_sequence
        self.lastInputTime = time.time()

    def get_last_byte_key(self):
        return self.lastByteKey

    def handle_control_mode(self, escapeSequence):
        converted_escape_sequence = self.unify_escape_seq(escapeSequence)
        if converted_escape_sequence == b"^[R":
            self.controlMode = not self.controlMode
            self.switchCtrlModeOnce = 0
            if self.controlMode:
                self.env["runtime"]["OutputManager"].present_text(
                    _("Sticky Mode On"),
                    sound_icon="Accept",
                    interrupt=True,
                    flush=True,
                )
            else:
                self.env["runtime"]["OutputManager"].present_text(
                    _("Sticky Mode On"),
                    sound_icon="Cancel",
                    interrupt=True,
                    flush=True,
                )
            return True
        if converted_escape_sequence == b"^[:":
            self.switchCtrlModeOnce = 2
            self.env["runtime"]["OutputManager"].present_text(
                _("bypass"), sound_icon="PTYBypass", interrupt=True, flush=True
            )
            return True
        return False

    def send_bytes(self, byteMacro):
        pass

    def detect_byte_command(self, escapeSequence):
        converted_escape_sequence = self.unify_escape_seq(escapeSequence)
        command = self.env["runtime"]["InputManager"].get_command_for_shortcut(
            converted_escape_sequence
        )
        if command != "":
            self.env["runtime"]["EventManager"].put_to_event_queue(
                FenrirEventType.execute_command, command
            )
            command = ""
            return True
        return False

    def load_byte_shortcuts(
        self, kb_config_path=fenrir_path + "/../../config/keyboard/pty.conf"
    ):
        kb_config = open(kb_config_path, "r")
        while True:
            line = kb_config.readline()
            if not line:
                break
            line = line.replace("\n", "")
            if line.replace(" ", "") == "":
                continue
            if line.replace(" ", "").startswith("#"):
                continue
            if line.count("=") != 1:
                continue
            values = line.split("=")
            clean_shortcut = bytes(values[0], "UTF-8")
            repeat = 1
            if len(clean_shortcut) > 2:
                if chr(clean_shortcut[1]) == ",":
                    try:
                        repeat = int(chr(clean_shortcut[0]))
                        clean_shortcut = clean_shortcut[2:]
                    except Exception as e:
                        self.env["runtime"]["DebugManager"].write_debug_out(
                            "ByteManager load_byte_shortcuts: Error parsing repeat count: "
                            + str(e),
                            debug.DebugLevel.ERROR,
                        )
                        repeat = 1
                        clean_shortcut = clean_shortcut
            shortcut = b""
            for i in range(repeat):
                shortcut += clean_shortcut
            command_name = values[1].upper()
            self.env["bindings"][shortcut] = command_name
            self.env["runtime"]["DebugManager"].write_debug_out(
                "Byte Shortcut: " + str(shortcut) + " command:" + command_name,
                debug.DebugLevel.INFO,
                on_any_level=True,
            )
        kb_config.close()
