#!/usr/bin/env python3
#
# Copyright (c) 2024 Stormux
# Copyright (c) 2010-2012 The Orca Team
# Copyright (c) 2012 Igalia, S.L.
# Copyright (c) 2005-2010 Sun Microsystems Inc.
#
# 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.
#
# Fork of Orca Screen Reader (GNOME)
# Original source: https://gitlab.gnome.org/GNOME/orca

"""Base class for Cthulhu plugins using pluggy."""

import os
import inspect
import logging

# Import pluggy for hook specifications
try:
    import pluggy
    cthulhu_hookimpl = pluggy.HookimplMarker("cthulhu")
except ImportError:
    # Fallback if pluggy is not available
    def cthulhu_hookimpl(func=None, **kwargs):
        """Dummy decorator for systems without pluggy."""
        if func is None:
            return lambda f: f
        return func

logger = logging.getLogger(__name__)

class Plugin:
    """Base class for Cthulhu plugins."""
    
    def __init__(self, *args, **kwargs):
        """Initialize the plugin with default attributes."""
        self.app = None
        self.pluginInfo = None
        self.moduleDir = ''
        self.hidden = False
        self.moduleName = ''
        self.name = ''
        self.version = ''
        self.website = ''
        self.authors = []
        self.buildIn = False
        self.description = ''
        self.iconName = ''
        self.copyright = ''
        self.dependencies = False
        self.helpUri = ''
        self.dataDir = ''
        self.translationContext = None
        self.dynamicApiManager = None
        self.signalManager = None
        
    def setApp(self, app):
        """Set the application reference.
        
        Args:
            app: The Cthulhu application instance.
        """
        self.app = app
        self.dynamicApiManager = app.getDynamicApiManager()
        self.signalManager = app.getSignalManager()

    def getApp(self):
        """Get the application reference.
        
        Returns:
            The Cthulhu application instance.
        """
        return self.app
    
    def setPluginInfo(self, pluginInfo):
        """Set the plugin information.
        
        Args:
            pluginInfo: The plugin information object.
        """
        self.pluginInfo = pluginInfo
        self.updatePluginInfoAttributes()
    
    def getPluginInfo(self):
        """Get the plugin information.
        
        Returns:
            The plugin information object.
        """
        return self.pluginInfo
    
    def updatePluginInfoAttributes(self):
        """Update plugin attributes from the plugin information."""
        self.moduleDir = ''
        self.hidden = False
        self.moduleName = ''
        self.name = ''
        self.version = ''
        self.website = ''
        self.authors = []
        self.buildIn = False
        self.description = ''
        self.iconName = ''
        self.copyright = ''
        self.dependencies = False
        self.helpUri = ''
        self.dataDir = ''
        
        pluginInfo = self.getPluginInfo()
        if pluginInfo is None:
            return
        
        if hasattr(self.app, 'getPluginSystemManager'):
            plugin_system = self.app.getPluginSystemManager()
            
            self.moduleName = plugin_system.getPluginModuleName(pluginInfo)
            self.name = plugin_system.getPluginName(pluginInfo)
            self.version = plugin_system.getPluginVersion(pluginInfo)
            self.moduleDir = plugin_system.getPluginModuleDir(pluginInfo)
            self.buildIn = plugin_system.isPluginBuildIn(pluginInfo)
            self.description = plugin_system.getPluginDescription(pluginInfo)
            self.hidden = plugin_system.isPluginHidden(pluginInfo)
            self.website = plugin_system.getPluginWebsite(pluginInfo)
            self.authors = plugin_system.getPluginAuthors(pluginInfo)
            self.iconName = plugin_system.getPluginIconName(pluginInfo)
            self.copyright = plugin_system.getPluginCopyright(pluginInfo)
            self.dependencies = plugin_system.getPluginDependencies(pluginInfo)
            self.helpUri = plugin_system.getPlugingetHelpUri(pluginInfo)
            self.dataDir = plugin_system.getPluginDataDir(pluginInfo)
        else:
            # Direct attribute access for pluggy-based plugins
            self.moduleName = getattr(pluginInfo, 'module_name', '')
            self.name = getattr(pluginInfo, 'name', '')
            self.version = getattr(pluginInfo, 'version', '')
            self.moduleDir = getattr(pluginInfo, 'module_dir', '')
            self.buildIn = getattr(pluginInfo, 'builtin', False)
            self.description = getattr(pluginInfo, 'description', '')
            self.hidden = getattr(pluginInfo, 'hidden', False)
            self.website = getattr(pluginInfo, 'website', '')
            self.authors = getattr(pluginInfo, 'authors', [])
            self.iconName = getattr(pluginInfo, 'icon_name', '')
            self.copyright = getattr(pluginInfo, 'copyright', '')
            self.dependencies = getattr(pluginInfo, 'dependencies', [])
            self.helpUri = getattr(pluginInfo, 'help_uri', '')
            self.dataDir = getattr(pluginInfo, 'data_dir', '')
        
        self.updateTranslationContext()
    
    def updateTranslationContext(self, domain=None, localeDir=None, language=None, fallbackToCthulhuTranslation=True):
        """Update the translation context.
        
        Args:
            domain: The translation domain.
            localeDir: The locale directory.
            language: The language to use.
            fallbackToCthulhuTranslation: Whether to fall back to Cthulhu translations.
        """
        self.translationContext = None
        useLocaleDir = f'{self.getModuleDir()}/locale/'
        
        if localeDir:
            if os.path.isdir(localeDir):
                useLocaleDir = localeDir
        
        useName = self.getModuleName()
        useDomain = useName
        
        if domain:
            useDomain = domain
        
        useLanguage = None
        if language:
            useLanguage = language
        
        translation_manager = self.getApp().getTranslationManager()
        self.translationContext = translation_manager.initTranslation(
            useName, 
            domain=useDomain, 
            localeDir=useLocaleDir, 
            language=useLanguage, 
            fallbackToCthulhuTranslation=fallbackToCthulhuTranslation
        )
        
        # Point _ to the translation object in the globals namespace of the caller frame
        try:
            callerFrame = inspect.currentframe().f_back
            # Install our gettext and ngettext function to the upper frame
            callerFrame.f_globals['_'] = self.translationContext.gettext
            callerFrame.f_globals['ngettext'] = self.translationContext.ngettext
        finally:
            del callerFrame  # Avoid reference problems with frames (per python docs)
    
    def getTranslationContext(self):
        """Get the translation context.
        
        Returns:
            The translation context.
        """
        return self.translationContext
    
    def isPluginBuildIn(self):
        """Check if the plugin is built-in.
        
        Returns:
            True if the plugin is built-in, False otherwise.
        """
        return self.buildIn
    
    def isPluginHidden(self):
        """Check if the plugin is hidden.
        
        Returns:
            True if the plugin is hidden, False otherwise.
        """
        return self.hidden
    
    def getAuthors(self):
        """Get the plugin authors.
        
        Returns:
            A list of plugin authors.
        """
        return self.authors
    
    def getCopyright(self):
        """Get the plugin copyright.
        
        Returns:
            The plugin copyright.
        """
        return self.copyright
    
    def getDataDir(self):
        """Get the plugin data directory.
        
        Returns:
            The plugin data directory.
        """
        return self.dataDir
    
    def getDependencies(self):
        """Get the plugin dependencies.
        
        Returns:
            The plugin dependencies.
        """
        return self.dependencies
    
    def getDescription(self):
        """Get the plugin description.
        
        Returns:
            The plugin description.
        """
        return self.description
    
    def getgetHelpUri(self):
        """Get the plugin help URI.
        
        Returns:
            The plugin help URI.
        """
        return self.helpUri
    
    def getIconName(self):
        """Get the plugin icon name.
        
        Returns:
            The plugin icon name.
        """
        return self.iconName
    
    def getModuleDir(self):
        """Get the plugin module directory.
        
        Returns:
            The plugin module directory.
        """
        return self.moduleDir
    
    def getModuleName(self):
        """Get the plugin module name.
        
        Returns:
            The plugin module name.
        """
        return self.moduleName
    
    def getName(self):
        """Get the plugin name.
        
        Returns:
            The plugin name.
        """
        return self.name
    
    def getVersion(self):
        """Get the plugin version.
        
        Returns:
            The plugin version.
        """
        return self.version
    
    def getWebsite(self):
        """Get the plugin website.
        
        Returns:
            The plugin website.
        """
        return self.website
    
    def getSetting(self, key):
        """Get a plugin setting.
        
        Args:
            key: The setting key.
        
        Returns:
            The setting value, or None if not found.
        """
        # To be implemented
        return None
    
    def setSetting(self, key, value):
        """Set a plugin setting.
        
        Args:
            key: The setting key.
            value: The setting value.
        """
        # To be implemented
        pass
    
    def registerGestureByString(self, function, name, gestureString, learnModeEnabled=True):
        """Register a gesture by string.
        
        Args:
            function: The function to call when the gesture is triggered.
            name: The name of the gesture.
            gestureString: The gesture string.
            learnModeEnabled: Whether the gesture is enabled in learn mode.
        
        Returns:
            The registered keybinding.
        """
        keybinding = self.getApp().getAPIHelper().registerGestureByString(
            function, 
            name, 
            gestureString, 
            'default', 
            'cthulhu', 
            learnModeEnabled, 
            contextName=self.getModuleName()
        )
        return keybinding
    
    def unregisterShortcut(self, keybinding, learnModeEnabled=True):
        """Unregister a shortcut.
        
        Args:
            keybinding: The keybinding to unregister.
            learnModeEnabled: Whether the shortcut is enabled in learn mode.
        
        Returns:
            True if the shortcut was unregistered, False otherwise.
        """
        ok = self.getApp().getAPIHelper().unregisterShortcut(
            keybinding, 
            contextName=self.getModuleName()
        )
        return ok
    
    def registerSignal(self, signalName, signalFlag=None, closure=None, accumulator=()):
        """Register a signal.
        
        Args:
            signalName: The signal name.
            signalFlag: The signal flags.
            closure: The closure.
            accumulator: The accumulator.
        
        Returns:
            True if the signal was registered, False otherwise.
        """
        if signalFlag is None:
            # Import GObject if available, otherwise use a dummy value
            try:
                from gi.repository import GObject
                signalFlag = GObject.SignalFlags.RUN_LAST
            except ImportError:
                signalFlag = 1  # Default value
        
        if closure is None:
            try:
                from gi.repository import GObject
                closure = GObject.TYPE_NONE
            except ImportError:
                closure = None  # Default value
        
        ok = self.signalManager.registerSignal(
            signalName, 
            signalFlag, 
            closure, 
            accumulator, 
            contextName=self.getModuleName()
        )
        return ok
    
    def unregisterSignal(self, signalName):
        """Unregister a signal.
        
        Args:
            signalName: The signal name.
        """
        # To be implemented
        pass
    
    def connectSignal(self, signalName, function, param=None):
        """Connect to a signal.
        
        Args:
            signalName: The signal name.
            function: The function to call when the signal is triggered.
            param: The parameter to pass to the function.
        
        Returns:
            The signal ID.
        """
        signalID = self.signalManager.connectSignal(
            signalName, 
            function, 
            param, 
            contextName=self.getModuleName()
        )
        return signalID
    
    def disconnectSignalByFunction(self, function):
        """Disconnect a signal by function.
        
        Args:
            function: The function to disconnect.
        """
        # Need to get mapped function
        mappedFunction = function
        self.signalManager.disconnectSignalByFunction(
            mappedFunction, 
            contextName=self.getModuleName()
        )
    
    def registerAPI(self, key, value, application=''):
        """Register an API.
        
        Args:
            key: The API key.
            value: The API value.
            application: The application.
        
        Returns:
            True if the API was registered, False otherwise.
        """
        ok = self.dynamicApiManager.registerAPI(
            key, 
            value, 
            application=application, 
            contextName=self.getModuleName()
        )
        return ok
    
    def unregisterAPI(self, key, application=''):
        """Unregister an API.
        
        Args:
            key: The API key.
            application: The application.
        """
        self.dynamicApiManager.unregisterAPI(
            key, 
            application=application, 
            contextName=self.getModuleName()
        )

    # Pluggy-specific hook implementations
    
    @cthulhu_hookimpl
    def activate(self):
        """Activate the plugin. This method should be overridden by subclasses."""
        logger.info(f"Activating plugin: {self.getName()}")
    
    @cthulhu_hookimpl
    def deactivate(self):
        """Deactivate the plugin. This method should be overridden by subclasses."""
        logger.info(f"Deactivating plugin: {self.getName()}")
