#------------------------------------------------------------------------------
# Copyright (c) 2013-2025, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
from atom.api import (
    Enum, Bool, Callable, List, Str, Typed, ForwardTyped, Event
)

from enaml.application import deferred_call
from enaml.core.declarative import d_
from .toolkit_object import ToolkitObject, ProxyToolkitObject


class ProxyFileDialog(ProxyToolkitObject):
    """ The abstract definition of a proxy FileDialog object.

    """
    #: A reference to the FileDialog declaration.
    declaration = ForwardTyped(lambda: FileDialog)

    def open(self):
        raise NotImplementedError

    def exec_(self):
        raise NotImplementedError


class FileDialog(ToolkitObject):
    """ A dialog widget that allows the user to open and save files and
    directories.

    """
    #: The title to use for the dialog.
    title = d_(Str())

    #: The mode of the dialog.
    mode = d_(Enum('open_file', 'open_files', 'save_file', 'directory'))

    #: The selected path in the dialog. This value will be used to set
    #: the initial working directory and file, as appropriate, when the
    #: dialog is opened. It will aslo be updated when the dialog is
    #: closed and accepted.
    path = d_(Str())

    #: The list of selected paths in the dialog. It will be updated
    #: when the dialog is closed and accepted. It is output only and
    #: is only applicable for the `open_files` mode.
    paths = List(Str())

    #: The string filters used to restrict the user's selections.
    filters = d_(List(Str()))

    #: The selected filter from the list of filters. This value will be
    #: used as the initial working filter when the dialog is opened. It
    #: will also be updated when the dialog is closed and accepted.
    selected_filter = d_(Str())

    #: Whether to use a platform native dialog, when available. This
    #: attribute is deprecated and no longer has any effect. Native
    #: dialogs are always used when available in a given toolkit.
    native_dialog = d_(Bool(True))

    #: An enum indicating if the dialog was accepted or rejected by
    #: the user. It will be updated when the dialog is closed. This
    #: value is output only.
    result = Enum('rejected', 'accepted')

    #: An optional callback which will be invoked when the dialog is
    #: closed. This is a convenience to make it easier to handle a
    #: dialog opened in non-blocking mode. The callback must accept
    #: a single argument, which will be the dialog instance.
    callback = d_(Callable())

    #: An event fired if the dialog is accepted. The payload will be
    #: the selected path.
    accepted = d_(Event(str), writable=False)

    #: An event fired when the dialog is rejected. It has no payload.
    rejected = d_(Event(), writable=False)

    #: An event fired when the dialog is closed. It has no payload.
    closed = d_(Event(), writable=False)

    #: Whether to destroy the dialog widget on close. The default is
    #: True since dialogs are typically used in a transitory fashion.
    destroy_on_close = d_(Bool(True))

    #: A reference to the ProxyFileDialog object.
    proxy = Typed(ProxyFileDialog)

    def open(self):
        """ Open the dialog in a non-blocking fashion.

        This method will always return None. The state of the dialog
        will be updated when the dialog is closed by the user.

        """
        if not self.is_initialized:
            self.initialize()
        self.proxy.open()

    def exec_(self):
        """ Open the dialog in a blocking fashion.

        Returns
        -------
        result : unicode
            The path selected by the user, or an empty string if the
            dialog is cancelled.

        """
        if not self.is_initialized:
            self.initialize()
        self.proxy.exec_()
        if self.result == 'accepted':
            return self.path
        return ''

    #--------------------------------------------------------------------------
    # Utility Methods
    #--------------------------------------------------------------------------
    def _handle_close(self, result, paths, selected_filter):
        """ Called by the proxy object when the dialog is closed.

        Parameters
        ----------
        result : string
            The result of the dialog; either 'accepted' or 'rejected'.

        paths : list
            The list of user selected paths. If the result is 'rejected'
            this should be an empty list.

        selected_filter : unicode
            The user selected name filter. If the result is 'rejected'
            this should be an empty string.

        """
        self.result = result
        if result == 'accepted':
            self.paths = paths
            self.path = paths[0] if paths else ''
            self.selected_filter = selected_filter
            self.accepted(self.path)
        else:
            self.rejected()
        if self.callback:
            self.callback(self)
        self.closed()
        if self.destroy_on_close:
            deferred_call(self.destroy)
