# (C) Copyright 2007-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" An extensible workbench window. """


# Standard library imports.
import logging

# Enthought library imports.
import pyface.workbench.api as pyface
from pyface.action.api import StatusBarManager
from traits.api import Delegate, Instance, List, Property, provides

from envisage.api import (
    ExtensionPoint,
    IExtensionPointUser,
    IExtensionRegistry,
    IServiceRegistry,
    ServiceRegistry,
)
from envisage.ui.action.api import ActionSet

# Local imports.
from .workbench_action_manager_builder import WorkbenchActionManagerBuilder
from .workbench_editor_manager import WorkbenchEditorManager

# Logging.
logger = logging.getLogger(__name__)


@provides(IServiceRegistry, IExtensionPointUser)
class WorkbenchWindow(pyface.WorkbenchWindow):
    """An extensible workbench window."""

    # Extension point Ids.
    ACTION_SETS = "envisage.ui.workbench.action_sets"
    VIEWS = "envisage.ui.workbench.views"
    PERSPECTIVES = "envisage.ui.workbench.perspectives"
    SERVICE_OFFERS = "envisage.ui.workbench.service_offers"

    #### 'WorkbenchWindow' interface ##########################################

    # The application that the window is part of.
    #
    # This is equivalent to 'self.workbench.application', and is provided just
    # as a convenience since windows often want access to the application.
    application = Delegate("workbench", modify=True)

    # The action sets that provide the toolbars, menus groups and actions
    # used in the window.
    action_sets = List(Instance(ActionSet))

    # The service registry for 'per window' services.
    service_registry = Instance(IServiceRegistry, factory=ServiceRegistry)

    #### 'IExtensionPointUser' interface ######################################

    # The extension registry that the object's extension points are stored in.
    extension_registry = Property(Instance(IExtensionRegistry))

    #### Private interface ####################################################

    # The workbench menu and tool bar builder.
    #
    # The builder is used to create the window's tool bar and menu bar by
    # combining all of the contributed action sets.
    _action_manager_builder = Instance(WorkbenchActionManagerBuilder)

    # Contributed action sets (each contribution is actually a factory).
    _action_sets = ExtensionPoint(id=ACTION_SETS)

    # Contributed views (each contribution is actually a factory).
    _views = ExtensionPoint(id=VIEWS)

    # Contributed perspectives (each contribution is actually a factory).
    _perspectives = ExtensionPoint(id=PERSPECTIVES)

    # Contributed service offers.
    _service_offers = ExtensionPoint(id=SERVICE_OFFERS)

    # The Ids of the services that were automatically registered.
    _service_ids = List

    ###########################################################################
    # 'IExtensionPointUser' interface.
    ###########################################################################

    def _get_extension_registry(self):
        """Trait property getter."""

        return self.application

    ###########################################################################
    # 'pyface.Window' interface.
    ###########################################################################

    #### Trait initializers ###################################################

    def _menu_bar_manager_default(self):
        """Trait initializer."""

        return self._action_manager_builder.create_menu_bar_manager("MenuBar")

    def _status_bar_manager_default(self):
        """Trait initializer."""

        return StatusBarManager()

    def _tool_bar_managers_default(self):
        """Trait initializer."""

        return self._action_manager_builder.create_tool_bar_managers("ToolBar")

    #### Trait change handlers ################################################

    def _opening_changed(self):
        """Static trait change handler."""

        self._service_ids = self._register_service_offers(self._service_offers)

    def _closed_changed(self):
        """Static trait change handler."""

        self._unregister_service_offers(self._service_ids)

    ###########################################################################
    # 'pyface.WorkbenchWindow' interface.
    ###########################################################################

    #### Trait initializers ###################################################

    def _editor_manager_default(self):
        """Trait initializer."""

        return WorkbenchEditorManager(window=self)

    def _icon_default(self):
        """Trait initializer."""

        return self.workbench.application.icon

    def _perspectives_default(self):
        """Trait initializer."""

        return [factory() for factory in self._perspectives]

    def _title_default(self):
        """Trait initializer."""

        return self.workbench.application.name

    def _views_default(self):
        """Trait initializer."""

        return [factory(window=self) for factory in self._views]

    ###########################################################################
    # 'WorkbenchWindow' interface.
    ###########################################################################

    def _action_sets_default(self):
        """Trait initializer."""

        return [factory(window=self) for factory in self._action_sets]

    ###########################################################################
    # 'IServiceRegistry' interface.
    ###########################################################################

    def get_service(self, protocol, query="", minimize="", maximize=""):
        """Return at most one service that matches the specified query."""

        service = self.service_registry.get_service(
            protocol, query, minimize, maximize
        )

        return service

    def get_service_properties(self, service_id):
        """Return the dictionary of properties associated with a service."""

        return self.service_registry.get_service_properties(service_id)

    def get_services(self, protocol, query="", minimize="", maximize=""):
        """Return all services that match the specified query."""

        services = self.service_registry.get_services(
            protocol, query, minimize, maximize
        )

        return services

    def register_service(self, protocol, obj, properties=None):
        """Register a service."""

        service_id = self.service_registry.register_service(
            protocol, obj, properties
        )

        return service_id

    def set_service_properties(self, service_id, properties):
        """Set the dictionary of properties associated with a service."""

        self.service_registry.set_service_properties(service_id, properties)

    def unregister_service(self, service_id):
        """Unregister a service."""

        self.service_registry.unregister_service(service_id)

    ###########################################################################
    # Private interface.
    ###########################################################################

    def __action_manager_builder_default(self):
        """Trait initializer."""

        action_manager_builder = WorkbenchActionManagerBuilder(
            window=self, action_sets=self.action_sets
        )

        return action_manager_builder

    def _register_service_offers(self, service_offers):
        """Register all service offers."""

        return list(map(self._register_service_offer, service_offers))

    def _register_service_offer(self, service_offer):
        """Register a service offer."""

        # Add the window to the service offer properties (this is so that it
        # is available to the factory when it is called to create the actual
        # service).
        service_offer.properties["window"] = self

        service_id = self.register_service(
            protocol=service_offer.protocol,
            obj=service_offer.factory,
            properties=service_offer.properties,
        )

        return service_id

    def _unregister_service_offers(self, service_ids):
        """Unregister all service offers."""

        # Unregister the services in the reverse order that we registered
        # them.
        service_ids_copy = service_ids[:]
        service_ids_copy.reverse()

        for service_id in service_ids_copy:
            self.unregister_service(service_id)
