// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2011 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WT_AUTH_AUTH_WIDGET_H_
#define WT_AUTH_AUTH_WIDGET_H_

#include <Wt/WTemplate>
#include <Wt/Auth/OAuthService>

namespace Wt {
  namespace Auth {

class AbstractPasswordService;
class AbstractUserDatabase;
class EnterPasswordFields;
class Login;
class OAuthService;
class User;

/*! \class AuthWidget Wt/Auth/AuthWidget
 *  \brief An authentication widget.
 *
 * The authentication widget is a widget that provides a login or
 * logout function (depending on whether the user is currently logged
 * in). You can use it for either or both purposes.
 *
 * Login or logout events are signalled to a Login object on which
 * this widget acts.
 *
 * The widget also processes environmental information related to
 * authentication:
 *
 * - email tokens, which are indicated in an internal path. The widget
 *   uses dialogs (by default) to interact with the user to act on the token.
 * - authentication tokens, which are stored in browser cookies, to implement
 *   remember-me functionality.
 * 
 * The processEnvironment() method initiates this process, and should
 * typically be called only at application startup time.
 *
 * The authentication widget can be configured for password-based
 * authentication (addPasswordAuth()) and/or OAuth-based
 * authentication (addOAuth()).
 *
 * It is very likely that the off-the shelf authentication widget not
 * entirely to your taste or functional requirements. The widget uses
 * two methods to allow customization:
 *
 * - all forms are rendered using a WTemplate, and thus you may change the
 *   layout and styling of the forms to your liking.
 * - all views are created using virtual methods, which may be specialized
 *   to create a customized view or to apply changes to the default view.
 *
 * \ingroup auth
 */
class WT_API AuthWidget : public WTemplate
{
public:
  /*! \brief Constructor
   *
   * Creates a new authentication widget which uses the authentication
   * service in \p baseAuth, and the user database \p
   * users.
   *
   * The result of authentication changes is propagated to the rest of
   * the application using a \p login object.
   *
   * Authentication methods should be configured using
   * addPasswordAuth() and addOAuth().
   */
  AuthWidget(const AuthService& baseAuth, AbstractUserDatabase& users,
	     Login& login, WContainerWidget *parent = 0);

  /*! \brief Sets an internal path for authentication services.
   *
   * Only the registration function is made available through an
   * internal path (so that one can redirect a user to the
   * registration page). Other internal paths involved in
   * authentication are configured in the service classes:
   * - AuthService::setEmailRedirectInternalPath(): email tokens
   * - OAuthService::redirectInternalPath(): an internal path used during the oauth
   *   process.
   */
  void setInternalBasePath(const std::string& path);

  /*! \brief Returns the internal path.
   *
   * \sa setInternalBasePath()
   */
  std::string internalBasePath() const { return basePath_; }

  /*! \brief Returns the login object.
   *
   * This returns the login object on which this authentication widget
   * acts.
   */
  Login& login() { return login_; }

  /*! \brief Returns the authentication base service.
   *
   * This returns the service passed through the constructor.
   */
  const AuthService *baseAuth() const { return &baseAuth_; }

  /*! \brief Adds a password authentication service.
   *
   * This enables password-based authentication capabilities in the widget:
   *
   * - login using an identity (username) and password
   * - a view to update the password when an email token is received
   *   with that intent
   *
   * Only one password authentication service can be configured.
   *
   * \sa addOAuth()
   */
  void addPasswordAuth(const AbstractPasswordService *auth);

  /*! \brief Returns the password authentication service.
   *
   * \sa addPasswordAuth()
   */
  const AbstractPasswordService *passwordAuth() const { return passwordAuth_; }

  /*! \brief Adds an OAuth authentication service provider.
   *
   * This enables OAuth-based authentication. More than one OAuth
   * authentication service can be configured: one for each supported
   * third-party OAuth identity provider.
   *
   * \sa addPasswordAuth()
   */
  void addOAuth(const OAuthService *auth);

  /*! \brief Adds a list of OAuth authentication service providers.
   *
   * \sa addOAuth()
   */
  void addOAuth(const std::vector<const OAuthService *>& auth);

  /*! \brief Returns the list of OAuth authentication service providers.
   *
   * \sa addOAuth()
   */
  std::vector<const OAuthService *> oAuth() const { return oAuth_; }

  /*! \brief Configures registration capabilities.
   *
   * Although the AuthWidget itself does not implement a registration
   * view, it may offer a button/link to do so, and calls
   * registerNewUser() when a user wishes to register.
   *
   * Even if registration is not enabled, the result of an OAuthService login
   * process may be that a new user is identified. Then the
   * createRegistrationView() is also used to present this new user with
   * a registration view, passing the information obtained through OAuth.
   */
  void setRegistrationEnabled(bool enabled);

  /*! \brief Starts a new registration process.
   *
   * This calls \p registerNewUser(0).
   */
  void registerNewUser();

  /*! \brief Starts a new registration process.
   *
   * This starts a new registration process, and may be called in response to
   * a user action, an internal path change, or an OAuthService login procedure which
   * identified a new user. In the latter case, the OAuth-provided information
   * is passed as parameter \p oauth.
   *
   * The default implementation creates a view using
   * createRegistrationView(), and shows it in a dialog using
   * showDialog().
   */
  virtual void registerNewUser(const Identity& oauth);

  /*! \brief Process the (initial) environment.
   *
   * This method process environmental information that may be relevant to
   * authentication:
   *
   * - email tokens, which are indicated through an internal path. The
   *   widget uses dialogs (by default) to interact with the user to
   *   act on the token.
   *
   * - authentication tokens, which are stored in browser cookies, to
   *   implement remember-me functionality. When logging in using an
   *   authentication token, the login is considered "weak" (since a
   *   user may have inadvertently forgotten to logout from a public
   *   computer). You should let the user authenticate using another,
   *   primary method before doing sensitive operations. The
   *   createPasswordPromptDialog() method may be useful for this.
   *
   * \sa letUpdatePassword() 
   */
  virtual void processEnvironment();

  /*! \brief Lets the user update his password.
   *
   * This creates a view to let the user enter his new password.
   *
   * The default implementation creates a new view using
   * createUpdatePasswordView() and shows it in a dialog using
   * showDialog().
   */
  virtual void letUpdatePassword(const User& user, bool promptPassword);

  /*! \brief Lets the user update his password.
   *
   * This creates a view to let the user enter his email address, used
   * to send an email containing instructions to enter a new password.
   *
   * The default implementation creates a new view using
   * createLostPasswordView and shows it in a dialog using
   * showDialog().
   */
  virtual void handleLostPassword();

  /*! \brief Creates the login view.
   *
   * This creates a view that allows the user to login, and is shown when
   * no user is current logged in.
   *
   * The default implementation combines the result of
   * createPasswordLogin() and/or createOAuthLogin() in a container
   * widget.
   *
   * \sa Login::loggedIn()
   * \sa createLoggedInView()
   */
  virtual void createLoginView();

  /*! \brief Creates the view shown when the user is logged in.
   *
   * The default implementation renders the
   * <tt>"Wt.Auth.template.logged-in"</tt> template.
   *
   * \sa Login::loggedIn()
   * \sa createLoginView()
   */
  virtual void createLoggedInView();

  /*! \brief Creates a password login view.
   *
   * This is used by the default implementation of createLoginView()
   * to prompt for the information needed for logging in using a
   * username and password.
   *
   * The default implementation renders the
   * <tt>"Wt.Auth.template.passwords-login"</tt> template.
   *
   * \sa createLoginView()
   */
  virtual void createPasswordLogin();

  /*! \brief Creates a lost password view.
   *
   * When email verification has been enabled, the user may indicate
   * that he has lost his password -- then proof of controlling the same
   * email address that had associated with his account is sufficient to
   * allow him to enter a new password.
   *
   * This creates the widget used to let the user enter his email
   * address. The default implementation creates a new
   * LostPasswordWidget.
   *
   * \sa handleLostPassword()
   */
  virtual WWidget *createLostPasswordView();

  /*! \brief Creates a registration view.
   * 
   * This creates a registration view, optionally using information
   * already obtained through OAuth.
   *
   * The default implementation creates a new RegistrationWidget, and
   * adds the same authentication mechanisms as used for logging in
   * in this widget.
   *
   * \sa registerNewUser()
   */
  virtual WWidget *createRegistrationView(const Identity& id);

  /*! \brief Creates a view to update a user's password.
   *
   * If \p promptPassword is \c true, the user has to enter his current
   * password in addition to a new password.
   *
   * This creates the widget used to let the user chose a new
   * password. The default implementation creates a new
   * UpdatePasswordWidget.
   *
   * \sa letUpdatePassword()
   */
  virtual WWidget *createUpdatePasswordView(const User& user,
					    bool promptPassword);

  /*! \brief Creates a widget to login using OAuth.
   *
   * The default implementation renders the
   * <tt>"Wt.Auth.template.oauth-login"</tt> template, and adds an
   * icon for each OAuth service provider available.
   *
   * There's alot to say about making a usable login mechanism for
   * OAuth (and federated login services in general). See FIXME link
   * to google.
   *
   * \sa createLoginView();
   */
  virtual void createOAuthLogin();

  /*! \brief Creates a password prompt dialog.
   *
   * This creates a dialog which prompts the user for his
   * password. The user is taken from \p login object, which also
   * signals an eventual success using its Login::changed() signal.
   *
   * The default implementation creates a new PasswordPromptDialog.
   */
  virtual WDialog *createPasswordPromptDialog(Login& login);

  void attemptLogin();

  virtual void displayError(const WString& error);
  virtual void displayInfo(const WString& message);

protected:
  /*! \brief Creates the user-interface.
   *
   * This method is called just before an initial rendering, and creates
   * the initial view.
   *
   * The default implementation calls createLoginView() or
   * createLoggedInView() depending on whether a user is currently
   * logged in.
   */
  virtual void create();

  /*! \brief Shows a dialog.
   *
   * This shows a dialog. The default method creates a standard WDialog,
   * with the given \p title and \p contents as central widget.
   *
   * When the central widget is deleted, it deletes the dialog.
   */
  virtual WDialog *showDialog(const WString& title, WWidget *contents);

  virtual void render(WFlags<RenderFlag> flags);

private:
  const AuthService& baseAuth_;
  AbstractUserDatabase& users_;
  Login& login_;
  std::string basePath_;
  const AbstractPasswordService *passwordAuth_;
  std::vector<const OAuthService *> oAuth_;
  bool registrationEnabled_;

  bool created_;
  WDialog *dialog_, *messageBox_;
  EnterPasswordFields *enterPasswordFields_;

  void logout();
  void loginThrottle(int delay);
  void closeDialog();
  void onLoginChange();
  void onPathChange(const std::string& path);
  bool handleRegistrationPath(const std::string& path);

  void oAuthStateChange(OAuthProcess *process);
  void oAuthDone(OAuthProcess *process, const Identity& identity);
  void setAuthToken(const User& user);
};

  }
}

#endif // WT_AUTH_AUTH_WIDGET_H_
