#!/usr/bin/env python3
# Cthulhu
#
# Copyright 2005-2008 Sun Microsystems Inc.
# Copyright 2011-2025 Igalia, S.L.
# Copyright 2025 Stormux <storm_dragon@stormux.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
# Boston MA  02110-1301 USA.

"""Provides typing echo support with D-Bus controls."""

__id__        = "$Id$"
__version__   = "$Revision$"
__date__      = "$Date$"
__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." \
                "Copyright (c) 2011-2025 Igalia, S.L."
__license__   = "LGPL"

import string
from typing import TYPE_CHECKING

from . import braille
from . import cmdnames
from . import dbus_service
from . import debug
from . import input_event
from . import keybindings
from . import messages
from . import settings
from . import settings_manager
from . import speech

if TYPE_CHECKING:
    from . import default

_settings_manager = settings_manager.getManager()

class TypingEchoPresenter:
    """Provides typing echo functionality with D-Bus remote control support."""

    def __init__(self):
        """Initialize the typing echo presenter."""
        debug.printMessage(debug.LEVEL_INFO, "TYPING ECHO PRESENTER: Initializing", True)

    # D-Bus getters and setters for key echo settings
    @dbus_service.getter
    def get_key_echo_enabled(self) -> bool:
        """Returns whether echo of key presses is enabled."""
        return _settings_manager.getSetting('enableKeyEcho')

    @dbus_service.setter
    def set_key_echo_enabled(self, value: bool) -> bool:
        """Sets whether echo of key presses is enabled."""
        try:
            _settings_manager.setSetting('enableKeyEcho', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting key echo: {e}", True)
            return False

    @dbus_service.getter
    def get_character_echo_enabled(self) -> bool:
        """Returns whether echo of inserted characters is enabled."""
        return _settings_manager.getSetting('enableEchoByCharacter')

    @dbus_service.setter
    def set_character_echo_enabled(self, value: bool) -> bool:
        """Sets whether echo of inserted characters is enabled."""
        try:
            _settings_manager.setSetting('enableEchoByCharacter', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting character echo: {e}", True)
            return False

    @dbus_service.getter
    def get_word_echo_enabled(self) -> bool:
        """Returns whether word echo is enabled."""
        return _settings_manager.getSetting('enableEchoByWord')

    @dbus_service.setter
    def set_word_echo_enabled(self, value: bool) -> bool:
        """Sets whether word echo is enabled."""
        try:
            _settings_manager.setSetting('enableEchoByWord', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting word echo: {e}", True)
            return False

    @dbus_service.getter
    def get_sentence_echo_enabled(self) -> bool:
        """Returns whether sentence echo is enabled."""
        return _settings_manager.getSetting('enableEchoBySentence')

    @dbus_service.setter
    def set_sentence_echo_enabled(self, value: bool) -> bool:
        """Sets whether sentence echo is enabled."""
        try:
            _settings_manager.setSetting('enableEchoBySentence', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting sentence echo: {e}", True)
            return False

    @dbus_service.getter
    def get_alphabetic_keys_enabled(self) -> bool:
        """Returns whether alphabetic keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableAlphabeticKeys')

    @dbus_service.setter
    def set_alphabetic_keys_enabled(self, value: bool) -> bool:
        """Sets whether alphabetic keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableAlphabeticKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting alphabetic keys: {e}", True)
            return False

    @dbus_service.getter
    def get_numeric_keys_enabled(self) -> bool:
        """Returns whether numeric keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableNumericKeys')

    @dbus_service.setter
    def set_numeric_keys_enabled(self, value: bool) -> bool:
        """Sets whether numeric keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableNumericKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting numeric keys: {e}", True)
            return False

    @dbus_service.getter
    def get_punctuation_keys_enabled(self) -> bool:
        """Returns whether punctuation keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enablePunctuationKeys')

    @dbus_service.setter
    def set_punctuation_keys_enabled(self, value: bool) -> bool:
        """Sets whether punctuation keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enablePunctuationKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting punctuation keys: {e}", True)
            return False

    @dbus_service.getter
    def get_space_enabled(self) -> bool:
        """Returns whether space key will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableSpace')

    @dbus_service.setter
    def set_space_enabled(self, value: bool) -> bool:
        """Sets whether space key will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableSpace', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting space key: {e}", True)
            return False

    @dbus_service.getter
    def get_modifier_keys_enabled(self) -> bool:
        """Returns whether modifier keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableModifierKeys')

    @dbus_service.setter
    def set_modifier_keys_enabled(self, value: bool) -> bool:
        """Sets whether modifier keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableModifierKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting modifier keys: {e}", True)
            return False

    @dbus_service.getter
    def get_function_keys_enabled(self) -> bool:
        """Returns whether function keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableFunctionKeys')

    @dbus_service.setter
    def set_function_keys_enabled(self, value: bool) -> bool:
        """Sets whether function keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableFunctionKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting function keys: {e}", True)
            return False

    @dbus_service.getter
    def get_action_keys_enabled(self) -> bool:
        """Returns whether action keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableActionKeys')

    @dbus_service.setter
    def set_action_keys_enabled(self, value: bool) -> bool:
        """Sets whether action keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableActionKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting action keys: {e}", True)
            return False

    @dbus_service.getter
    def get_navigation_keys_enabled(self) -> bool:
        """Returns whether navigation keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableNavigationKeys')

    @dbus_service.setter
    def set_navigation_keys_enabled(self, value: bool) -> bool:
        """Sets whether navigation keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableNavigationKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting navigation keys: {e}", True)
            return False

    @dbus_service.getter
    def get_diacritical_keys_enabled(self) -> bool:
        """Returns whether diacritical keys will be echoed when key echo is enabled."""
        return _settings_manager.getSetting('enableDiacriticalKeys')

    @dbus_service.setter
    def set_diacritical_keys_enabled(self, value: bool) -> bool:
        """Sets whether diacritical keys will be echoed when key echo is enabled."""
        try:
            _settings_manager.setSetting('enableDiacriticalKeys', value)
            return True
        except Exception as e:
            debug.printMessage(debug.LEVEL_WARNING, f"Error setting diacritical keys: {e}", True)
            return False

    @dbus_service.command
    def cycle_key_echo(self, script: 'default.Script', event=None):
        """Cycles through key echo modes."""
        if not _settings_manager.getSetting('enableKeyEcho'):
            _settings_manager.setSetting('enableKeyEcho', True)
            script.presentMessage(messages.KEY_ECHO_ENABLED)
        else:
            _settings_manager.setSetting('enableKeyEcho', False)
            script.presentMessage(messages.KEY_ECHO_DISABLED)
        return True

    def should_echo_keyboard_event(self, event: input_event.KeyboardEvent) -> bool:
        """Returns whether the given keyboard event should be echoed."""
        if not _settings_manager.getSetting('enableKeyEcho'):
            return False

        if event.event_string in ["shift", "control", "alt", "meta"]:
            return _settings_manager.getSetting('enableModifierKeys')

        if event.event_string.startswith("f") and event.event_string[1:].isdigit():
            return _settings_manager.getSetting('enableFunctionKeys')

        if event.event_string in ["return", "enter", "tab", "escape", "backspace", "delete"]:
            return _settings_manager.getSetting('enableActionKeys')

        if event.event_string in ["up", "down", "left", "right", "home", "end", "page_up", "page_down"]:
            return _settings_manager.getSetting('enableNavigationKeys')

        if event.event_string == "space":
            return _settings_manager.getSetting('enableSpace')

        if len(event.event_string) == 1:
            char = event.event_string
            if char.isalpha():
                return _settings_manager.getSetting('enableAlphabeticKeys')
            elif char.isdigit():
                return _settings_manager.getSetting('enableNumericKeys')
            elif char in string.punctuation:
                return _settings_manager.getSetting('enablePunctuationKeys')

        return False

    def is_character_echoable(self, event: input_event.KeyboardEvent) -> bool:
        """Returns True if the script will echo this event as part of character echo."""
        if not _settings_manager.getSetting('enableEchoByCharacter'):
            return False

        # Character echo is for printable characters being inserted
        if len(event.event_string) == 1 and event.event_string.isprintable():
            return True

        return False

    def echo_keyboard_event(self, script: 'default.Script', event: input_event.KeyboardEvent) -> None:
        """Presents the KeyboardEvent event."""
        if self.should_echo_keyboard_event(event):
            if event.event_string == "space":
                script.presentMessage(messages.SPACE)
            elif event.event_string == "tab":
                script.presentMessage(messages.TAB)
            elif event.event_string == "return" or event.event_string == "enter":
                script.presentMessage(messages.ENTER)
            elif event.event_string == "backspace":
                script.presentMessage(messages.BACKSPACE)
            elif event.event_string == "delete":
                script.presentMessage(messages.DELETE)
            else:
                # For simple characters and other keys, just speak the event string
                script.presentMessage(event.event_string)

# Global instance
_manager = None

def getManager():
    """Get the typing echo presenter manager."""
    global _manager
    if not _manager:
        _manager = TypingEchoPresenter()
    return _manager