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

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

"""
Remote controll:
section<space>command<space>parameters
sections:command,setting
setting commands:
- set section#setting=value[;section#setting=value]
- reset
command commands:
- say text to speech
- interrupt
examples
settings:
settings set section#setting=value[,section#setting=value]
setting set speech#voice=de
setting reset
setting save /path/settings.conf
command:
command say this is a test
command interrupt
"""


import os
import time

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


class RemoteManager:
    def __init__(self):
        # command controll
        self.commandConst = "COMMAND "
        self.sayConst = "SAY "
        self.vmenuConst = "VMENU "
        self.resetVmenuConst = "RESETVMENU"
        self.interruptConst = "INTERRUPT"
        self.quitAppConst = "QUITAPPLICATION"
        self.tempDisableSpeechConst = "TEMPDISABLESPEECH"
        self.defineWindowConst = "WINDOW "
        self.resetWindowConst = "RESETWINDOW"
        self.setClipboardConst = "CLIPBOARD "
        self.exportClipboardConst = "EXPORTCLIPBOARD"
        # setting controll
        self.settingConst = "SETTING "
        self.setSettingConst = "SET "
        self.saveAsSettingConst = "SAVEAS "
        self.saveSettingConst = "SAVE"
        self.resetSettingConst = "RESET"

    def initialize(self, environment):
        self.env = environment
        self.env["runtime"]["SettingsManager"].load_driver(
            self.env["runtime"]["SettingsManager"].get_setting(
                "remote", "driver"
            ),
            "RemoteDriver",
        )

    def shutdown(self):
        self.env["runtime"]["SettingsManager"].shutdown_driver("RemoteDriver")

    def handle_settings_change_with_response(self, settings_text):
        if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "remote", "enableSettingsRemote"
        ):
            return {
                "success": False,
                "message": "Settings remote control is disabled",
            }

        upper_settings_text = settings_text.upper()
        try:
            # set setting
            if upper_settings_text.startswith(self.setSettingConst):
                parameter_text = settings_text[len(self.setSettingConst) :]
                self.set_settings(parameter_text)
                return {
                    "success": True,
                    "message": f"Setting applied: {parameter_text}",
                }
            # save as setting
            elif upper_settings_text.startswith(self.saveAsSettingConst):
                parameter_text = settings_text[len(self.saveAsSettingConst) :]
                self.save_settings(parameter_text)
                return {
                    "success": True,
                    "message": f"Settings saved to: {parameter_text}",
                }
            # save setting
            elif upper_settings_text == self.saveSettingConst:
                self.save_settings()
                return {"success": True, "message": "Settings saved"}
            # reset setting
            elif upper_settings_text == self.resetSettingConst:
                self.reset_settings()
                return {
                    "success": True,
                    "message": "Settings reset to defaults",
                }
            else:
                return {
                    "success": False,
                    "message": f"Unknown settings command: {settings_text}",
                }
        except Exception as e:
            return {"success": False, "message": f"Settings error: {str(e)}"}

    def handle_settings_change(self, settings_text):
        if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "remote", "enableSettingsRemote"
        ):
            return

        upper_settings_text = settings_text.upper()
        # set setting
        if upper_settings_text.startswith(self.setSettingConst):
            parameter_text = settings_text[len(self.setSettingConst) :]
            self.set_settings(parameter_text)
        # save as setting
        elif upper_settings_text.startswith(self.saveAsSettingConst):
            parameter_text = settings_text[len(self.saveAsSettingConst) :]
            self.save_settings(parameter_text)
        # save setting
        elif upper_settings_text == self.saveSettingConst:
            self.save_settings()
        # reset setting
        elif upper_settings_text == self.resetSettingConst:
            self.reset_settings()

    def handle_command_execution_with_response(self, command_text):
        if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "remote", "enableCommandRemote"
        ):
            return {
                "success": False,
                "message": "Command remote control is disabled",
            }

        upper_command_text = command_text.upper()

        try:
            # say
            if upper_command_text.startswith(self.sayConst):
                parameter_text = command_text[len(self.sayConst) :]
                self.say(parameter_text)
                return {
                    "success": True,
                    "message": f"Speaking: {parameter_text[:50]}{'...' if len(parameter_text) > 50 else ''}",
                }
            # interrupt
            elif upper_command_text == self.interruptConst:
                self.interrupt_speech()
                return {"success": True, "message": "Speech interrupted"}
            # temp disable speech
            elif upper_command_text == self.tempDisableSpeechConst:
                self.temp_disable_speech()
                return {
                    "success": True,
                    "message": "Speech temporarily disabled",
                }
            # set vmenu
            elif upper_command_text.startswith(self.vmenuConst):
                parameter_text = command_text[len(self.vmenuConst) :]
                self.set_vmenu(parameter_text)
                return {
                    "success": True,
                    "message": f"VMenu set to: {parameter_text}",
                }
            # reset vmenu
            elif upper_command_text == self.resetVmenuConst:
                self.reset_vmenu()
                return {"success": True, "message": "VMenu reset"}
            # quit fenrir
            elif upper_command_text == self.quitAppConst:
                self.quit_fenrir()
                return {"success": True, "message": "Fenrir shutting down"}
            # define window
            elif upper_command_text.startswith(self.defineWindowConst):
                parameter_text = command_text[len(self.defineWindowConst) :]
                self.define_window(parameter_text)
                return {
                    "success": True,
                    "message": f"Window defined: {parameter_text}",
                }
            # reset window
            elif upper_command_text == self.resetWindowConst:
                self.reset_window()
                return {"success": True, "message": "Window reset"}
            # set clipboard
            elif upper_command_text.startswith(self.setClipboardConst):
                parameter_text = command_text[len(self.setClipboardConst) :]
                self.set_clipboard(parameter_text)
                return {
                    "success": True,
                    "message": f"Clipboard set: {parameter_text[:50]}{'...' if len(parameter_text) > 50 else ''}",
                }
            elif upper_command_text.startswith(self.exportClipboardConst):
                self.export_clipboard()
                return {
                    "success": True,
                    "message": "Clipboard exported to file",
                }
            else:
                return {
                    "success": False,
                    "message": f"Unknown command: {command_text}",
                }
        except Exception as e:
            return {"success": False, "message": f"Command error: {str(e)}"}

    def handle_command_execution(self, command_text):
        if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "remote", "enableCommandRemote"
        ):
            return

        upper_command_text = command_text.upper()

        # say
        if upper_command_text.startswith(self.sayConst):
            parameter_text = command_text[len(self.sayConst) :]
            self.say(parameter_text)
        # interrupt
        elif upper_command_text == self.interruptConst:
            self.interrupt_speech()
        # temp disable speech
        elif upper_command_text == self.tempDisableSpeechConst:
            self.temp_disable_speech()
        # set vmenu
        elif upper_command_text.startswith(self.vmenuConst):
            parameter_text = command_text[len(self.vmenuConst) :]
            self.set_vmenu(parameter_text)
        # reset vmenu
        elif upper_command_text == self.resetVmenuConst:
            self.reset_vmenu()
        # quit fenrir
        elif upper_command_text == self.quitAppConst:
            self.quit_fenrir()
        # define window
        elif upper_command_text.startswith(self.defineWindowConst):
            parameter_text = command_text[len(self.defineWindowConst) :]
            self.define_window(parameter_text)
        # reset window
        elif upper_command_text == self.resetWindowConst:
            self.reset_window()
        # set clipboard
        elif upper_command_text.startswith(self.setClipboardConst):
            parameter_text = command_text[len(self.setClipboardConst) :]
            self.set_clipboard(parameter_text)
        elif upper_command_text.startswith(self.exportClipboardConst):
            self.export_clipboard()

    def temp_disable_speech(self):
        self.env["runtime"]["OutputManager"].temp_disable_speech()

    def set_vmenu(self, vmenu=""):
        self.env["runtime"]["VmenuManager"].set_curr_menu(vmenu)

    def reset_vmenu(self):
        self.env["runtime"]["VmenuManager"].set_curr_menu()

    def set_clipboard(self, text=""):
        self.env["runtime"]["MemoryManager"].add_value_to_first_index(
            "clipboardHistory", text
        )

    def quit_fenrir(self):
        self.env["runtime"]["EventManager"].stop_main_event_loop()

    def define_window(self, window_text):
        start = {}
        end = {}
        try:
            window_list = window_text.split(" ")
            if len(window_list) < 4:
                return
            start["x"] = int(window_list[0])
            start["y"] = int(window_list[1])
            end["x"] = int(window_list[2])
            end["y"] = int(window_list[3])

            self.env["runtime"]["CursorManager"].set_window_for_application(
                start, end
            )
        except Exception as e:
            pass

    def reset_window(self):
        self.env["runtime"]["CursorManager"].clear_window_for_application()

    def say(self, text):
        if not text:
            return
        if text == "":
            return
        self.env["runtime"]["OutputManager"].speak_text(text)

    def interrupt_speech(self):
        self.env["runtime"]["OutputManager"].interrupt_output()

    def export_clipboard(self):
        clipboard_file_path = self.env["runtime"][
            "SettingsManager"
        ].get_setting("general", "clipboardExportPath")
        clipboard_file_path = clipboard_file_path.replace(
            "$user", self.env["general"]["curr_user"]
        )
        clipboard_file_path = clipboard_file_path.replace(
            "$USER", self.env["general"]["curr_user"]
        )
        clipboard_file_path = clipboard_file_path.replace(
            "$User", self.env["general"]["curr_user"]
        )
        clipboard_file = open(clipboard_file_path, "w")
        try:
            if self.env["runtime"]["MemoryManager"].is_index_list_empty(
                "clipboardHistory"
            ):
                self.env["runtime"]["OutputManager"].present_text(
                    _("clipboard empty"), interrupt=True
                )
                return
            clipboard = self.env["runtime"][
                "MemoryManager"
            ].get_index_list_element("clipboardHistory")
            # Fenrir will crash if the clipboard variable is type None
            if clipboard is not None:
                clipboard_file.write(clipboard)
            else:
                clipboard_file.write("")
            clipboard_file.close()
            os.chmod(clipboard_file_path, 0o644)
            self.env["runtime"]["OutputManager"].present_text(
                _("clipboard exported to file"), interrupt=True
            )
        except Exception as e:
            self.env["runtime"]["DebugManager"].write_debug_out(
                "export_clipboard_to_file:run: Filepath:"
                + clipboard_file
                + " trace:"
                + str(e),
                debug.DebugLevel.ERROR,
            )

    def save_settings(self, setting_config_path=None):
        if not setting_config_path:
            setting_config_path = self.env["runtime"][
                "SettingsManager"
            ].get_settings_file()
        if setting_config_path == "":
            return
        self.env["runtime"]["SettingsManager"].save_settings(
            setting_config_path
        )

    def reset_settings(self):
        self.env["runtime"]["SettingsManager"].reset_setting_arg_dict()

    def set_settings(self, settingsArgs):
        self.env["runtime"]["SettingsManager"].parse_setting_args(settingsArgs)
        self.env["runtime"]["ScreenManager"].update_screen_ignored()
        self.env["runtime"]["InputManager"].handle_device_grab(force=True)

    def handle_remote_incomming_with_response(self, event_data):
        if not event_data:
            return {"success": False, "message": "No data received"}

        upper_event_data = event_data.upper()
        self.env["runtime"]["DebugManager"].write_debug_out(
            "RemoteManager:handle_remote_incomming_with_response: event: "
            + str(event_data),
            debug.DebugLevel.INFO,
        )

        try:
            if upper_event_data.startswith(self.settingConst):
                settings_text = event_data[len(self.settingConst) :]
                return self.handle_settings_change_with_response(settings_text)
            elif upper_event_data.startswith(self.commandConst):
                command_text = event_data[len(self.commandConst) :]
                return self.handle_command_execution_with_response(
                    command_text
                )
            else:
                return {
                    "success": False,
                    "message": "Unknown command format. Use 'COMMAND ...' or 'SETTING ...'",
                }
        except Exception as e:
            return {"success": False, "message": f"Exception: {str(e)}"}

    def handle_remote_incomming(self, event_data):
        if not event_data:
            return
        upper_event_data = event_data.upper()
        self.env["runtime"]["DebugManager"].write_debug_out(
            "RemoteManager:handle_remote_incomming: event: " + str(event_data),
            debug.DebugLevel.INFO,
        )

        if upper_event_data.startswith(self.settingConst):
            settings_text = event_data[len(self.settingConst) :]
            self.handle_settings_change(settings_text)
        elif upper_event_data.startswith(self.commandConst):
            command_text = event_data[len(self.commandConst) :]
            self.handle_command_execution(command_text)
