# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Author: Olivier Tilloy <olivier@fluendo.com>

from elisa.core.utils import defer
from elisa.core.utils.i18n import install_translation

from elisa.plugins.pigment.pigment_controller import PigmentController
from elisa.plugins.pigment.widgets.input import DEFAULT_PASSWORD_CHAR, \
                                                DEFAULT_OBFUSCATE_DELAY

from elisa.plugins.poblesec import modal_popup
from elisa.plugins.poblesec.widgets.keyboard import Keyboard, TextEntry, \
                                                    PasswordTextEntry
from elisa.plugins.poblesec.actions import OpenControllerAction


_ = install_translation('poblesec')


class LoginController(PigmentController):

    """
    A generic login controller presenting the user with an entry
    and a virtual keyboard to enter a username and a password.
    """

    def set_frontend(self, frontend):
        super(LoginController, self).set_frontend(frontend)

        osk_qwerty = 'elisa.plugins.poblesec.osk_qwerty'
        osk_123 = 'elisa.plugins.poblesec.osk_123'
        osk_symbols = 'elisa.plugins.poblesec.osk_symbols'
        layouts = [osk_qwerty, osk_123, osk_symbols]

        self.keyboard = Keyboard(layouts)
        self.keyboard.title.foreground.label = \
            _('ENTER YOUR USERNAME & PASSWORD')

        self._username = TextEntry()
        self._username.instructions = _('Username')
        self._username.decorations.set_name('username')
        self._username.visible = True
        self.keyboard.add_entry(self._username)

        self._password = PasswordTextEntry()
        self._password.instructions = _('Password')
        self._password.decorations.set_name('password')
        self._password.visible = True
        self.keyboard.add_entry(self._password)

        self.keyboard.connect('validated', self._on_validated)
        self.keyboard.visible = True
        self.widget.add(self.keyboard)
        self.widget.set_focus_proxy(self.keyboard)

    def clean(self):
        self.keyboard.disconnect_by_func(self._on_validated)
        return super(LoginController, self).clean()

    def _on_validated(self, keyboard):
        dfr = self.login(self._username.content, self._password.content)
        dfr.addCallbacks(self.success, self.failure)
        return dfr

    def go_back(self):
        # Go back one screen.
        browser = self.frontend.retrieve_controllers('/poblesec/browser')[0]
        return browser.history.go_back()

    def close(self):
        """
        Close this controller.

        @return: a deferred fired when done closing
        @rtype:  L{elisa.core.utils.defer.Deferred}
        """
        return self.go_back()

    def login(self, username, password):
        """
        Attempt to log in user the supplied credentials (username and password).

        This method should be overridden by subclasses to implement the desired
        login logic. The default implementation fails.

        @param username: the user name
        @type username:  C{unicode}
        @param password: the password
        @type password:  C{unicode}

        @return: a deferred fired when the login is done (successful or failed)
                 with the result
        @rtype:  L{elisa.core.utils.defer.Deferred}
        """
        return defer.fail(NotImplementedError())

    def success(self, result):
        """
        Callback invoked when the login succeeds.

        This method should be overridden by subclasses to implement the desired
        action to be taken when the login succeeds. The default implementation
        does nothing.

        @param result: the result of the login (may be of any type)

        @return: a deferred fired when the callback is executed
        @rtype:  L{elisa.core.utils.defer.Deferred}
        """
        # To be overridden by subclasses.
        return defer.succeed(None)

    def failure(self, failure):
        """
        Callback invoked when the login fails.

        This method may be overridden by subclasses to implement a custom
        behaviour. The default implementation shows an error popup.

        @param failure: the failure object
        @type failure:  L{twisted.python.failure.Failure}

        @return: a deferred fired when the callback is executed
        @rtype:  L{elisa.core.utils.defer.Deferred}
        """
        main = self.frontend.retrieve_controllers('/poblesec')[0]
        title = _('LOGIN FAILED')
        subtitle = _('Could Not Login')
        text = \
            _("Moovida was unable to login using the details you entered.\n" \
              "Please recheck the login information you provided and attempt " \
              "to login again by selecting 'Check Login Details'.\n" \
              "Alternatively, you can cancel the login process by selecting " \
              "'Cancel Login'.")

        def close_and_callback(callback):
            dfr = main.hide_popup()
            dfr.addCallback(lambda result: callback())
            return dfr

        buttons = []
        buttons.append((_('Cancel Login'),
                        lambda: close_and_callback(self.close)))
        buttons.append((_('Check Login Details'), main.hide_popup))

        main.enqueue_popup(title, subtitle, text, buttons, modal_popup.ErrorPopup)

        # FIXME: the popup system not returning deferreds is a pain.
        return defer.succeed(None)
