// -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*-

/* Copyright (C) 2007 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

_CONFIGINCLUDE(giommconfig.h)

#m4 _PUSH(SECTION_CC_PRE_INCLUDES)
#undef G_DISABLE_DEPRECATED
#define GLIB_DISABLE_DEPRECATION_WARNINGS 1
#m4 _POP()

#include <giomm/actiongroup.h>
#include <giomm/actionmap.h>
#include <giomm/applicationcommandline.h>
#include <giomm/file.h>
#include <glibmm/object.h>
#include <glibmm/optionentry.h>
#include <glibmm/optiongroup.h>
#include <glibmm/variant.h>
#include <glibmm/variantdict.h>
#include <giomm/dbusconnection.h>
#include <giomm/notification.h>

_DEFS(giomm,gio)
_PINCLUDE(glibmm/private/object_p.h)

namespace Gio
{

_WRAP_ENUM(ApplicationFlags, GApplicationFlags, NO_GTYPE)

/** Application - Core application class.
 * An Application is the foundation of an application, unique for a given
 * application identifier. The Application class wraps some low-level
 * platform-specific services and is intended to act as the foundation for
 * higher-level application classes such as Gtk::Application or MxApplication.
 * In general, you should not use this class outside of a higher level
 * framework.
 *
 * One of the core features that Application provides is process uniqueness,
 * in the context of a "session". The session concept is platform-dependent,
 * but corresponds roughly to a graphical desktop login. When your application
 * is launched again, its arguments are passed through platform communication
 * to the already running program. The already running instance of the program
 * is called the <i>primary instance</i>.
 *
 * Before using Application, you must choose an "application identifier". The
 * expected form of an application identifier is very close to that of of a
 * <a href="
 * http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface">DBus
 * bus name</a>. Examples include: "com.example.MyApp",
 * "org.example.internal-apps.Calculator". For details on valid application
 * identifiers, see id_is_valid().
 *
 * Application provides convenient life cycle management by maintaining a
 * <i>use count</i> for the primary application instance. The use count can be
 * changed using hold() and release(). If it drops to zero, the application
 * exits.
 *
 * Application also implements the ActionGroup and ActionMap
 * interfaces and lets you easily export actions by adding them with
 * Gio::ActionMap::add_action(). When invoking an action by calling
 * Gio::ActionGroup::activate_action() on the application, it is always
 * invoked in the primary instance.
 *
 * There is a number of different entry points into an Application:
 *
 * - via 'Activate' (i.e. just starting the application)
 * - via 'Open' (i.e. opening some files)
 * - via activating an action
 *
 * The signal_startup() signal lets you handle the application initialization
 * for all of these in a single place.
 *
 * See the C API docs for an example.
 *
 * @newin{2,32}
 */
class Application : public Glib::Object, public ActionGroup, public ActionMap
{
  _CLASS_GOBJECT(Application, GApplication, G_APPLICATION, Glib::Object, GObject)
  _IMPLEMENTS_INTERFACE(ActionGroup)
  _IMPLEMENTS_INTERFACE(ActionMap)

protected:
  /** Constructs an application instance.
   * If no application ID is given then some features (most notably application uniqueness) will be disabled.
   *
   * @param application_id The application ID.
   * @param flags The application flags.
   */
  explicit Application(const Glib::ustring& application_id = Glib::ustring(), ApplicationFlags flags = APPLICATION_FLAGS_NONE);
  _IGNORE(g_application_new)

public:
  _CUSTOM_DTOR()

  /** The OptionType enum values determine the expected type of a command line option.
   * If an option expects an extra argument, it can be specified in several ways;
   * with a short option: "-x arg", with a long option: "--name arg" or combined
   * in a single argument: "--name=arg". All option types except OPTION_TYPE_BOOL
   * expect an extra argument. OPTION_TYPE_STRING_VECTOR and
   * OPTION_TYPE_FILENAME_VECTOR accept more than one extra argument.
   *
   * The descriptions of the enum values show what type of Glib::Variant<>
   * is stored in a Glib::VariantDict.
   *
   * @newin{2,42}
   *
   * @ingroup glibmmEnums
   */
  enum OptionType
  {
    OPTION_TYPE_BOOL,   ///< bool
    OPTION_TYPE_STRING, ///< Glib::ustring
    OPTION_TYPE_INT,    ///< gint32
    //OPTION_TYPE_CALLBACK,
    OPTION_TYPE_FILENAME = OPTION_TYPE_INT+2, ///< std::string
    OPTION_TYPE_STRING_VECTOR,   ///< std::vector<Glib::ustring>
    OPTION_TYPE_FILENAME_VECTOR, ///< std::vector<std::string>
    OPTION_TYPE_DOUBLE,          ///< double
    OPTION_TYPE_INT64            ///< gint64
  };

  /** Creates an application instance.
   * If no application ID is given then some features (most notably application uniqueness) will be disabled.
   *
   * @param application_id The application ID.
   * @param flags The application flags.
   */
  _WRAP_CREATE(const Glib::ustring& application_id = Glib::ustring(), ApplicationFlags flags = APPLICATION_FLAGS_NONE)

  _WRAP_METHOD(static bool id_is_valid(const Glib::ustring& application_id), g_application_id_is_valid)

  _WRAP_METHOD(Glib::ustring get_id() const, g_application_get_application_id)
  _WRAP_METHOD(void set_id(const Glib::ustring& application_id), g_application_set_application_id)


  _WRAP_METHOD(Glib::RefPtr<DBus::Connection> get_dbus_connection(), g_application_get_dbus_connection, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const DBus::Connection> get_dbus_connection() const, g_application_get_dbus_connection, refreturn, constversion)

  _WRAP_METHOD(Glib::ustring get_dbus_object_path() const, g_application_get_dbus_object_path)

  _WRAP_METHOD(guint get_inactivity_timeout() const, g_application_get_inactivity_timeout)
  _WRAP_METHOD(void set_inactivity_timeout(guint inactivity_timeout), g_application_set_inactivity_timeout)

  _WRAP_METHOD(ApplicationFlags get_flags() const, g_application_get_flags)
  _WRAP_METHOD(void set_flags(ApplicationFlags flags), g_application_set_flags)

  _WRAP_METHOD(void set_action_group(const Glib::RefPtr<ActionGroup>& action_group), g_application_set_action_group,
    deprecated "Use the Gio::ActionMap interface instead.")

  //Note: We would like to add a group, not just some entries,
  //so we can do pre and post parsing. See https://bugzilla.gnome.org/show_bug.cgi?id=727602
  //but instead we need to use the VariantDict passed to the handle_local_options signal
  //and provided by ApplicationCommandLine::get_options_dict() in on_command_line().

  /** Adds a main option entry to be handled by the Application.
   *
   * This function is comparable to Glib::OptionGroup::add_entry() +
   * Glib::OptionContext::set_main_group().
   *
   * After the commandline arguments are parsed, the
   * signal_handle_local_options() signal will be emitted.  At this
   * point, the application can inspect the parsed values.
   *
   * Unlike OptionGroup + OptionContext, Application packs the arguments
   * into a Glib::VariantDict which is passed to the
   * signal_handle_local_options() handler, where it can be
   * inspected and modified. If Gio::APPLICATION_HANDLES_COMMAND_LINE is
   * set, then the resulting dictionary is sent to the primary instance,
   * where Gio::ApplicationCommandLine::get_options_dict() will return it.
   * This "packing" is done according to the type of the argument --
   * booleans for normal flags, Glib::ustring's for strings, std::string's for
   * filenames, etc.  The packing only occurs if the flag is given (ie: we
   * do not pack a "false" Variant in the case that a flag is missing).
   *
   * In general, it is recommended that all commandline arguments are
   * parsed locally.  The options dictionary should then be used to
   * transmit the result of the parsing to the primary instance, where
   * Glib::VariantDict::lookup_value() can be used.  For local options, it is
   * possible to consult (and potentially remove) the option from the options dictionary.
   *
   * This function is new in GLib 2.40.  Before then, the only real choice
   * was to send all of the commandline arguments (options and all) to the
   * primary instance for handling.  Application ignored them completely
   * on the local side.  Calling this function "opts in" to the new
   * behaviour, and in particular, means that unrecognised options will be
   * treated as errors.  Unrecognised options have never been ignored when
   * Gio::APPLICATION_HANDLES_COMMAND_LINE is unset.
   *
   * If signal_handle_local_options() needs to see the list of
   * filenames, then the use of G_OPTION_REMAINING as @a long_name is recommended.
   * G_OPTION_REMAINING can be used as a key into
   * the options dictionary.  If you do use G_OPTION_REMAINING then you
   * need to handle these arguments for yourself because once they are
   * consumed, they will no longer be visible to the default handling
   * (which treats them as filenames to be opened).
   *
   * @newin{2,42}
   *
   * @param arg_type A Gio::Application::OptionType.
   * @param long_name The long name of an option can be used to specify it
   *     in a commandline as `--long_name`. Every option must have a
   *     long name.
   * @param short_name If an option has a short name, it can be specified
   *     `-short_name` in a commandline. @a short_name must be a printable
   *     ASCII character different from '-', or '\0' if the option has no
   *     short name.
   * @param description The description for the option in `--help` output.
   * @param arg_description The placeholder to use for the extra argument parsed
   *     by the option in `--help` output.
   * @param flags Flags from Glib::OptionEntry::Flags. Do not set FLAG_FILENAME.
   *     Character encoding is chosen with @a arg_type.
   */
  void add_main_option_entry(OptionType arg_type, const Glib::ustring& long_name,
    gchar short_name = '\0', const Glib::ustring& description = Glib::ustring(),
    const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);
  _IGNORE(g_application_add_main_option_entries)

  /** Adds a main option entry to be handled by the Application.
   *
   * Adds a string option entry, but lets the callback @a slot parse the extra
   * argument instead of having it packed in a Glib::VariantDict.
   *
   * If you create more than one Application instance (unusual),
   * one Application instance can't add an option with the same name as
   * another instance adds. This restriction does not apply to the
   * add_main_option_entry() that takes an OptionType parameter.
   *
   * @newin{2,42}
   *
   * @see add_main_option_entry(OptionType, const Glib::ustring&,
   *   gchar, const Glib::ustring&, const Glib::ustring&, int)
   */
  void add_main_option_entry(const Glib::OptionGroup::SlotOptionArgString& slot,
    const Glib::ustring& long_name,
    gchar short_name = '\0', const Glib::ustring& description = Glib::ustring(),
    const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);

  /** Adds a main option entry to be handled by the Application.
   *
   * Adds a filename option entry, but lets the callback @a slot parse the extra
   * argument instead of having it packed in a Glib::VariantDict.
   *
   * If you create more than one Application instance (unusual),
   * one Application instance can't add an option with the same name as
   * another instance adds. This restriction does not apply to the
   * add_main_option_entry() that takes an OptionType parameter.
   *
   * @newin{2,42}
   *
   * @see add_main_option_entry(OptionType, const Glib::ustring&,
   *   gchar, const Glib::ustring&, const Glib::ustring&, int)
   */
  void add_main_option_entry_filename(const Glib::OptionGroup::SlotOptionArgFilename& slot,
    const Glib::ustring& long_name,
    gchar short_name = '\0', const Glib::ustring& description = Glib::ustring(),
    const Glib::ustring& arg_description = Glib::ustring(), int flags = 0);

  // _WRAP_METHOD(void add_option_group(Glib::OptionGroup& group), g_application_add_option_group)
  // add_option_group() is probably not very useful. If implemented, it must probably
  // be custom-implemented. See https://bugzilla.gnome.org/show_bug.cgi?id=727822#c10
  _IGNORE(g_application_add_option_group)

  _WRAP_METHOD(bool is_registered() const, g_application_get_is_registered)
  _WRAP_METHOD(bool is_remote() const, g_application_get_is_remote)

  //Renamed from register() because that is a C++ keyword.
  _WRAP_METHOD(bool register_application(const Glib::RefPtr<Gio::Cancellable>& cancellable{?}), g_application_register, errthrow)
  _IGNORE(g_application_impl_register)

  _WRAP_METHOD(void hold(), g_application_hold)
  _WRAP_METHOD(void release(), g_application_release)
  _WRAP_METHOD(void activate(), g_application_activate)

  typedef std::vector< Glib::RefPtr<File> > type_vec_files;

  /* Opens the given files.
   *
   * In essence, this results in the open signal being emitted
   * in the primary instance.
   *
   * @a hint is simply passed through to the open signal.  It is
   * intended to be used by applications that have multiple modes for
   * opening files (eg: "view" vs "edit", etc).
   *
   * The application must be registered before calling this method
   * and it must have the APPLICATION_HANDLES_OPEN flag set.
   *
   * @param files The files to open. This must be non-empty.
   * @param hint A hint.
   *
   * @newin{2,32}
   */
  void open(const type_vec_files& files, const Glib::ustring& hint = Glib::ustring());
  _IGNORE(g_application_open)

  /* Opens the given file.
   *
   * In essence, this results in the open signal being emitted
   * in the primary instance.
   *
   * @a hint is simply passed through to the open signal.  It is
   * intended to be used by applications that have multiple modes for
   * opening files (eg: "view" vs "edit", etc).
   *
   * The application must be registered before calling this method
   * and it must have the APPLICATION_HANDLES_OPEN flag set.
   *
   * @param file The file to open. This must be non-empty.
   * @param hint A hint.
   *
   * @newin{2,32}
   */
  void open(const Glib::RefPtr<Gio::File>& file, const Glib::ustring& hint = Glib::ustring());

  _WRAP_METHOD(int run(int argc, char** argv), g_application_run)

  _WRAP_METHOD(void quit(), g_application_quit)

  _WRAP_METHOD(static void set_default(const Glib::RefPtr<Application>& application), g_application_set_default)

  /// Unsets any existing default application.
  static void unset_default();

  _WRAP_METHOD(static Glib::RefPtr<Application> get_default(), g_application_get_default)

  _WRAP_METHOD(void mark_busy(), g_application_mark_busy)
  _WRAP_METHOD(void unmark_busy(), g_application_unmark_busy)

  _WRAP_METHOD(void send_notification(const Glib::ustring& id{?}, const Glib::RefPtr<Notification>& notification), g_application_send_notification)
  _WRAP_METHOD(void withdraw_notification(const Glib::ustring& id), g_application_withdraw_notification)

  _WRAP_PROPERTY("action-group", Glib::RefPtr<ActionGroup>)
  _WRAP_PROPERTY("application-id", Glib::ustring)
  _WRAP_PROPERTY("flags", ApplicationFlags)
  _WRAP_PROPERTY("inactivity-timeout", guint)
  _WRAP_PROPERTY("is-registered", bool)
  _WRAP_PROPERTY("is-remote", bool)

//#m4 _CONVERSION(`const gchar*', `const Glib::ustring&', `Glib::ustring($3)')
//#m4 _CONVERSION(`GVariant*', `const Glib::VariantBase&', `Glib::wrap($3, true)')

  _WRAP_SIGNAL(void startup(), "startup")
  _WRAP_SIGNAL(void activate(), "activate")

  //We wrap the open signal without _WRAP_SIGNAL(), because we need to change its parameters.
  //See bug https://bugzilla.gnome.org/show_bug.cgi?id=637457
  Glib::SignalProxy2< void,  const type_vec_files&, const Glib::ustring& > signal_open();
  _IGNORE_SIGNAL(open)

#m4 _CONVERSION(`GApplicationCommandLine*', `const Glib::RefPtr<ApplicationCommandLine>&',`Glib::wrap($3, true)')
  _WRAP_SIGNAL(int command_line(const Glib::RefPtr<ApplicationCommandLine>& command_line), "command-line")

  //TODO: Remove no_default_handler when we can break ABI
  //TODO: Avoid the use of the Variants in the VariantDict?
  //options must be non-const. The handler is meant to modify it. See the description
  //of add_main_option_entry(OptionType, ...).
#m4 _CONVERSION(`GVariantDict*',`const Glib::RefPtr<Glib::VariantDict>&',`Glib::wrap($3, true)')
  _WRAP_SIGNAL(int handle_local_options(const Glib::RefPtr<Glib::VariantDict>& options), "handle-local-options", no_default_handler)

protected:
  virtual void on_open(const type_vec_files& files, const Glib::ustring& hint);

#m4begin
  _PUSH(SECTION_PCC_CLASS_INIT_DEFAULT_SIGNAL_HANDLERS)
  klass->open = &open_callback;
  _SECTION(SECTION_PH_DEFAULT_SIGNAL_HANDLERS)
  static void open_callback(GApplication* self, GFile** files, gint n_files, const gchar* hint);
  _POP()
#m4end

#m4 _CONVERSION(`char**&', `gchar***',`&($3)')
#m4 _CONVERSION(`gchar***', `char**&',`*($3)')
  _WRAP_VFUNC(bool local_command_line(char**& arguments, int& exit_status), local_command_line)

#m4 _CONVERSION(`GVariant*',`const Glib::VariantBase&',`Glib::wrap($3,true)')

  _WRAP_VFUNC(void before_emit(const Glib::VariantBase& platform_data), "before_emit")
  _WRAP_VFUNC(void after_emit(const Glib::VariantBase& platform_data), "after_emit")

  //TODO: File a bug about GVariantBuilder not being registered with the GType system first:
  //_WRAP_VFUNC(void add_platform_data(Glib::VariantBuilder* builder), "add_platform_data")

  _WRAP_VFUNC(void quit_mainloop(), "quit_mainloop")
  _WRAP_VFUNC(void run_mainloop(), "run_mainloop")


private:
  /** This is just a way to call Glib::init() (which calls g_type_init()) before
   * calling application_class_.init(), so that
   * g_application_get_type() will always succeed.
   * See https://bugzilla.gnome.org/show_bug.cgi?id=639925
   */
  const Glib::Class& custom_class_init();

  // Code, common to the public add_main_option_entry*() methods.
  void add_main_option_entry_private(GOptionArg arg, const Glib::ustring& long_name,
    gchar short_name, const Glib::ustring& description,
    const Glib::ustring& arg_description, int flags);
};

} // namespace Gio
