/* 
 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _MFORMS_Utilities_H_
#define _MFORMS_Utilities_H_

/**
 * Implementation some miscellaneous stuff needed in mforms.
 */

#include <mforms/base.h>
#include <boost/function.hpp>

#include "cairo/cairo.h"

namespace mforms {
  // Constants for special folders on a system.
  enum MFORMS_EXPORT FolderType {
    Documents,          //!<< The path to the user's documents folder.
    Desktop,            //!<< The physical path to the user's desktop.
    ApplicationData,    //!<< Path to folder to store application specifc data for a user.

    // Platform specific folders.
    WinProgramFiles,    //!<< Windows only, 64 bit applications.
    WinProgramFilesX86, //!<< Windows only, 32 bit applications.

    ApplicationSettings //!<< Full path to App specific folder inside ApplicationData where config files and others are kept
  };

  enum MFORMS_EXPORT PasswordStoreScheme
  {
    SessionStorePasswordScheme = 1,
    PersistentStorePasswordScheme = 2
  };

  /**
   * Flags which describe which modifier key was pressed during a event.
   */
  enum ModifierKey {
    ModifierNoModifier =      0,
    ModifierControl    = 1 << 0,
    ModifierShift      = 1 << 1,
    ModifierCommand    = 1 << 2, // Command on Mac, Windows key on Windows.
    ModifierAlt        = 1 << 3,
  };

#ifndef SWIG
  inline ModifierKey operator| (ModifierKey a, ModifierKey b)
  {
    return (ModifierKey) ((int) a | (int) b);
  }
#endif

  enum DialogResult {
    ResultOk     =  1,
    ResultCancel =  0,
    ResultOther  = -1
  };

  // Describes the type of message, confirmation etc. we want to show to the user.
  enum DialogType
  {
    DialogMessage,
    DialogError,
    DialogWarning,
    DialogQuery,
    DialogSuccess, // Like DialogMessage but with a special icon to signal a successful operation.
  };

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
  struct MFORMS_EXPORT UtilitiesImplPtrs
  {
    int (__stdcall *show_message)(const std::string &title, const std::string &text,
                                  const std::string &ok, const std::string &cancel,
                                  const std::string &other);
    int (__stdcall *show_error)(const std::string &title, const std::string &text,
                                  const std::string &ok, const std::string &cancel,
                                  const std::string &other);
    int (__stdcall *show_warning)(const std::string &title, const std::string &text,
                                  const std::string &ok, const std::string &cancel,
                                  const std::string &other);
    int (__stdcall *show_message_with_checkbox)(const std::string &title, const std::string &text,
                                                const std::string &ok, const std::string &cancel,
                                                const std::string &other,
                                                const std::string &checkbox_text, // empty text = default "Don't show this message again" text
                                                bool &remember_checked);

    void (__stdcall *show_wait_message)(const std::string &title, const std::string &text);
    bool (__stdcall *hide_wait_message)();
    bool (__stdcall *run_cancelable_wait_message)(const std::string &title, const std::string &text,
                                                  const boost::function<void ()> &start_task, const boost::function<bool ()> &cancel_task);
    void (__stdcall *stop_cancelable_wait_message)();
    
    void (__stdcall *set_clipboard_text)(const std::string &text);
    std::string (__stdcall *get_clipboard_text)();
    std::string (__stdcall *get_special_folder)(mforms::FolderType type);
    
    void (__stdcall *open_url)(const std::string &url);
    void (__stdcall *reveal_file)(const std::string &url); // TODO XXX Windows
    bool (__stdcall *move_to_trash)(const std::string &path);
    
    void (__stdcall *add_timeout)(float delay, const boost::function<bool ()> &callback);
    
    void (__stdcall *store_password)(const std::string &service, const std::string &account, const std::string &password);
    bool (__stdcall *find_password)(const std::string &service, const std::string &account, std::string &password);
    void (__stdcall *forget_password)(const std::string &service, const std::string &account);

    void* (__stdcall *perform_from_main_thread)(const boost::function<void* ()> &slot, bool wait_completion);
  };
#endif
#endif

  class Box;
  class Button;

    /** Various Utility functions */
  class MFORMS_EXPORT Utilities
  {
#ifdef SWIG
%ignore show_message(const std::string &title, const std::string &text, const std::string &ok, const std::string &cancel);
%ignore show_message(const std::string &title, const std::string &text, const std::string &ok);
%ignore show_warning(const std::string &title, const std::string &text, const std::string &ok, const std::string &cancel);
%ignore show_warning(const std::string &title, const std::string &text, const std::string &ok);
%ignore show_error(const std::string &title, const std::string &text, const std::string &ok, const std::string &cancel);
%ignore show_error(const std::string &title, const std::string &text, const std::string &ok);
#endif
  public:
    /** Show a message dialog. Return value is from the DialogResult enum.
     
     In Python, all arguments are mandatory. */
    static int show_message(const std::string &title, const std::string &text,
                            const std::string &ok, const std::string &cancel="",
                            const std::string &other="");
    /** Show an error dialog. Return value is from the DialogResult enum.
     
     In Python, all arguments are mandatory. */
    static int show_error(const std::string &title, const std::string &text,
                            const std::string &ok, const std::string &cancel="",
                            const std::string &other="");
    /** Show a warning dialog. Return value is from the DialogResult enum.
     
     In Python, all arguments are mandatory. */
    static int show_warning(const std::string &title, const std::string &text,
                            const std::string &ok, const std::string &cancel="",
                            const std::string &other="");

    /** Show a message dialog and save the answer, if the checkbox is enabled.
     
     In Python, all arguments are mandatory. */
    static int show_message_and_remember(const std::string &title, const std::string &text,
                                         const std::string &ok, const std::string &cancel,
                                         const std::string &other,
                                         const std::string &answer_id, const std::string &checkbox_text);
    static void forget_message_answers();
    static void set_message_answers_storage_path(const std::string &path);
  private:
    static void save_message_answers();

  public:
    static void show_wait_message(const std::string &title, const std::string &text);
    static bool hide_wait_message();
    
    static bool run_cancelable_task(const std::string &title, const std::string &text,
                                    const boost::function<void* ()> &task,
                                    const boost::function<bool ()> &cancel_task,
                                    void *&task_result);

    /** Asks the user to enter a string, which is returned to the caller.

     @param title - the title of the input dialog
     @param description - description of the value to enter.
     @param default_value - an initial value for the edit control
     @param ret_value - the string the user entered, can be empty or the default string too

     @return true if user presses ok or false if its cancelled.
     */
    static bool request_input(const std::string &title, const std::string &description,
      const std::string &default_value, std::string &ret_value);

    /** Prompts the user for a password and whether it should be stored.
     
     @param title - the title of the password dialog
     @param service - the service the password refers to (ie sudo@hostname, Mysql@hostname etc)
     @param username - the username the password corresponds to, if empty the user will be able to enter it
     @param ret_password - the password the user typed
     @param ret_store - true if the user clicks in "Store Password" checkbox
     
     @return true if user presses ok or false if its cancelled.
     
     In Python, ret_password and ret_store are returned as a tuple. */
    static bool ask_for_password_check_store(const std::string &title, const std::string &service,
      std::string &username /*in/out*/, std::string &ret_password /*out*/, bool &ret_store /*out*/);

    /** Prompts the user for a password.
     
     @param title - the title of the password dialog
     @param service - the service the password refers to (ie sudo@hostname, Mysql@hostname etc)
     @param username - the username the password corresponds to
     @param ret_password - the password the user typed
     
     @return true if user presses ok or false if its cancelled.
     
     If you need the username to be editable by the user, use credentials_for_service()
     
     In Python, ret_password is returned by the function. */
    static bool ask_for_password(const std::string &title, const std::string &service, const std::string &username,
                                 std::string &ret_password /*out*/);

    /** Tries to find a previously stored password and prompts the user if not found.
     
     @param title - the title of the password dialog
     @param service - the service the password refers to (ie sudo@hostname, Mysql@hostname etc)
     @param username - the username the password corresponds to, if empty the user will be able to enter it
     @param reset_password - whether the password should be reset without looking for a stored one
     @param ret_password - the password the user typed

     @return true if user presses ok or false if its cancelled.
     
     In Python, ret_password and ret_store are returned as a tuple. */
    static bool find_or_ask_for_password(const std::string &title, const std::string &service, const std::string &username,
                                         bool reset_password, std::string &ret_password /*out*/)
    {
      std::string tmp(username);
      return credentials_for_service(title, service, tmp, reset_password, ret_password);
    }

#ifndef SWIG
    /**
     * Function similar to ask_for_password, but it also allows to enter a user name if none is given on call.
     */
    static bool credentials_for_service(const std::string &title, const std::string &service, std::string &username /*in/out*/,
                                         bool reset_password, std::string &ret_password /*out*/);
#endif

    /** Store the password for the given service and account.
     
     @li In Windows, an encrypted Vault file is used.
     @li In Mac, the secure KeyChain is used.
     @li In Linux, the gnome-keychain is used, unless it's not available (such as in KDE).
     In that case, passwords will be forgotten when the application exits.
     */
    static void store_password(const std::string &service, const std::string &account, const std::string &password);
    /** Locates the password for the given service and account. 

     @return true if password was found else false.
     */
    static bool find_password(const std::string &service, const std::string &account, std::string &password);
    /** Clears the stored password for the given service and account */
    static void forget_password(const std::string &service, const std::string &account);
    
    
    /** Sets the given text to the system clipboard */
    static void set_clipboard_text(const std::string &text);
    /** Gets the text stored in the system clipboard */
    static std::string get_clipboard_text();

    /** Gets the path of the requested special folder:     
       Documents
       Desktop
     */
    static std::string get_special_folder(FolderType type);

    /** Opens the given URL in the default system browser. */
    static void open_url(const std::string &url);
    
    /** Moves the given file or folder to the trash. The file might be permanently
     deleted instead of being moved to trash, under some circumstances. */
    static bool move_to_trash(const std::string &path);

    /** Shows the given file path in the OS Finder/Explorer */
    static void reveal_file(const std::string &path);
#ifndef SWIG
    /** Sets up a callback to be called after a given interval. 
     
     Interval is in seconds, with fractional values allowed.
     The callback must return true if it wants to be triggered again
     */
    static void add_timeout(float interval, const boost::function<bool ()> &callback);
#endif
    /** Convenience function to add an OK and Cancel buttons in a box. 
     
     This function will reorder buttons according to the standard order in the platform
     (ie OK Cancel in Windows and Cancel OK elsewhere).
     */
    static void add_end_ok_cancel_buttons(mforms::Box *box, mforms::Button *ok, mforms::Button *cancel);

#ifndef SWIG
    // Don't wrap this from mforms, because the typeinfo isnt getting shared across
    // modules... uncomment this if that's solved
    static cairo_surface_t* load_icon(const std::string& name);
    static std::string shorten_string(cairo_t* cr, const std::string& text, double width);
#endif

#ifndef SWIG
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifdef _WIN32
  public:
    static void enter_modal_loop();
    static void leave_modal_loop();
    static bool in_modal_loop();
#endif
#endif
    
    static void *perform_from_main_thread(const boost::function<void* ()> &slot, bool wait_completion = true);
#endif // !SWIG

    static bool in_main_thread();
  };

};
#endif // _MFORMS_Utilities_H_
