/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>
#include	<string.h>

#include	"base.h"
#include	"base_ct.h"
#include	"ConfigFile.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValueList.h"
#include	"ConfigKeyValueString.h"
#include	"file.h"
#include	"log.h"
#include	"memory.h"



/*\\\ CONSTANTES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

const char	ConfigFile::delimiter_list_0 [] = " \t\n,";



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise                                             */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*==========================================================================*/

ConfigFile::ConfigFile (const char *filename_0)
{
	_filename_0 = STRDUP (filename_0);
	if (_filename_0 == NULL)
	{
		LOG_printf ("ConfigFile::ConfigFile: Error: Couldn't allocate memory for filename.\n");
	}
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit                                                */
/*==========================================================================*/

ConfigFile::~ConfigFile (void)
{
	if (_filename_0 != NULL)
	{
		FREE (_filename_0);
		_filename_0 = NULL;
	}
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	ConfigFile::check_ok (void) const
{
	if (this == NULL)
	{
		LOG_printf ("ConfigFile::check_ok: Error: \"this\" pointer is NULL.\n");
		return (-1);
	}

	if (_filename_0 == NULL)
	{
		LOG_printf ("ConfigFile::check_ok: Error: _filename_0 is NULL.\n");
		return (-1);
	}

	/*** A faire ***/

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	ConfigFile::self_display (void) const
{

	/*** A faire ***/

}



signed long	ConfigFile::read (std::list <ConfigKey> &key_list)
{
	FILE		*file_ptr;
	long		index;
	char		*text_0;
	signed long	error_code;
	int		nbr_lexemes;
	char		*lex_0_array [ConfigFile_MAX_NBR_LEXEMES+1];

	key_list.clear ();

	/* Ouverture du fichier */
	file_ptr = FILE_fopen (_filename_0, "r");
	if (file_ptr == NULL)
	{
		LOG_printf ("ConfigFile::read: Error: couldn't open file %s.\n", _filename_0);
		return (ConfigFile_INTERNAL_ERROR);
	}

	/* Lecture du texte */
	text_0 = read_text (file_ptr);
	if (text_0 == NULL)
	{
		fclose (file_ptr);
		return (ConfigFile_INTERNAL_ERROR);
	}

	/* Ejection des commentaires */
	error_code = remove_comments (text_0);
	if (error_code != ConfigFile_OK)
	{
		FREE (text_0);
		fclose (file_ptr);
		return (error_code);
	}

	/* Isolation des accolades */
	error_code = isolate_brackets (&text_0);
	if (error_code != ConfigFile_OK)
	{
		FREE (text_0);
		fclose (file_ptr);
		return (error_code);
	}

	/* Separation des lexemes */
	nbr_lexemes = BASE_parse_command_line (text_0, lex_0_array,
	                                       ConfigFile_MAX_NBR_LEXEMES,
	                                       delimiter_list_0);

	/* Recuperation des cles */
	index = 0;
	error_code = get_key_list (key_list, lex_0_array, index);
	if (error_code != ConfigFile_OK)
	{
		FREE (text_0);
		fclose (file_ptr);
		return (error_code);
	}

	/* Fermeture */
	FREE (text_0);
	fclose (file_ptr);

	return (ConfigFile_OK);
}



signed int	ConfigFile::write (const std::list <ConfigKey> &key_list)
{

	/*** A faire ***/

	return (0);
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



char	*ConfigFile::read_text (FILE *file_ptr)
{
	int		c;
	long		file_len;
	long		buffer_size;
	char		*text_0;
	char		*temp_0;

	file_len = 0;
	buffer_size = 1024;
	text_0 = (char *) MALLOC (buffer_size);
	if (text_0 == NULL)
	{
		LOG_printf ("ConfigFile::read_text: Error: couldn't allocate memory for file loading.\n");
		return (NULL);
	}

	while ((c = fgetc (file_ptr)) != EOF)
	{
		text_0 [file_len] = (char) c;
		file_len ++;
		if (file_len >= buffer_size - 1)
		{
			buffer_size += 1024;
			temp_0 = (char *) REALLOC (text_0, buffer_size);
			if (temp_0 == NULL)
			{
				LOG_printf ("ConfigFile::read_text: Error: couldn't reallocate memory for file loading.\n");
				FREE (text_0);
				return (NULL);
			}
			text_0 = temp_0;
		}
	}
	text_0 [file_len] = '\0';

	return (text_0);
}



signed long	ConfigFile::remove_comments (char *text_0)
{
	long		line_cnt;
	bool		quote_flag;
	char		*eol_0;

	line_cnt = 1;
	while (*text_0 != '\0')
	{
		/* Pointe sur le caractere de fin de ligne (ou de fichier) */
		eol_0 = strchr (text_0, '\n');
		if (eol_0 == NULL)
		{
			eol_0 = text_0 + strlen (text_0);
		}

		/* Recherche le // le plus a gauche dans la ligne */
		quote_flag = false;
		while (text_0 < eol_0)
		{
			if (*text_0 == '"')
			{
				quote_flag = ! quote_flag;
			}
			else if (! quote_flag)
			{
				if (   text_0 [0] == '/'
				    && text_0 [1] == '/')
				{
					memset (text_0, ' ', eol_0 - text_0);
					text_0 = eol_0;
					break;
				}
			}
			text_0 ++;
		}

		/* Regarde si on termine une ligne sans fermer les guillemets */
		if (quote_flag)
		{
			return (ConfigFile_NON_CLOSED_QUOTES | line_cnt);
		}

		/* Le caractere de fin de ligne est en fait une
			fin de fichier: on s'en va */
		if (*text_0 == '\0')
		{
			break;
		}

		/* Saute au debut de la ligne suivante */
		text_0 ++;
	}

	return (ConfigFile_OK);
}



signed long	ConfigFile::isolate_brackets (char **text_0_ptr)
{
	bool		old_delim_flag;
	bool		delim_flag;
	bool		quote_flag;
	long		len;
	long		bracket_cnt;
	char		*cur_0;
	char		*new_0;

	new_0 = (char *) MALLOC (strlen (*text_0_ptr) * 2 + 10);
	if (new_0 == NULL)
	{
		LOG_printf ("ConfigFile::isolate_brackets: Error: couldn't allocate memory for file processing.\n");
		return (ConfigFile_INTERNAL_ERROR);
	}

	cur_0 = *text_0_ptr;
	len = 0;
	old_delim_flag = true;
	delim_flag = true;
	bracket_cnt = 0;
	quote_flag = false;
	while (*cur_0 != '\0')
	{
		new_0 [len] = *cur_0;
		delim_flag = BASE_is_delimiter (cur_0 [1], delimiter_list_0);

		if (*cur_0 == '"')
		{
			quote_flag = ! quote_flag;
		}
		
		else if (! quote_flag)
		{
			if (   *cur_0 == '{'
				 || *cur_0 == '}'
				 || *cur_0 == '=')
			{
				if (! old_delim_flag)
				{
					new_0 [len] = ' ';
					len ++;
				}

				new_0 [len] = *cur_0;
				len ++;
				new_0 [len] = ' ';
				delim_flag = true;

				if (*cur_0 == '{')
				{
					bracket_cnt ++;
				}
				else if (*cur_0 == '}')
				{
					bracket_cnt --;
				}
			}
		}

		len ++;
		cur_0 ++;
		old_delim_flag = delim_flag;
	}

	new_0 [len] = '\0';
	FREE (*text_0_ptr);
	*text_0_ptr = new_0;

	if (bracket_cnt > 0)
	{
		return (ConfigFile_NON_CLOSED_BRACKETS);
	}
	else if (bracket_cnt < 0)
	{
		return (ConfigFile_NON_OPENED_BRACKETS);
	}

	return (ConfigFile_OK);
}



signed long	ConfigFile::get_key_list (std::list <ConfigKey> &key_list, char *lex_0_array [], long &index)
{
	signed long	error_code;
	ConfigKey	key;
	std::list <ConfigKey>	sub_key_list;

	key_list.clear ();

	while (lex_0_array [index] != NULL)
	{
		/* Fin de la liste ? */
		if (lex_0_array [index] [0] == '}')
		{
			break;
		}

		key.set_name ("");

		/* Verifie la presence du '=' */
		if (lex_0_array [index + 1] != NULL)
		{
			if (lex_0_array [index + 1] [0] == '=')
			{
				/* Recupere le nom de la cle et saute le '=' */
				key.set_name (lex_0_array [index]);
				index += 2;

				/* Verifie qu'on ait bien quelque chose apres le '=' */
				if (lex_0_array [index] == NULL)
				{
					key_list.clear ();
					return (ConfigFile_MISSING_PARAMETER);
				}
			}
		}

		/* La valeur est une liste */
		if (lex_0_array [index] [0] == '{')
		{
			index ++;
			error_code = get_key_list (sub_key_list, lex_0_array, index);
			if (error_code != ConfigFile_OK)
			{
				key_list.clear ();
				return (error_code);
			}
			key.set_value (ConfigKeyValueList (sub_key_list));
		}

		/* La valeur est une chaine simple */
		else
		{
			key.set_value (ConfigKeyValueString (lex_0_array [index]));
		}

		/* Ajoute la cle a la liste */
		key_list.push_back (key);

		/* Cle suivante */
		index ++;
	}

	return (ConfigFile_OK);
}



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
