#include <glib.h>
#include <glib/gi18n.h>

#include "config.h"
#include "Common.h"
#include "UserLoginView.h"
#include "DmGreeterHelper.h"

#define LOGIN_BUTTON_ICON_WIDTH (37)
#define LOGIN_BUTTON_ICON_HEIGHT (39)

#define ERROR_BOX_MARKUP_FORMAT "<span foreground=\"#ff0000\" font_desc=\"12\">%1</span>"
#define SWITCH_USER_LABEL_FORMAT "<span foreground=\"#FFFFFF\" font_desc=\"%1\">%2</span>"

#define ALLOW_LOGIN_FAILED_TIMES (5)
#define LOGIN_FAIL_DELAY_TIME_DT (30)
#define MAX_LOGIN_FAIL_DELAY_TIME (180)

UserLoginView::UserLoginView()
    : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0)
    , m_loginCount(0)
    , m_delaySecs(0)
    , m_inputEntryArea(Gtk::ORIENTATION_HORIZONTAL, 0) {

    double scaleX = 1.0f;
    double scaleY = 1.0f;

    Common::getPrimaryScaleFactors(scaleX, scaleY);

    m_isOtherUser = false;
    m_inputEntry.set_size_request(295, 39);
    m_inputEntry.set_visibility(false);
    m_inputEntry.set_invisible_char('*');
    m_inputEntry.set_placeholder_text(_("please input password!"));
    
    m_sigEntryActiveIter = m_inputEntry.signal_activate().connect(sigc::mem_fun(*this, &UserLoginView::onEntryActive));

    initLoginButton();
    initSwitchUserButton();

    m_errMsgBox.set_size_request(332, 190 * scaleY);

    m_loginBtn.set_size_request(37, 39);

    m_switchUserArea.set_size_request(110 * scaleX, 32 * scaleY);
    m_switchUserButton.set_size_request(110 * scaleX, 32 * scaleY);
    m_switchUserLbl.set_size_request(110 * scaleX, 32 * scaleY);

    Glib::ustring compose = Glib::ustring::compose(SWITCH_USER_LABEL_FORMAT, Glib::ustring::format(12 * MIN(scaleX, scaleY)), Glib::ustring::format(_("Switch user")));
    m_switchUserLbl.set_markup(compose);

    m_switchUserArea.add(m_switchUserLbl);
    m_switchUserArea.add_overlay(m_switchUserButton);

    m_inputEntryArea.pack_start(m_inputEntry, false, false, 0);
    m_inputEntryArea.pack_start(m_loginBtn, false, false, 0);
    m_inputEntryArea.set_halign(Gtk::ALIGN_CENTER);

    m_inputEntryArea.set_margin_top((int)(60 * scaleY));

    pack_start(m_userItem, false, false, 0);
    pack_start(m_inputEntryArea, false, false, 0);
    pack_start(m_errMsgBox, false, false, 0);
    pack_start(m_switchUserArea, false, false, 0);

    m_sigSessionFailedIter = DmGreeterHelper::inst()->signalSessionFailed().connect(sigc::mem_fun(*this, &UserLoginView::onSessionFailed));
    m_sigPromptIter = DmGreeterHelper::inst()->signalPrompt().connect(sigc::mem_fun(*this, &UserLoginView::onPrompt));
    m_sigMsgIter = DmGreeterHelper::inst()->signalMessage().connect(sigc::mem_fun(*this, &UserLoginView::onMessage));
    m_sigAuthFailedIter = DmGreeterHelper::inst()->signalAuthFailed().connect(sigc::mem_fun(*this, &UserLoginView::onLoginFailed));

    m_userItem.set_face_can_focus(false);
    set_can_focus(false);
}

UserLoginView::~UserLoginView() {
    m_sigSessionFailedIter.disconnect();
    m_sigPromptIter.disconnect();
    m_sigMsgIter.disconnect();
    m_sigAuthFailedIter.disconnect();

    m_sigEntryActiveIter.disconnect();
    m_sigLoginPressIter.disconnect();
    m_sigSwitchPressIter.disconnect();
    m_sigSwitchKeyPressIter.disconnect();
}

void UserLoginView::setUserName(const Glib::ustring& name) {
    m_userItem.setUserName(name);
    if (!name.empty()) {
        m_inputEntryArea.set_sensitive(false);
        DmGreeterHelper::inst()->startAuthentication(name);
    } else {
        m_isOtherUser = true;
        m_inputEntryArea.set_sensitive(true);
        m_inputEntry.set_visibility(true);
        m_inputEntry.set_placeholder_text(_("please input user name!"));
        m_inputEntry.set_text("");
    }
}

void UserLoginView::focusEntry() {
    if (m_inputEntry.is_visible()) {
        m_inputEntry.grab_focus();
    }
}

void UserLoginView::requestSwitchUser() {
    realDoSwitchUser();
}

void UserLoginView::resetFocus() {
    m_inputEntry.grab_focus();
}

sigc::signal<void> UserLoginView::signalSwitchUser() {
	return m_switchUserSig;
}

void UserLoginView::initLoginButton() {
    StateImage stateImages;
    stateImages.setStateImage(StateImage::STATE_NORMAL, GLOBAL_RES_DIR"/icon/enter_default.png");
    stateImages.setStateImage(StateImage::STATE_PRESSED, GLOBAL_RES_DIR"/icon/enter_pressed_or_hover.png");
    stateImages.setStateImage(StateImage::STATE_HOVER, GLOBAL_RES_DIR"/icon/enter_pressed_or_hover.png");

    m_loginBtn.setStateImage(stateImages);
    m_sigLoginPressIter = m_loginBtn.signal_button_press_event().connect(sigc::mem_fun(*this, &UserLoginView::onLoginButtonPress));
}

void UserLoginView::initSwitchUserButton() {
    StateImage stateImages;
    stateImages.setStateImage(StateImage::STATE_NORMAL, GLOBAL_RES_DIR"/icon/switch_user_default.png");
    stateImages.setStateImage(StateImage::STATE_PRESSED, GLOBAL_RES_DIR"/icon/switch_user_pressed.png");
    stateImages.setStateImage(StateImage::STATE_HOVER, GLOBAL_RES_DIR"/icon/switch_user_hover.png");

    m_switchUserButton.setStateImage(stateImages);
    m_sigSwitchPressIter = m_switchUserButton.signal_button_press_event().connect(sigc::mem_fun(*this, &UserLoginView::onSwitchUserButtonPress));
    m_sigSwitchKeyPressIter = m_switchUserButton.signal_key_press_event().connect(sigc::mem_fun(*this, &UserLoginView::onSwitchUserKeyPress));
}

bool UserLoginView::onLoginButtonPress(GdkEventButton* event) {
    if (DmGreeterHelper::inst()->isLoginLocked()) {
        return true;
    }
    realDoLogin();
    return false;
}

bool UserLoginView::onSwitchUserButtonPress(GdkEventButton* event) {
    if (DmGreeterHelper::inst()->isLoginLocked()) {
        return true;
    }
    realDoSwitchUser();
    return false;
}

bool UserLoginView::onSwitchUserKeyPress(GdkEventKey* event) {
    if (DmGreeterHelper::inst()->isLoginLocked()) {
        return true;
    }
    if (GDK_KEY_PRESS == event->type) {
		switch (event->keyval) {
		case GDK_KEY_Return:
		case GDK_KEY_KP_Enter:
		case GDK_KEY_space:
			realDoSwitchUser();
			break;
		}
	}
    return false;
}

void UserLoginView::onEntryActive() {
    if (DmGreeterHelper::inst()->isLoginLocked()) {
        return;
    }
    realDoLogin();
}

void UserLoginView::onSessionFailed() {
    Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format("start session failed!"));
    m_errMsgBox.set_markup(compose);
}

void UserLoginView::onMessage(Glib::ustring text, int type) {
    Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format(text));
    m_errMsgBox.set_markup(compose); 
    if (!DmGreeterHelper::inst()->isInAuthentication()) {
        if (!m_isOtherUser) {
            setUserName(m_userItem.getUserName());
        } else {
            setUserName("");
        }
    }   
}

void UserLoginView::onPrompt(Glib::ustring text, int type) {
    if (DmGreeterHelper::inst()->isAuthenticated()) {
        DmGreeterHelper::inst()->requestStartSession();
    } else if (DmGreeterHelper::inst()->isInAuthentication()) {
        m_inputEntryArea.set_sensitive(true);
        m_inputEntry.grab_focus();
        m_inputEntry.set_visibility(false);
        m_inputEntry.set_placeholder_text(_("please input password!"));
        m_inputEntry.set_text("");
    } else {
        Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format(text));
        m_errMsgBox.set_markup(compose);
        if (!m_isOtherUser) {
            setUserName(m_userItem.getUserName());
        } else {
            setUserName("");
        }
    }
}

void UserLoginView::onLoginFailed() {
    char* errorString = NULL;
    
    if (!m_isOtherUser) {
        errorString = g_strdup_printf(_("Incorrect password, please try again(remain %d times)"), MAX(ALLOW_LOGIN_FAILED_TIMES - m_loginCount, 0));
        setUserName(m_userItem.getUserName());
    } else {
        errorString = g_strdup_printf(_("Incorrect name or password, please try again(remain %d times)"), MAX(ALLOW_LOGIN_FAILED_TIMES - m_loginCount, 0));
        setUserName("");
    }
    Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format(errorString));
    m_errMsgBox.set_markup(compose);
    m_inputEntry.set_text("");
    g_free(errorString);

    if (m_loginCount >= ALLOW_LOGIN_FAILED_TIMES) {
        DmGreeterHelper::inst()->setLoginLock(true);
        m_inputEntryArea.set_sensitive(false);
        m_inputEntry.set_sensitive(false);

        m_delaySecs = (m_loginCount - ALLOW_LOGIN_FAILED_TIMES + 1) * LOGIN_FAIL_DELAY_TIME_DT;
        m_delaySecs = MIN(m_delaySecs, MAX_LOGIN_FAIL_DELAY_TIME);

        errorString = g_strdup_printf(_("Login failed, please retry after %d secs"), m_delaySecs);
        Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format(errorString));
        m_errMsgBox.set_markup(compose);
        g_free(errorString);

        Glib::RefPtr<Glib::TimeoutSource> tsource = Glib::TimeoutSource::create(1000);
        tsource->connect(sigc::mem_fun(*this, &UserLoginView::delayLogin));
        tsource->attach();
    }
}

void UserLoginView::realDoLogin() {
    m_errMsgBox.set_text("");
    if (!m_userItem.getUserName().empty()) {
        m_inputEntryArea.set_sensitive(false);
        m_loginCount++;
        DmGreeterHelper::inst()->respondPwd(m_inputEntry.get_text());
    } else {
        setUserName(m_inputEntry.get_text());
    }
}

void UserLoginView::realDoSwitchUser() {
    m_isOtherUser = false;
    m_errMsgBox.set_text("");
    DmGreeterHelper::inst()->cancelAuthentication();
    signalSwitchUser().emit();
}

bool UserLoginView::delayLogin() {
    m_delaySecs--;
    char* errorString = NULL;
    if (0 < m_delaySecs) {
        errorString = g_strdup_printf(_("Login failed, please retry after %d secs"), m_delaySecs);
        Glib::ustring compose = Glib::ustring::compose(ERROR_BOX_MARKUP_FORMAT, Glib::ustring::format(errorString));
        m_errMsgBox.set_markup(compose);
        g_free(errorString);
        return true;
    }

    m_errMsgBox.set_text("");
    DmGreeterHelper::inst()->setLoginLock(false);
    m_inputEntryArea.set_sensitive(true);
    m_inputEntry.set_sensitive(true);
    m_inputEntry.grab_focus();
    return false;
}