#ifndef SFDO_DESKTOP_FILE_H
#define SFDO_DESKTOP_FILE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>

// libsfdo-desktop-file implements the desktop entry file format from the desktop entry
// specification, version 1.5:
//
//  https://specifications.freedesktop.org/desktop-entry-spec/1.5/

enum sfdo_desktop_file_error_code {
	SFDO_DESKTOP_FILE_ERROR_NONE = 0,

	// Input error
	SFDO_DESKTOP_FILE_ERROR_IO,

	// Unexpected NUL character
	SFDO_DESKTOP_FILE_ERROR_NT,

	// Invalid UTF-8 sequence
	SFDO_DESKTOP_FILE_ERROR_UTF8,

	// Memory allocation error
	SFDO_DESKTOP_FILE_ERROR_OOM,

	// Unspecified syntax error; deprecated, see more specific error codes below
	SFDO_DESKTOP_FILE_ERROR_SYNTAX,

	// Duplicate group
	SFDO_DESKTOP_FILE_ERROR_DUPLICATE_GROUP,

	// Duplicate key
	SFDO_DESKTOP_FILE_ERROR_DUPLICATE_KEY,

	// No default value for a key with a localized value
	SFDO_DESKTOP_FILE_ERROR_NO_DEFAULT_VALUE,

	// Unexpected line break
	SFDO_DESKTOP_FILE_ERROR_UNEXPECTED_LINE_BREAK,

	// Invalid group header character
	SFDO_DESKTOP_FILE_ERROR_INVALID_GROUP_CHARACTER,

	// Unexpected character after a group header
	SFDO_DESKTOP_FILE_ERROR_TRAILING_CHARACTER_AFTER_GROUP,

	// Unexpected entry
	SFDO_DESKTOP_FILE_ERROR_UNEXPECTED_ENTRY,

	// Expected '='
	SFDO_DESKTOP_FILE_ERROR_EXPECTED_EQUALS,

	// Invalid key character
	SFDO_DESKTOP_FILE_ERROR_INVALID_KEY_CHARACTER,

	// Unknown escape sequence
	SFDO_DESKTOP_FILE_ERROR_UNKNOWN_ESCAPE_SEQUENCE,
};

struct sfdo_desktop_file_error {
	enum sfdo_desktop_file_error_code code;
	int line, column;
};

struct sfdo_desktop_file_document;

struct sfdo_desktop_file_group;

struct sfdo_desktop_file_entry;

enum sfdo_desktop_file_load_options {
	// The default desktop entry file loading options.
	SFDO_DESKTOP_FILE_LOAD_OPTIONS_DEFAULT = 0,

	// Deprecated; same as SFDO_DESKTOP_FILE_LOAD_OPTION_ALLOW_DUPLICATE_GROUPS.
	SFDO_DESKTOP_FILE_LOAD_ALLOW_DUPLICATE_GROUPS = 1 << 0,

	// If this flag is set, the loader will allow groups with the same names. It becomes the
	// responsibility of the caller to handle duplicate groups correctly.
	SFDO_DESKTOP_FILE_LOAD_OPTION_ALLOW_DUPLICATE_GROUPS = 1 << 0,
};

// Load a document.
//
// locale is a string of form lang_COUNTRY.ENCODING@MODIFIER, where _COUNTRY, .ENCODING, and
// @MODIFIER may be omitted. It is used to select the best values for localized strings. May be
// NULL.
//
// options is a result of bitwise OR of zero or more enum sfdo_desktop_file_load_options values.
//
// Returns NULL on failure, in which case the information about the error is saved to error. error
// may be NULL.
struct sfdo_desktop_file_document *sfdo_desktop_file_document_load(
		FILE *fp, const char *locale, int options, struct sfdo_desktop_file_error *error);

// Destroy a document.
//
// document may be NULL, in which case the function is no-op.
void sfdo_desktop_file_document_destroy(struct sfdo_desktop_file_document *document);

// Get the first group of the document.
//
// Returns NULL if the document has no groups.
struct sfdo_desktop_file_group *sfdo_desktop_file_document_get_groups(
		struct sfdo_desktop_file_document *document);

// Get the description of an error by its code.
const char *sfdo_desktop_file_error_code_get_description(enum sfdo_desktop_file_error_code code);

// Get the next group of the document.
//
// Returns NULL if there is no next group.
struct sfdo_desktop_file_group *sfdo_desktop_file_group_get_next(
		struct sfdo_desktop_file_group *group);

// Get the name of a group.
//
// The length of the name is saved to len. len may be NULL.
const char *sfdo_desktop_file_group_get_name(struct sfdo_desktop_file_group *group, size_t *len);

// Get the group location in the file.
//
// The location is saved to line and column. line and column may be NULL.
void sfdo_desktop_file_group_get_location(
		struct sfdo_desktop_file_group *group, int *line, int *column);

// Get an entry from a group by a key.
//
// If key_len is equal to SFDO_NT, key is assumed to be null-terminated.
//
// Returns NULL if there is no matching entry.
struct sfdo_desktop_file_entry *sfdo_desktop_file_group_get_entry(
		struct sfdo_desktop_file_group *group, const char *key, size_t key_len);

// Get the entry key.
//
// The length of the key is saved to len. len may be NULL.
const char *sfdo_desktop_file_entry_get_key(struct sfdo_desktop_file_entry *entry, size_t *len);

// Get the entry value.
//
// The length of the value is saved to len. len may be NULL.
const char *sfdo_desktop_file_entry_get_value(struct sfdo_desktop_file_entry *entry, size_t *len);

// Get the localized entry value.
//
// The length of the value is saved to len. len may be NULL.
const char *sfdo_desktop_file_entry_get_localized_value(
		struct sfdo_desktop_file_entry *entry, size_t *len);

// Get the entry value list.
const struct sfdo_string *sfdo_desktop_file_entry_get_value_list(
		struct sfdo_desktop_file_entry *entry, size_t *n_items);

// Get the localized entry value list.
const struct sfdo_string *sfdo_desktop_file_entry_get_localized_value_list(
		struct sfdo_desktop_file_entry *entry, size_t *n_items);

// Get the entry location in the file.
//
// The location is saved to line and column. line and column may be NULL.
void sfdo_desktop_file_entry_get_location(
		struct sfdo_desktop_file_entry *entry, int *line, int *column);

#ifdef __cplusplus
}
#endif

#endif
