"""
User prompt utility.
"""

from distutils.spawn import find_executable
import getpass
import logging
import subprocess
import sys

from gi.repository import Gtk


__all__ = ['password', 'browser']


def require_Gtk():
    """
    Make sure Gtk is properly initialized.

    :raises RuntimeError: if Gtk can not be properly initialized
    """
    # if we attempt to create any GUI elements with no X server running the
    # program will just crash, so let's make a way to catch this case:
    if not Gtk.init_check()[0]:
        raise RuntimeError("X server not connected!")


dialog_definition = r"""
<interface>
  <object class="GtkDialog" id="entry_dialog">
    <property name="border_width">5</property>
    <property name="window_position">center</property>
    <property name="type_hint">dialog</property>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="spacing">6</property>
        <property name="border_width">6</property>
        <child>
          <object class="GtkLabel" id="message">
            <property name="xalign">0</property>
          </object>
        </child>
        <child>
          <object class="GtkEntry" id="entry">
            <property name="visibility">False</property>
            <property name="activates_default">True</property>
          </object>
        </child>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <child>
              <object class="GtkButton" id="cancel_button">
                <property name="label">gtk-cancel</property>
                <property name="use_stock">True</property>
              </object>
            </child>
            <child>
              <object class="GtkButton" id="ok_button">
                <property name="label">gtk-ok</property>
                <property name="use_stock">True</property>
                <property name="can_default">True</property>
                <property name="has_default">True</property>
              </object>
            </child>
          </object>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="-6">cancel_button</action-widget>
      <action-widget response="-5">ok_button</action-widget>
    </action-widgets>
  </object>
</interface>
"""


def password_dialog(title, message):
    """
    Show a Gtk password dialog.

    :param str title:
    :param str message:
    :returns: the password or ``None`` if the user aborted the operation
    :rtype: str
    :raises RuntimeError: if Gtk can not be properly initialized
    """
    require_Gtk()
    builder = Gtk.Builder.new_from_string(dialog_definition, -1)
    dialog = builder.get_object('entry_dialog')
    label = builder.get_object('message')
    entry = builder.get_object('entry')
    dialog.set_title(title)
    label.set_label(message)
    dialog.show_all()
    response = dialog.run()
    dialog.hide()
    if response == Gtk.ResponseType.OK:
        return entry.get_text()
    else:
        return None


def get_password_gui(device):
    """Get the password to unlock a device from GUI."""
    text = 'Enter password for {0.device_presentation}: '.format(device)
    try:
        return password_dialog('udiskie', text)
    except RuntimeError:
        return None


def get_password_tty(device):
    """Get the password to unlock a device from terminal."""
    text = 'Enter password for {0.device_presentation}: '.format(device)
    try:
        return getpass.getpass(text)
    except EOFError:
        print("")
        return None


def password(hint_gui):
    """
    Create a password prompt function.

    :param bool hint_gui: whether a GUI input dialog should be preferred
    """
    def gui():
        try:
            require_Gtk()
            return get_password_gui
        except RuntimeError:
            return None
    def tty():
        if sys.stdin.isatty():
            return get_password_tty
        else:
            return None
    if hint_gui:
        return gui() or tty()
    else:
        return tty() or gui()


def browser(browser_name='xdg-open'):
    """
    Create a browse-directory function.

    :param str browser_name: file manager program name
    :returns: one-parameter open function
    :rtype: callable
    """
    if not browser_name:
        return None
    executable = find_executable(browser_name)
    if executable is None:
        # Why not raise an exception? -I think it is more convenient (for
        # end users) to have a reasonable default, without enforcing it.
        logging.getLogger(__name__).warn(
            "Can't find file browser: '%s'. "
            "You may want to change the value for the '-b' option."
            % (browser_name,))
        return None
    def browse(path):
        return subprocess.Popen([executable, path])
    return browse
