/* Copyright(C) 2006 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, see <http://www.gnu.org/licenses/>.
 */

_DEFS(glibmm,glib)

#include <glibmmconfig.h>
#include <glibmm/ustring.h>
#include <glibmm/error.h>
#include <glibmm/utility.h>
#include <glibmm/refptr.h>
#include <glib.h>
#include <vector>

#ifndef DOXYGEN_SHOULD_SKIP_THIS
extern "C" { typedef struct _GKeyFile GKeyFile; }
#endif

namespace Glib
{

/** Exception class for KeyFile errors.
 */
_WRAP_GERROR(KeyFileError, GKeyFileError, G_KEY_FILE_ERROR, NO_GTYPE, decl_prefix GLIBMM_API)

/** This class lets you parse, edit or create files containing groups of key-value pairs, which we call key files
 * for lack of a better name. Several freedesktop.org specifications use key files now, e.g the Desktop Entry
 * Specification and the Icon Theme Specification.
 *
 * The syntax of key files is described in detail in the Desktop Entry Specification, here is a quick summary: Key
 * files consists of groups of key-value pairs, interspersed with comments.
 *
 * @code
 * # this is just an example
 * # there can be comments before the first group
 *
 * [First Group]
 *
 * Name=Key File Example\tthis value shows\nescaping
 *
 * # localized strings are stored in multiple key-value pairs
 * Welcome=Hello
 * Welcome[de]=Hallo
 * Welcome[fr]=Bonjour
 * Welcome[it]=Ciao
 *
 * [Another Group]
 *
 * Numbers=2;20;-200;0
 *
 * Booleans=true;false;true;true
 * @endcode
 *
 * Lines beginning with a '#' and blank lines are considered comments.
 *
 * Groups are started by a header line containing the group name enclosed in '[' and ']', and ended implicitly by
 * the start of the next group or the end of the file. Each key-value pair must be contained in a group.
 *
 * Key-value pairs generally have the form key=value, with the exception of localized strings, which have the form
 * key[locale]=value. Space before and after the '=' character are ignored. Newline, tab, carriage return and
 * backslash characters in value are escaped as \\n, \\t, \\r, and \\\\, respectively. To preserve leading spaces in
 * values, these can also be escaped as \\s.
 *
 * Key files can store strings (possibly with localized variants), integers, booleans and lists of these. Lists are
 * separated by a separator character, typically ';' or ','. To use the list separator character in a value in a
 * list, it has to be escaped by prefixing it with a backslash.
 *
 * This syntax is obviously inspired by the .ini files commonly met on Windows, but there are some important
 * differences:
 * - .ini files use the ';' character to begin comments, key files use the '#' character.
 * - Key files allow only comments before the first group.
 * - Key files are always encoded in UTF-8.
 * - Key and Group names are case-sensitive, for example a group called [GROUP] is a different group from [group].
 *
 * Note that in contrast to the Desktop Entry Specification, groups in key files may contain the same key multiple
 * times; the last entry wins. Key files may also contain multiple groups with the same name; they are merged
 * together. Another difference is that keys and group names in key files are not restricted to ASCII characters.
 *
 * @newin{2,14}
 */
class GLIBMM_API KeyFile final
{
  _CLASS_OPAQUE_REFCOUNTED(KeyFile, GKeyFile, g_key_file_new, g_key_file_ref, g_key_file_unref, GLIBMM_API)
  _IGNORE(g_key_file_new, g_key_file_ref, g_key_file_unref, g_key_file_free)

public:

  _WRAP_ENUM(Flags, GKeyFileFlags, NO_GTYPE, decl_prefix GLIBMM_API)

  //TODO: Maybe replace all the get_*/set_* methods with some generic get/set
  //methods when it is possible.

  _WRAP_METHOD(bool load_from_file(const std::string& file, Flags flags = Flags::NONE), g_key_file_load_from_file, errthrow)

  /** Loads a KeyFile from memory
   * @param data The data to use as a KeyFile
   * @param flags Bitwise combination of the flags to use for the KeyFile
   * @return true if the KeyFile was successfully loaded, false otherwise
   * @throw Glib::KeyFileError
   */
  bool load_from_data(const Glib::ustring& data, Flags flags = Flags::NONE);
  _IGNORE(g_key_file_load_from_data)

#m4 _CONVERSION(`const std::vector<std::string>& ', `const gchar**', `const_cast<const gchar**>($3.data())')

  _IGNORE(g_key_file_load_from_dirs)

  _WRAP_METHOD_DOCS_ONLY(g_key_file_load_from_dirs, errthrow "Glib::KeyFileError, Glib::FileError")
  bool load_from_dirs(const std::string& file, const std::vector<std::string>&  search_dirs, std::string& full_path, Flags flags = Flags::NONE);

  /** Looks for a KeyFile named @a file in the paths returned from
   * g_get_user_data_dir() and g_get_system_data_dirs() and loads them
   * into the keyfile object, placing the full path to the file in
   * @a full_path.
   * @param file The file to search for
   * @param[out] full_path Return location for a string containing the full path of the file
   * @param flags Bitwise combination of the flags to use for the KeyFile
   * @return true if the KeyFile was successfully loaded, false otherwise
   * @throw Glib::KeyFileError
   * @throw Glib::FileError
   */
  bool load_from_data_dirs(const std::string& file, std::string& full_path, Flags flags = Flags::NONE);
  _IGNORE(g_key_file_load_from_data_dirs)

  /** Outputs the KeyFile as a string
   * @return A string object holding the contents of KeyFile
   * @throw Glib::KeyFileError
   */
  Glib::ustring to_data();
  _IGNORE(g_key_file_to_data)

  _WRAP_METHOD(bool save_to_file(const std::string& filename), g_key_file_save_to_file, errthrow "Glib::FileError")

  _WRAP_METHOD(Glib::ustring get_start_group() const, g_key_file_get_start_group)

  /** Gets a list of all groups in the KeyFile
   * @returns A list containing the names of the groups
   */
  std::vector<Glib::ustring> get_groups() const;
  _IGNORE(g_key_file_get_groups)

  /** Gets a list of all keys from the group @a group_name.
   * @param group_name The name of a group
   * @returns A list containing the names of the keys in @a group_name
   * @throw Glib::KeyFileError
   */
  std::vector<Glib::ustring> get_keys(const Glib::ustring& group_name) const;
  _IGNORE(g_key_file_get_keys)

  _WRAP_METHOD(bool has_group(const Glib::ustring& group_name) const, g_key_file_has_group)
  _WRAP_METHOD(bool has_key(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_has_key, errthrow)

  _WRAP_METHOD(Glib::ustring get_value(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_value, errthrow)
  _WRAP_METHOD(Glib::ustring get_string(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_string, errthrow)

  _WRAP_METHOD(Glib::ustring get_locale_for_key(const Glib::ustring& group_name,
    const Glib::ustring& key, const Glib::ustring& locale{NULL} = {}) const,
    g_key_file_get_locale_for_key, newin "2,60")

  /** Gets the value associated with @a key under @a group_name translated
   * into the current locale.
   * @return the value as a Glib::ustring
   * @throw Glib::KeyFileError
  */
  Glib::ustring get_locale_string(const Glib::ustring& group_name, const Glib::ustring& key) const;
  _IGNORE(g_key_file_get_locale_string)

  _WRAP_METHOD(Glib::ustring get_locale_string(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& locale) const, g_key_file_get_locale_string, errthrow)

  /** Gets the value in the first group, under @a key, interpreting it as
   * a boolean.
   * @param key The name of the key.
   * @return The value of @a key as a boolean.
   * @throw Glib::KeyFileError.
   * @newin{2,6}
   */
  bool get_boolean(const Glib::ustring& key) const;

  _WRAP_METHOD(bool get_boolean(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_boolean, errthrow)

  /** Gets the value in the first group, under @a key, interpreting it as
   * an integer.
   * @param key The name of the key
   * @return The value of @a key as an integer
   * @throw Glib::KeyFileError
   * @newin{2,6}
   */
  int get_integer(const Glib::ustring& key) const;

  _WRAP_METHOD(int get_integer(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_integer, errthrow)

  /** Gets the value in the first group, under @a key, interpreting it as
   * a signed 64-bit integer. This is similar to get_integer() but can return
   * 64-bit results without truncation.
   * @param key The name of the key.
   * @return The value of @a key as a signed 64-bit integer, or <tt>0</tt> if
   * the key was not found or could not be parsed.
   * @throw Glib::KeyFileError.
   * @newin{2,28}
   */
  gint64 get_int64(const Glib::ustring& key) const;

  _WRAP_METHOD(gint64 get_int64(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_int64, errthrow "Glib::KeyFileError")

  /** Gets the value in the first group, under @a key, interpreting it as
   * an unsigned 64-bit integer. This is similar to get_integer() but can
   * return large positive results without truncation.
   * @param key The name of the key.
   * @return The value of @a key as an unsigned 64-bit integer, or <tt>0</tt>
   * if the key was not found or could not be parsed.
   * @throw Glib::KeyFileError.
   * @newin{2,28}
   */
  guint64 get_uint64(const Glib::ustring& key) const;

  _WRAP_METHOD(guint64 get_uint64(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_uint64, errthrow "Glib::KeyFileError")

  /** Gets the value in the first group, under @a key, interpreting it as
   * a double.
   * @param key The name of the key
   * @return The value of @a key as an double
   * @throw Glib::KeyFileError
   *
   * @newin{2,14}
   */
  double get_double(const Glib::ustring& key) const;

  _WRAP_METHOD(double get_double(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_double, errthrow)

  _WRAP_METHOD(void set_double(const Glib::ustring& group_name, const Glib::ustring& key, double value), g_key_file_set_double)

  /** Associates a new double value with @a key under the start group.
   * If @a key  cannot be found then it is created.
   *
   * @newin{2,12}
   *
   * @param key A key.
   * @param value An double value.
   */
  void set_double(const Glib::ustring& key, double value);

  /** Returns the values associated with @a key under @a group_name
   * @param group_name The name of a group
   * @param key The name of a key
   * @return A list containing the values requested
   * @throw Glib::KeyFileError
   */
  std::vector<Glib::ustring> get_string_list(const Glib::ustring& group_name, const Glib::ustring& key) const;
  _IGNORE(g_key_file_get_string_list)

  /** Returns the values associated with @a key under @a group_name
   * translated into the current locale, if available.
   * @param group_name The name of a group
   * @param key The name of a key
   * @return A list containing the values requested
   * @throw Glib::KeyFileError
   */
  std::vector<Glib::ustring> get_locale_string_list(const Glib::ustring& group_name, const Glib::ustring& key) const;

  /** Returns the values associated with @a key under @a group_name
   * translated into @a locale, if available.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param locale The name of a locale
   * @return A list containing the values requested
   * @throw Glib::KeyFileError
   */
  std::vector<Glib::ustring> get_locale_string_list(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& locale) const;
  _IGNORE(g_key_file_get_locale_string_list)

  /** Returns the values associated with @a key under @a group_name
   * @param group_name The name of a group
   * @param key The name of a key
   * @return A list of booleans
   * @throw Glib::KeyFileError
   */
  std::vector<bool> get_boolean_list(const Glib::ustring& group_name, const Glib::ustring& key) const;
  _IGNORE(g_key_file_get_boolean_list)

  /** Returns the values associated with @a key under @a group_name
   * @param group_name The name of a group
   * @param key The name of a key
   * @return A list of integers
   * @throw Glib::KeyFileError
   */
  std::vector<int> get_integer_list(const Glib::ustring& group_name, const Glib::ustring& key) const;
  _IGNORE(g_key_file_get_integer_list)

  /** Returns the values associated with @a key under @a group_name
   * @param group_name The name of a group
   * @param key The name of a key
   * @return A list of doubles
   * @throw Glib::KeyFileError
   */
  std::vector<double> get_double_list(const Glib::ustring& group_name, const Glib::ustring& key) const;
  _IGNORE(g_key_file_get_double_list)

  /** Get comment from top of file
   * @return The comment
   * @throw Glib::KeyFileError
   */
  Glib::ustring get_comment() const;

  /** Get comment from above a group
   * @param group_name The group
   * @return The comment
   * @throw Glib::KeyFileError
   */
  Glib::ustring get_comment(const Glib::ustring& group_name) const;

  _WRAP_METHOD(Glib::ustring get_comment(const Glib::ustring& group_name, const Glib::ustring& key) const, g_key_file_get_comment, errthrow)

  _WRAP_METHOD(void set_list_separator(gchar separator), g_key_file_set_list_separator)
  _WRAP_METHOD(void set_value(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& value), g_key_file_set_value)
  _WRAP_METHOD(void set_string(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& string), g_key_file_set_string)
  _WRAP_METHOD(void set_locale_string(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& locale, const Glib::ustring& string), g_key_file_set_locale_string)
  _WRAP_METHOD(void set_boolean(const Glib::ustring& group_name, const Glib::ustring& key, bool value), g_key_file_set_boolean)
  _WRAP_METHOD(void set_integer(const Glib::ustring& group_name, const Glib::ustring& key, int value), g_key_file_set_integer)
  _WRAP_METHOD(void set_int64(const Glib::ustring& group_name, const Glib::ustring& key, gint64 value), g_key_file_set_int64)
  _WRAP_METHOD(void set_uint64(const Glib::ustring& group_name, const Glib::ustring& key, guint64 value), g_key_file_set_uint64)

  /** Sets a list of string values for @a key under @a group_name. If
   * key cannot be found it is created. If @a group_name cannot be found
   * it is created.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param list A list holding objects of type Glib::ustring
   */
  void set_string_list(const Glib::ustring& group_name, const Glib::ustring& key, const std::vector<Glib::ustring>&  list);
  _IGNORE(g_key_file_set_string_list)

  /** Sets a list of string values for the @a key under @a group_name and marks
   * them as being for @a locale. If the @a key or @a group_name cannot be
   * found, they are created.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param locale A locale
   * @param list A list holding objects of type Glib::ustring
   */
  void set_locale_string_list(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& locale, const std::vector<Glib::ustring>&  list);
  _IGNORE(g_key_file_set_locale_string_list)

  /** Sets a list of booleans for the @a key under @a group_name.
   * If either the @a key or @a group_name cannot be found they are created.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param list A list holding object of type bool
   */
  void set_boolean_list(const Glib::ustring& group_name, const Glib::ustring& key, const std::vector<bool>&  list);
  _IGNORE(g_key_file_set_boolean_list)

  /** Sets a list of integers for the @a key under @a group_name.
   * If either the @a key or @a group_name cannot be found they are created.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param list A list holding object of type int
   */
  void set_integer_list(const Glib::ustring& group_name, const Glib::ustring& key, const std::vector<int>&  list);
  _IGNORE(g_key_file_set_integer_list)

  /** Sets a list of doubles for the @a key under @a group_name.
   * If either the @a key or @a group_name cannot be found they are created.
   * @param group_name The name of a group
   * @param key The name of a key
   * @param list A list holding object of type int
   *
   * @newin{2,14}
   */
  void set_double_list(const Glib::ustring& group_name, const Glib::ustring& key, const std::vector<double>&  list);
  _IGNORE(g_key_file_set_double_list)


  /** Places @a comment at the start of the file, before the first group.
   * @param comment The Comment
   * @throw Glib::KeyFileError
   */
  void set_comment(const Glib::ustring& comment);

  /** Places @a comment above @a group_name.
   * @param group_name The Group the comment should be above
   * @param comment The comment
   * @throw Glib::KeyFileError
   */
  void set_comment(const Glib::ustring& group_name, const Glib::ustring& comment);

  _WRAP_METHOD(void set_comment(const Glib::ustring& group_name, const Glib::ustring& key, const Glib::ustring& comment), g_key_file_set_comment, errthrow)

  _WRAP_METHOD(void remove_comment(const Glib::ustring& group_name, const Glib::ustring& key), g_key_file_remove_comment, errthrow)
  _WRAP_METHOD(void remove_key(const Glib::ustring& group_name, const Glib::ustring& key), g_key_file_remove_key, errthrow)
  _WRAP_METHOD(void remove_group(const Glib::ustring& group_name), g_key_file_remove_group, errthrow)
};

} // namespace Glib
