# (C) Copyright 2007-2025 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!
# Enthought library imports.
from apptools.preferences.api import PreferencesHelper
from traits.api import Callable, Instance, Str
from traitsui.api import Controller


class PreferencesPane(Controller):
    """A panel for configuring application preferences."""

    #### 'Controller' interface ###############################################

    #: The preferences helper for which this pane is a view.
    model = Instance(PreferencesHelper)

    #### 'PreferencesPane' interface ##########################################

    #: An identifier for the pane (unique within a category).
    id = Str

    #: The ID of the category in which to place the pane.
    category = Str("General")

    #: The pane appears after the pane with this ID.
    before = Str

    #: The pane appears after the pane with this ID.
    after = Str

    #: The preferences dialog to which the pane belongs. Set by the framework.
    dialog = Instance("envisage.ui.tasks.preferences_dialog.PreferencesDialog")

    #: The factory to use for creating the preferences model object, of form:
    #:
    #:     ``callable(**traits) -> PreferencesHelper``
    #:
    #: If not specified, the preferences helper must be supplied manually.
    model_factory = Callable

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

    _model = Instance(PreferencesHelper)

    ###########################################################################
    # 'HasTraits' interface.
    ###########################################################################

    def trait_context(self):
        """Re-implemented to use a copy of the model that is not connected to
        the preferences node.
        """
        if self.model is None:
            if self.model_factory is not None:
                preferences = self.dialog.application.preferences
                self.model = self.model_factory(preferences=preferences)
            else:
                raise ValueError("A preferences pane must have a model!")

        # Make sure that we don't clone the preferences trait, since that
        # can lead to the preferences node being updated prematurely.
        # xref: enthought/envisage#582
        traits_to_clone = [
            trait_name
            for trait_name in self.model.copyable_trait_names()
            if trait_name != "preferences"
        ]
        self._model = self.model.clone_traits(traits_to_clone)
        self._model.preferences = None
        return {"object": self._model, "controller": self, "handler": self}

    ###########################################################################
    # 'Handler' interface.
    ###########################################################################

    def apply(self, info=None):
        """Handles the Apply button being clicked."""
        trait_names = list(
            filter(self._model._is_preference_trait, self._model.trait_names())
        )
        self.model.copy_traits(self._model, trait_names)

    def close(self, info, is_ok):
        """
        Handles the user attempting to close a dialog-based user interface.
        """
        if is_ok:
            self.apply()
        return super().close(info, is_ok)
