/*
 * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf 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.
 *
 * NetSurf 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, see <http://www.gnu.org/licenses/>.
 */

/** \file
 * Option reading and saving (interface).
 *
 * Global options are defined in desktop/options.h
 * Distinct target options are defined in <TARGET>/options.h
 *
 * The implementation API is slightly compromised because it still has
 * "global" tables for both the default and current option tables.
 *
 * The initialisation and read/write interfaces take pointers to an
 * option table which would let us to make the option structure
 * opaque.
 *
 * All the actual acessors assume direct access to a global option
 * table (nsoptions). To avoid this the acessors would have to take a
 * pointer to the active options table and be implemented as functions
 * within nsoptions.c
 *
 * Indirect access would have an impact on performance of NetSurf as
 * the expected option lookup cost is currently that of a simple
 * dereference (which this current implementation keeps).
 */

#ifndef _NETSURF_UTILS_NSOPTION_H_
#define _NETSURF_UTILS_NSOPTION_H_

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

#include "utils/errors.h"

/* allow targets to include any necessary headers of their own */
#define NSOPTION_BOOL(NAME, DEFAULT)
#define NSOPTION_STRING(NAME, DEFAULT)
#define NSOPTION_INTEGER(NAME, DEFAULT)
#define NSOPTION_UINT(NAME, DEFAULT)
#define NSOPTION_COLOUR(NAME, DEFAULT)

#include "desktop/options.h"
#if defined(riscos)
#include "riscos/options.h"
#elif defined(nsgtk)
#include "gtk/options.h"
#elif defined(nsbeos)
#include "beos/options.h"
#elif defined(nsamiga)
#include "amiga/options.h"
#elif defined(nsframebuffer)
#include "framebuffer/options.h"
#elif defined(nsatari)
#include "atari/options.h"
#elif defined(nsmonkey)
#include "monkey/options.h"
#endif

#undef NSOPTION_BOOL
#undef NSOPTION_STRING
#undef NSOPTION_INTEGER
#undef NSOPTION_UINT
#undef NSOPTION_COLOUR



enum { OPTION_HTTP_PROXY_AUTH_NONE = 0,
       OPTION_HTTP_PROXY_AUTH_BASIC = 1,
       OPTION_HTTP_PROXY_AUTH_NTLM = 2 };

#define DEFAULT_MARGIN_TOP_MM 10
#define DEFAULT_MARGIN_BOTTOM_MM 10
#define DEFAULT_MARGIN_LEFT_MM 10
#define DEFAULT_MARGIN_RIGHT_MM 10
#define DEFAULT_EXPORT_SCALE 0.7

#ifndef DEFAULT_REFLOW_PERIOD
/** Default reflow time in cs */
#define DEFAULT_REFLOW_PERIOD 25
#endif

/** The options type. */
enum nsoption_type_e {
	OPTION_BOOL, /**< Option is a boolean. */
	OPTION_INTEGER, /**< Option is an integer. */
	OPTION_UINT, /**< Option is an unsigned integer */
	OPTION_STRING, /**< option is a heap allocated string. */
	OPTION_COLOUR /**< Option  is a netsurf colour. */
};

struct nsoption_s {
	const char *key;
	int key_len;
	enum nsoption_type_e type;
	union {
		bool b;
		int i;
		unsigned int u;
		char *s;
		const char *cs;
		colour c;
	} value;
};

/* construct the option enumeration */
#define NSOPTION_BOOL(NAME, DEFAULT) NSOPTION_##NAME,
#define NSOPTION_STRING(NAME, DEFAULT) NSOPTION_##NAME,
#define NSOPTION_INTEGER(NAME, DEFAULT) NSOPTION_##NAME,
#define NSOPTION_UINT(NAME, DEFAULT) NSOPTION_##NAME,
#define NSOPTION_COLOUR(NAME, DEFAULT) NSOPTION_##NAME,

enum nsoption_e {
#include "desktop/options.h"
#if defined(riscos)
#include "riscos/options.h"
#elif defined(nsgtk)
#include "gtk/options.h"
#elif defined(nsbeos)
#include "beos/options.h"
#elif defined(nsamiga)
#include "amiga/options.h"
#elif defined(nsframebuffer)
#include "framebuffer/options.h"
#elif defined(nsatari)
#include "atari/options.h"
#elif defined(nsmonkey)
#include "monkey/options.h"
#endif
	NSOPTION_LISTEND /* end of list */
};

#undef NSOPTION_BOOL
#undef NSOPTION_STRING
#undef NSOPTION_INTEGER
#undef NSOPTION_UINT
#undef NSOPTION_COLOUR

/**
 * global active option table.
 */
extern struct nsoption_s *nsoptions;

/**
 * global default option table.
 */
extern struct nsoption_s *nsoptions_default;

/**
 * default setting callback.
 */
typedef nserror(nsoption_set_default_t)(struct nsoption_s *defaults);


/**
 * Initialise option system.
 *
 * @param set_default callback to allow the customisation of the default
 *                    options.
 * @param ppots pointer to update to get options table or NULL.
 * @param pdefs pointer to update to get default options table or NULL.
 * @return The error status
 */
nserror nsoption_init(nsoption_set_default_t *set_default, struct nsoption_s **popts, struct nsoption_s **pdefs);


/**
 * Finalise option system
 *
 * Releases all resources allocated in the initialisation.
 *
 * @param opts the options table or NULL to use global table.
 * @param defs the default options table to use or NULL to use global table
 * return The error status
 */
nserror nsoption_finalise(struct nsoption_s *opts, struct nsoption_s *defs);


/**
 * Read choices file and set them in the passed table
 *
 * @param path The path to read the file from
 * @param opts The options table to enerate values from or NULL to use global
 * @return The error status
 */
nserror nsoption_read(const char *path, struct nsoption_s *opts);


/**
 * Write options that have changed from the defaults to a file.
 *
 * The \a nsoption_dump can be used to output all entries not just
 * changed ones.
 *
 * @param path The path to read the file from
 * @param opts The options table to enerate values from or NULL to use global
 * @param defs The default table to use or NULL to use global
 * @return The error status
 */
nserror nsoption_write(const char *path, struct nsoption_s *opts, struct nsoption_s *defs);


/**
 * Write all options to a stream.
 *
 * @param outf The stream to write to
 * @param opts The options table to enerate values from or NULL to use global
 * @return The error status
 */
nserror nsoption_dump(FILE *outf, struct nsoption_s *opts);


/**
 * Process commandline and set options approriately.
 *
 * @param pargc Pointer to the size of the argument vector.
 * @param argv The argument vector.
 * @param opts The options table to enerate values from or NULL to use global
 * @return The error status
 */
nserror nsoption_commandline(int *pargc, char **argv, struct nsoption_s *opts);


/**
 * Fill a buffer with an option using a format.
 *
 * The format string is copied into the output buffer with the
 * following replaced:
 * %k - The options key
 * %t - The options type
 * %V - value (HTML formatting)
 * %v - value (plain formatting)
 * %p - provenance either "user" or "default"
 *
 * @param string The buffer in which to place the results.
 * @param size The size of the string buffer.
 * @param option The option .
 * @param fmt The format string.
 * @return The number of bytes written to \a string or -1 on error
 */
int nsoption_snoptionf(char *string, size_t size, enum nsoption_e option, const char *fmt);


/**
 * Get the value of a boolean option.
 *
 * Gets the value of an option assuming it is a boolean type.
 * @note option type is unchecked so care must be taken in caller.
 */
#define nsoption_bool(OPTION) (nsoptions[NSOPTION_##OPTION].value.b)


/**
 * Get the value of an integer option.
 *
 * Gets the value of an option assuming it is a integer type.
 * @note option type is unchecked so care must be taken in caller.
 */
#define nsoption_int(OPTION) (nsoptions[NSOPTION_##OPTION].value.i)


/**
 * Get the value of an unsigned integer option.
 *
 * Gets the value of an option assuming it is a integer type.
 * @note option type is unchecked so care must be taken in caller.
 */
#define nsoption_uint(OPTION) (nsoptions[NSOPTION_##OPTION].value.u)


/**
 * Get the value of a string option.
 *
 * Gets the value of an option assuming it is a string type.
 * @note option type is unchecked so care must be taken in caller.
 */
#define nsoption_charp(OPTION) (nsoptions[NSOPTION_##OPTION].value.s)


/**
 * Get the value of a netsurf colour option.
 *
 * Gets the value of an option assuming it is a colour type.
 * @note option type is unchecked so care must be taken in caller.
 */
#define nsoption_colour(OPTION) (nsoptions[NSOPTION_##OPTION].value.c)


/** set a boolean option in the default table */
#define nsoption_set_bool(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.b = VALUE


/** set an integer option in the default table */
#define nsoption_set_int(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.i = VALUE


/** set a colour option in the default table */
#define nsoption_set_colour(OPTION, VALUE) nsoptions[NSOPTION_##OPTION].value.c = VALUE


/**
 * Set string option in specified table.
 *
 * Sets the string option to the value given freeing any resources
 * currently allocated to the option. If the passed string is empty it
 * is converted to the NULL value.
 *
 * @param opts The table to set option in
 * @param option_idx The option
 * @param s The string to set. This is used directly and not copied.
 */
nserror nsoption_set_tbl_charp(struct nsoption_s *opts, enum nsoption_e option_idx, char *s);

/** set string option in default table */
#define nsoption_set_charp(OPTION, VALUE) \
	nsoption_set_tbl_charp(nsoptions, NSOPTION_##OPTION, VALUE)

/** set string option in default table if currently unset */
#define nsoption_setnull_charp(OPTION, VALUE)				\
	do {								\
		if (nsoptions[NSOPTION_##OPTION].value.s == NULL) {	\
			nsoption_set_tbl_charp(nsoptions, NSOPTION_##OPTION, VALUE); \
		} else {						\
			free(VALUE);					\
		}							\
	} while (0)

#endif