/* Copyright (c) 2013, Vsevolod Stakhov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CFG_RCL_H_
#define CFG_RCL_H_

#include "config.h"
#include "ucl/include/ucl.h"
#include "uthash.h"

#define CFG_RCL_ERROR cfg_rcl_error_quark ()
static inline GQuark
cfg_rcl_error_quark (void)
{
	return g_quark_from_static_string ("cfg-rcl-error-quark");
}

struct rspamd_rcl_section;
struct config_file;

struct rspamd_rcl_struct_parser {
	gpointer user_struct;
	goffset offset;
	enum {
		RSPAMD_CL_FLAG_TIME_FLOAT = 0x1 << 0,
		RSPAMD_CL_FLAG_TIME_TIMEVAL = 0x1 << 1,
		RSPAMD_CL_FLAG_TIME_TIMESPEC = 0x1 << 2,
		RSPAMD_CL_FLAG_TIME_INTEGER = 0x1 << 3,
		RSPAMD_CL_FLAG_TIME_UINT_32 = 0x1 << 4,
		RSPAMD_CL_FLAG_INT_16 = 0x1 << 5,
		RSPAMD_CL_FLAG_INT_32 = 0x1 << 6,
		RSPAMD_CL_FLAG_INT_64 = 0x1 << 7,
		RSPAMD_CL_FLAG_INT_SIZE = 0x1 << 8,
		RSPAMD_CL_FLAG_STRING_PATH = 0x1 << 9
	} flags;
};

/**
 * Common handler type
 * @param cfg configuration
 * @param obj object to parse
 * @param ud user data (depends on section)
 * @param err error object
 * @return TRUE if a section has been parsed
 */
typedef gboolean (*rspamd_rcl_handler_t) (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * A handler type that is called at the end of section parsing
 * @param cfg configuration
 * @param ud user data
 */
typedef void (*rspamd_rcl_section_fin_t)(struct config_file *cfg, gpointer ud);

struct rspamd_rcl_default_handler_data {
	struct rspamd_rcl_struct_parser pd;
	const gchar *key;
	rspamd_rcl_handler_t handler;
	UT_hash_handle hh;
};

struct rspamd_rcl_section {
	const gchar *name;					/**< name of section */
	rspamd_rcl_handler_t handler;		/**< handler of section attributes */
	enum ucl_type type;			/**< type of attribute */
	gboolean required;					/**< whether this param is required */
	gboolean strict_type;				/**< whether we need strict type */
	UT_hash_handle hh;					/** hash handle */
	struct rspamd_rcl_section *subsections; /**< hash table of subsections */
	struct rspamd_rcl_default_handler_data *default_parser; /**< generic parsing fields */
	rspamd_rcl_section_fin_t fin; /** called at the end of section parsing */
	gpointer fin_ud;
};

/**
 * Init common sections known to rspamd
 * @return top section
 */
struct rspamd_rcl_section* rspamd_rcl_config_init (void);

/**
 * Get a section specified by path, it understand paths separated by '/' character
 * @param top top section
 * @param path '/' divided path
 * @return
 */
struct rspamd_rcl_section *rspamd_rcl_config_get_section (struct rspamd_rcl_section *top,
		const char *path);

/**
 * Read RCL configuration and parse it to a config file
 * @param top top section
 * @param cfg target configuration
 * @param obj object to handle
 * @return TRUE if an object can be parsed
 */
gboolean rspamd_read_rcl_config (struct rspamd_rcl_section *top,
		struct config_file *cfg, const ucl_object_t *obj, GError **err);


/**
 * Parse default structure for a section
 * @param section section
 * @param cfg config file
 * @param obj object to parse
 * @param ptr ptr to pass
 * @param err error ptr
 * @return TRUE if the object has been parsed
 */
gboolean rspamd_rcl_section_parse_defaults (struct rspamd_rcl_section *section,
		struct config_file *cfg, const ucl_object_t *obj, gpointer ptr,
		GError **err);
/**
 * Here is a section of common handlers that accepts rcl_struct_parser
 * which itself contains a struct pointer and the offset of a member in a
 * specific structure
 */

/**
 * Parse a string field of a structure
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a string value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_string (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * Parse an integer field of a structure
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_integer (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);


/**
 * Parse a float field of a structure
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_double (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * Parse a time field of a structure
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure (flags mean the exact structure used)
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_time (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * Parse a string list field of a structure presented by a GList* object
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure (flags mean the exact structure used)
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_string_list (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * Parse a boolean field of a structure
 * @param cfg config pointer
 * @param obj object to parse
 * @param ud struct_parser structure (flags mean the exact structure used)
 * @param section the current section
 * @param err error pointer
 * @return TRUE if a value has been successfully parsed
 */
gboolean rspamd_rcl_parse_struct_boolean (struct config_file *cfg, const ucl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err);

/**
 * Utility functions
 */

/**
 * Register new parser for a worker type of an option with the specified name
 * @param cfg config structure
 * @param type type of worker (GQuark)
 * @param name name of option
 * @param handler handler of option
 * @param target opaque target structure
 * @param offset offset inside a structure
 */
void rspamd_rcl_register_worker_option (struct config_file *cfg, gint type, const gchar *name,
		rspamd_rcl_handler_t handler, gpointer target, gsize offset, gint flags);

/**
 * Regiester a default parser for a worker
 * @param cfg config structure
 * @param type type of worker (GQuark)
 * @param func handler function
 * @param ud userdata for handler function
 */
void rspamd_rcl_register_worker_parser (struct config_file *cfg, gint type,
		gboolean (*func)(ucl_object_t *, gpointer), gpointer ud);
#endif /* CFG_RCL_H_ */