
#include <eedb/auth/Services.hpp>
#include <eedb/widgets/DefaultAuthPage.hpp>

#include <Wt/Auth/AbstractUserDatabase>
#include <Wt/Auth/AuthWidget>
#include <Wt/WContainerWidget>

namespace std {

template <>
struct default_delete< Wt::Auth::AuthWidget > {
    void operator()(Wt::Auth::AuthWidget * ptr) {
        if(ptr && !ptr->parent())
            delete ptr;
    }
};
}

namespace eedb {

struct DefaultAuthPage::DefaultAuthPagePriv {
    DefaultAuthPagePriv(const auth::Services & baseAuth,
      std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase,
      Wt::Auth::Login & _login)
     : _authWidget(std::make_unique< Wt::Auth::AuthWidget >(*baseAuth.authService(), *userDatabase.get(), _login, nullptr)),
       _userDatabase(std::move(userDatabase)) {
        _authWidget->model()->addPasswordAuth(eedb::auth::Services::passwordService());
        _authWidget->model()->addOAuth(eedb::auth::Services::oAuthServices());
        _authWidget->setRegistrationEnabled(true);
    }

    Wt::Signal<> _onNeedEmailVerification;
    Wt::Signal<> _onUserWeakLogin;
    Wt::Signal<> _onUserStrongLogin;
    Wt::Signal<> _onUserLogout;

    std::unique_ptr< Wt::Auth::AuthWidget > _authWidget;
    std::unique_ptr< Wt::Auth::AbstractUserDatabase > _userDatabase;
};

DefaultAuthPage::DefaultAuthPage(const auth::Services & baseAuth,
  std::unique_ptr< Wt::Auth::AbstractUserDatabase > userDatabase,
  Wt::Auth::Login & _login)
 : _priv(std::make_unique< DefaultAuthPagePriv >(baseAuth, std::move(userDatabase), _login)) {
    _priv->_authWidget->login().changed().connect([this](auto...) {
        if(_priv->_authWidget->login().state() == Wt::Auth::LoginState::StrongLogin) {
            this->notifyUserStrongLogin();
        } else if(_priv->_authWidget->login().state() == Wt::Auth::LoginState::WeakLogin) {
            this->notifyUserWeakLogin();
        } else if(_priv->_authWidget->login().state() == Wt::Auth::LoginState::DisabledLogin) {
            this->notifyNeedEmailVerification();
        } else {
            this->notifyUserLogout();
        }
    });
}

DefaultAuthPage::DefaultAuthPage(DefaultAuthPage && rhs) : _priv(std::move(rhs._priv)) {}

DefaultAuthPage & DefaultAuthPage::operator=(DefaultAuthPage && rhs) {
    _priv = std::move(rhs._priv);
    return *this;
}

DefaultAuthPage::~DefaultAuthPage() = default;
void DefaultAuthPage::attachTo(Wt::WContainerWidget * parent) {
    parent->addWidget(_priv->_authWidget.get());
}

void DefaultAuthPage::detach() {
    auto parent = dynamic_cast< Wt::WContainerWidget * >(_priv->_authWidget->parent());
    if(parent)
        parent->removeWidget(_priv->_authWidget.get());
}

void DefaultAuthPage::processEnvironment() {
    _priv->_authWidget->processEnvironment();
}

void DefaultAuthPage::registerNeedVerification(std::function< void() > f) {
    _priv->_onNeedEmailVerification.connect([=](auto...) { f(); });
}

void DefaultAuthPage::registerOnUserWeakLogin(std::function< void() > f) {
    _priv->_onUserWeakLogin.connect([=](auto...) { f(); });
}

void DefaultAuthPage::registerOnUserStrongLogin(std::function< void() > f) {
    _priv->_onUserStrongLogin.connect([=](auto...) { f(); });
}

void DefaultAuthPage::registerOnUserLogout(std::function< void() > f) {
    _priv->_onUserLogout.connect([=](auto...) { f(); });
}

void DefaultAuthPage::notifyUserStrongLogin() const {
    _priv->_onUserStrongLogin.emit();
}

void DefaultAuthPage::notifyUserWeakLogin() const {
    _priv->_onUserWeakLogin.emit();
}

void DefaultAuthPage::notifyNeedEmailVerification() const {
    _priv->_onNeedEmailVerification.emit();
}

void DefaultAuthPage::notifyUserLogout() const {
    _priv->_onUserLogout.emit();
}
}
