/**
	\file	util.h
	\brief	Header file for util.c. Contains all-purpose macros, types and function protos.

	This header file contains all general macros, types/enums/structs declaration and functions
	prototypes that are used as general tools.
*/

#ifndef _util_h_
#define _util_h_


#include <stdio.h>
#include <stdarg.h>

#include "config.h"

#define MIN(X,Y) ((X)>(Y)?(Y):(X))
#define MAX(X,Y) ((X)>(Y)?(X):(Y))

#ifndef ssize_t
#ifndef _GNU_SOURCE
#define ssize_t long int
#define _SSIZE_T
#endif
#endif

/* Mac OS X specific defines */
#ifdef OS_DARWIN
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* QNX specific defines */
#ifdef OS_QNX
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* BeOS specific defines */
#ifdef OS_BEOS
#include "gnu-strndup.h"
#endif

/* Sun Solaris (SunOS) specific defines */
#ifdef OS_SUNOS
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* HP-UX specific defines */
#ifdef OS_HPUX
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* IBM Aix specific defines */
#ifdef OS_AIX
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* FreeBSD specific defines */
#ifdef OS_FREEBSD
#ifdef __GNUC__
#if __GNUC__ >= 4
#define _SSIZE_T_DECLARED
#endif
#endif
#include "gnu-getline.h"
#include "gnu-strndup.h"
#endif

/* Microsoft Windows specific defines */
#ifdef OS_WIN32
#ifndef lstat
#define lstat(x, y) stat(x, y)
#endif
#ifndef S_ISLNK
#define S_ISLNK(m) (0)
#endif
#ifndef S_ISREG
#define S_ISREG(m) ((m&_S_IFREG)==_S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m)&_S_IFDIR)==_S_IFDIR)
#endif
#ifndef O_CREAT
#define O_CREAT _O_CREAT
#endif
#ifndef O_BINARY
#define O_BINARY _O_BINARY
#endif
#ifndef O_EXCL
#define O_EXCL _O_EXCL
#endif
#include "gnu-getline.h"
#include "gnu-mkstemp.h"
#include "gnu-strcasecmp.h"
#include "gnu-strndup.h"
#endif

/*	define per-system PATH separator (char/string used to separate
	sublevels of directories in a path
*/
#ifdef OS_WIN32
/**	\def	DIR_SEPARATOR_STRING
	\brief	Defines what's the path separator string for WIN32 platform.
*/
#define DIR_SEPARATOR_STRING "\\"
/**	\def	DIR_SEPARATOR_CHAR
	\brief	Defines what's the path separator character for WIN32 platform.
*/
#define DIR_SEPARATOR_CHAR '\\'
#else
/**	\def	DIR_SEPARATOR_STRING
	\brief	Defines what's the path separator string for POSIX systems.
*/
#define DIR_SEPARATOR_STRING "/"
/**	\def	DIR_SEPARATOR_CHAR
	\brief	Defines what's the path separator character for POSIX systems.
*/
#define DIR_SEPARATOR_CHAR '/'
#endif

/*	define some picture formats markers and tags
	note: markers and tags that belongs to supported formats and data
	structures are defined in the relevant .h files (Adobe, Exiv, IPTC, JFIF)
*/
#define GIF87_MARKER_STRING "GIF87"
#define GIF89_MARKER_STRING	"GIF89"
#define BMP_MARKER_STRING	"BM"
#define PNG_MARKER_STRING 0x89504e47UL
#define TIFF_LITTLE_ENDIAN_MARKER_STRING 0x49492a00UL
#define TIFF_BIG_ENDIAN_MARKER_STRING 0x4d4d002aUL
#define DJVU_MARKER_STRING	"DJvu"

/**	\typedef	bool
	\brief		Invent the boolean type.
*/
typedef short int bool;
#define false 0
#define true 1

/** \enum	OP
	\brief	Possible operation types.

	Define OPeration types.
*/
enum OP
{
	OP_CHECK,		/**< check mode (only check known data structures) */
	OP_DUMP,		/**< dump mode: check and dump data structures */
	OP_DUMP_FULL,	/**< full dump mode: check, dump data structures and relevant values */
	OP_DUMP_VALUE,	/**< dump value mode: check and dump requested value only */
	OP_FILTER,		/**< filter mode: check and filter datasets (filter is excluding (remove), including (keep),
						 or editing (add/replace). if no filter pattern is set, nothing will be done) */
	/* reserved */
	OP_EOT			/**< reserved */
};

/* define minimum and maximum log level */
#define MIN_LOG_LEVEL 0
#define MAX_LOG_LEVEL 4

/*	define some default, minimum and maximum values for dump wrapping of
	metadata
*/
#define DEFAULT_WRAP_DUMP 0
#define MIN_WRAP_DUMP 8
#define MAX_WRAP_DUMP 1024

/** \enum	LOG_LEVEL
	\brief	Possible log levels.

	Define log levels (from quiet to debug).
*/
enum LOG_LEVEL
{
	LOG_LEVEL_QUIET,	/**< */
	LOG_LEVEL_ERRORS,	/**< */
	LOG_LEVEL_WARNINGS,	/**< */
	LOG_LEVEL_INFO,		/**< */
	LOG_LEVEL_DEBUG,	/**< */
	/* reserved */
	LOG_LEVEL_EOT		/**< reserved */
};

/** \enum	SEVERITY
	\brief	Possible messages severities.

	Define all messages severities (from fatal error to debug).
*/
enum SEVERITY
{
	SEV_FATAL_ERROR,	/**< */
	SEV_ERROR,			/**< */
	SEV_WARNING,		/**< */
	SEV_INFO,			/**< */
	SEV_DEBUG			/**< reserved */
};

/** \enum	FILTER_MODE
	\brief	Possible filter modes.

	Define all possible filter modes.
*/
enum FILTER_MODE
{
	FILTER_NONE=0,	/**< */
	FILTER_INCLUDE=1,	/**< */
	FILTER_EXCLUDE=2,	/**< */
	FILTER_EDIT=4		/**< */
};

/** \enum	FILTER_EDIT_VALUE_TYPE
	\brief	Possible filter edit value types.

	Define all possible types for filter edits values.
*/
enum FILTER_EDIT_VALUE_TYPE
{
	FEV_NONE,	/**< */
	FEV_HEX,	/**< */
	FEV_TEXT	/**< */
};
	
/** \struct	op_label
	\brief	OP label data structure.

	Defines a structure so that labels and descriptions can be associated to
	enum OP values.
*/
struct op_label
{
	enum OP op;			/**< operation type ID */
	char* label;		/**< operation label (short, to be used in command-line invocation) */
	char* description;	/**< operation description (long, shown in usage help) */
};

/**	\struct	log_level_descr
	\brief	Log level data structure.

	Defines a data structure so that descriptions can be associated to enum
	LOG_LEVEL values.
*/
struct log_level_descr
{
	enum LOG_LEVEL level;	/**< log level ID */
	char* description;		/**< log level description (long, shown in usage help) */
};

/**	\struct	prefs_struct
	\brief	Preferences structure.

	Defines a data structure of the preferences (aimed to be globally used).
*/
struct prefs_struct
{
	enum LOG_LEVEL log_level;			/**< log level */
	bool technical;						/**< technical flag: is set, extra checks will be made and severity level of some messages might be increased */
	enum OP op;							/**< operation type */
	char* context_text;					/**< current context descriptive text */
	unsigned long int context_warnings;	/**< number of encountered warnings in current context */
	unsigned long int context_errors;	/**< number of encountered errors in current context */
	unsigned long int context_fixes;	/**< number of fixes done in current context */
	unsigned long int warnings;			/**< total number of encountered warnings */
	unsigned long int errors;			/**< total number of encountered errors */
	unsigned long int fixes;			/**< total number of fixed done */
	unsigned short int wrap_dump;		/**< wrap dump of dataset value flag: is set, long data dump will be wrapped */
	bool rewrite;						/**< rewrite mode flag: set according to the operation type and some other settings */
	bool rewrite_create_backup;			/**< create backup of written files flag: if set, backup will be created for each file open in write mode */
	bool fix;							/**< fix flag: is set, fixes will be done (if possible) when a broken structure is encountered */
	int filter_mode;					/**< filter mode flag: used when the operation type is filter */
	bool rewrite_cached;				/**< cached write mode flag: if set, written bytes will pass thru a cache in memory */
	bool extract_iptc;					/**< IPTC extraction flag: is set, IPTC datasets will be written to a file */
	bool extract_extension_append;		/**< extraction: filename extension append flag: is set, extension will be appended to the input filename (when writing datasets) */
	bool test_rewrite;					/**< test write flag: mostly provided for debug purposes: if set, files opened in write mode will not override any existing file but the temporary write file will be kept */
/*	bool edit_overwrite; */				/**< allow overwriting existing field values when adding (edit) */
};

/** \struct	parser_result
	\brief	Parser result structure.

	Define a data structure that is returned/modified by a parser, and which
	will contain a sum up of the parsing results.
*/
struct parser_result
{
	unsigned long int parsed_bytes;		/**< (parsed bytes) bytes eaten by the parser */
	unsigned long int written_bytes;	/**< (written bytes) bytes rewritten by the parser (following the OP mode and some other conditions) */
	bool ret; 							/**< (parser return code) false if any critical parsing error has been found */
};

/** \enum	EXIT_CODE
	\brief	Possible program exit codes.
	\todo	Make this portable (BSD, VMS, etc.), regarding to EXIT_SUCCESS/EXIT_FAILURE (man 3 exit).

	Defines the possible exit code returned by the program (from normal
	termination to fatal error).
*/
enum EXIT_CODE
{
	EXIT_CODE_NORMAL=0,				/**< indicates a normal termination of the program */
	EXIT_CODE_USAGE_ERROR,			/**< indicates a usage error (commonly command-line parser error) */
	EXIT_CODE_ASYNCHRONOUS_SIGNAL,	/**< indicates that the program stopped due to a signal trap */
	EXIT_CODE_NORMAL_WITH_WARNINGS,	/**< indicates that some non critical warning(s) have been encountered  */
	EXIT_CODE_NORMAL_WITH_ERRORS,	/**< indicates that some non critical error(s) have been encountered  */
	EXIT_CODE_FATAL_ERROR,			/**< indicates a fatal error has been encountered */
	/* reserved */
	EXIT_EOT						/**< reserved */
};

/** \struct	exit_code_label
	\brief	Exit code label data structure.

	Defines a data structure so that labels can be associated to enum EXIT_CODE
	values.
*/
struct exit_code_label
{
	enum EXIT_CODE code;	/**< exit code (see EXIT_CODE enum) */
	char* label;			/**< label */
};

/* functions prototypes */

/* low-level file management */
size_t fsize(FILE*);
bool fexist(const char*);
int frename(const char*, const char*);
int fremove(const char*, const char*);
bool fisfile(const char*);
bool fislink(const char*);
bool fisdir(const char*);
char* fextension(const char*);
char* fbasename(const char*);
char* ffilename(const char*);
char* fdirname(const char*);
char* fcleanpath(const char*);

/* high-level file management */
void backup_file(const char*);

/* memory random access */
unsigned char getbyte(unsigned char*, unsigned long int);
unsigned short int getword(unsigned char*, unsigned long int);
unsigned short int getrawword(unsigned char*, unsigned long int);
unsigned long int getlong(unsigned char*, unsigned long int);
unsigned long int getrawlong(unsigned char*, unsigned long int);
unsigned char* getstring(unsigned char*, unsigned long int, unsigned char*, const unsigned long int);
unsigned char* getraw(unsigned char*, unsigned long int, unsigned char*, const unsigned long int);
void setbyte(unsigned char*, unsigned long int, unsigned char);
void setword(unsigned char*, unsigned long int, unsigned short int);
void setlong(unsigned char*, unsigned long int, unsigned long int);

/* verbosity (output), error management */
void message_fatal_error(char*, va_list);
void message_error(char*, va_list);
void message_warning(char*, va_list);
void message_info(char*, va_list);
void message_debug(char*, va_list);
void fatal_error(char*, ...);
void error(char*, ...);
void warning(char*, ...);
void info(char*, ...);
void debug(char*, ...);
void message(enum SEVERITY, char*, ...);

/* context management (mostly for verbosity purposes) */
void context_set_text(char*);
void context_print_text(void);
void context_print_info(void);
void context_reset(void);

/* internal tables access */
struct op_label* op_match_label(char*);

/* write mode */
void init_rewrite();
size_t dump_rewrite(unsigned char*, unsigned long int);
void post_rewrite(unsigned char*, struct parser_result*);

/* type convertion */
bool get_unsigned_value(const char*, unsigned long int*);
bool get_hexa_value(const char*, unsigned long int*);

/* string manipulation */
size_t strpos(const char*, const char);
size_t strrpos(const char*, const char);

/* filter exclude/include pattern loading/decoding */
bool filter_parse_line(char*, size_t, unsigned long int);
void filter_load_line(char*);
void filter_load_file(FILE*);

/* filter edit pattern loading/decoding */
bool edit_parse_line(const char*, size_t, unsigned long int);
void edit_load_line(const char*);
void edit_load_file(FILE*);

/* dumping dataset values */
void dump_hexa(unsigned char*, unsigned long int);

/* @<file> files list loading (into a vector) */
void filenames_list_load(char***, int*, FILE*);
		
#endif
