/*===========================================================================
 *
 * File:	GenCFG.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Friday, December 08, 2000
 *
 * Implements the CConfigFile class, a general config loading/parsing/saving 
 * class for handling text configuration files easily.
 *
 *=========================================================================*/

	/* Include Files */
#include "gencfg.h"


#undef  __FUNC__
#define __FUNC__ "CConfigRecord::CConfigRecord()"
/*===========================================================================
 *
 * Class CConfigRecord Constructor
 *
 *=========================================================================*/
CConfigRecord::CConfigRecord (void) {
  pName = NULL;
  NumValues = 0;
 }
/*===========================================================================
 *		End of Class CConfigRecord Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigRecord::Destroy()"
/*===========================================================================
 *
 * Class CConfigRecord Destructor
 *
 *=========================================================================*/
void CConfigRecord::Destroy (void) {
  int LoopCounter;

	/* Delete all the parameter values */
  for (LoopCounter = 0; LoopCounter < NumValues; LoopCounter++) {
    DestroyPointer(pValues[LoopCounter]);
   }

	/* Unallocate the parameter name */
  DestroyPointerArray(pName);
  NumValues = 0;
 }
/*===========================================================================
 *		End of Class CConfigRecord Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigRecord::AddValue()"
/*===========================================================================
 *
 * Class CConfigRecord Method - boolean AddValue (pString);
 *
 * Attempts to add a value to the parameter record.  Returns
 * FALSE on any error.
 *
 *=========================================================================*/
boolean CConfigRecord::AddValue (const char* pString) {

	/* Make sure the values are valid */
  ASSERT(pString != NULL);
  
	/* Is there enough room in array to fit new record? */
  CHECK_MAXINDEX(NumValues, MAX_CFG_VALUES, FALSE);
  
	/* Allocate the new item */
  CreatePointer(pValues[NumValues], CVariant);

	/* Set the contents of the new value */
  pValues[NumValues]->SetPChar(pString);
  
	/* Return success */
  NumValues++;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CConfigRecord::AddValue()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigRecord::MatchName()"
/*===========================================================================
 *
 * Class CConfigRecord Method - boolean MatchName (pString, CaseSensitive);
 *
 * Compares the given string and the variable name returning TRUE if
 * they are identical.  Use the case sensitivity flag to specify whether
 * character case matters.
 *
 *=========================================================================*/
boolean CConfigRecord::MatchName (const char* pString, const boolean CaseSensitive) {
  return MatchString(pName, pString, CaseSensitive);
 }
/*===========================================================================
 *		End of Class Method CConfigRecord::MatchName()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigRecord::SetValue()"
/*===========================================================================
 *
 * Class CConfigRecord Method - boolean SetValue (Index, pString);
 *
 * Sets the parameter value if present to the appropiate value.  Returns
 * FALSE on any error.
 *
 *=========================================================================*/
boolean CConfigRecord::SetValue (const int Index, const char* pString) {

	/* Is the index valid? */
  CHECK_VALIDINDEX(Index, NumValues, FALSE);
  
	/* Set the value */
  pValues[Index]->SetPChar(pString);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CConfigRecord::SetValue()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::CConfigFile()"
/*===========================================================================
 *
 * Class CConfigFile Constructor
 *
 *=========================================================================*/
CConfigFile::CConfigFile () {
  NumVariables = 0;
  LineCount = 0;
  VariableSepChar = '=';
  ValueSepChar = ',';
  CommentSepChar = '#';
  pFilename = NULL;
 }
/*===========================================================================
 *		End of CConfigFile Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::Destroy()"
/*===========================================================================
 *
 * Class CConfigFile Destructor
 *
 *=========================================================================*/
void CConfigFile::Destroy (void) {
  LineCount = 0;

	/* Delete any allocated variable records */
  DELETE_POINTERARRAY(pVariables, NumVariables);
 }
/*===========================================================================
 *		End of Class CConfigFile Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::AddVariable()"
/*===========================================================================
 *
 * Class CConfigFile Method - int AddVariable (pName);
 *
 * Attempts to add the reference to a variable.  Returns the index of the
 * variable record or NULL_VAR_INDEX on error.
 *
 *=========================================================================*/
int CConfigFile::AddVariable (const char* pName) {

	/* Is the input valid? */
  ASSERT(pName != NULL);

	/* Ignore an empty variable name */
  if (*pName == NULL_CHAR) return (-1);
  
	/* Is there enough room in array to add record? */
  CHECK_MAXINDEX(NumVariables, MAX_CFG_VARS, NULL_VAR_INDEX);
  
	/* Create the array record */
  CreatePointer(pVariables[NumVariables], CConfigRecord);
  
	/* Set the new variable contents */
  pVariables[NumVariables]->SetName(pName);
  NumVariables++;

	/* Return the new variable index */
  return (NumVariables - 1);
 }
/*===========================================================================
 *		End of Class Method CConfigFile::AddVariable()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::FindVariable()"
/*===========================================================================
 *
 * Class CConfigFile Method - int FindVariable (pString);
 *
 * Attempts to find the specified variable name.  Returns NULL_VAR_INDEX
 * on error or the found variable index on success
 *
 *=========================================================================*/
int CConfigFile::FindVariable (const char* pString) {
  int LoopCounter;	/* Loop counter */

	/* Ensure valid input */
  CHECK_NULLPOINTER(pString, NULL_VAR_INDEX);

	/* Look for a match in the current records */
  for (LoopCounter = 0; LoopCounter < NumVariables; LoopCounter++) {
    if (pVariables[LoopCounter]->MatchName(pString)) return (LoopCounter);
   }

	/* Nothing found? */
  return (NULL_VAR_INDEX);
 }
/*===========================================================================
 *		End of Class Method CConfigFile::FindVariable()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::FindVariable()"
/*===========================================================================
 *
 * Class CConfigFile Method - boolean Load (pFilename);
 *
 * Attempts to load and parse the specified config filename. Returns 
 * FALSE on any error.  If the given filename is NULL, the current member
 * filename is used.
 *
 *=========================================================================*/
boolean CConfigFile::Load (const char* pCFGFilename) {
  char  LineBuffer[CFG_LINE_SIZE+1];	/* Input buffer string */
  FILE* pFileHandle;
  int   Result;

	/* Attempt to open file with a valid filename */
  if (pCFGFilename == NULL) pCFGFilename = pFilename;
  pFileHandle = openfile(pFilename, "rt");
  if (!pFileHandle) return (FALSE);

	/* Delete any current contents */
  Destroy();

	/* Change the filename if required */
  if (pCFGFilename != pFilename) SetFilename(pCFGFilename);

	/* Read in entire file line-by-line */
  while (!feof(pFileHandle)) {

		/* Read the next line from the file */
    Result = read_eol(pFileHandle, LineBuffer, CFG_LINE_SIZE);
    if (Result == READ_MSL) read_eol(pFileHandle);
    LineCount++;

    Result = ParseLine(LineBuffer);
   }

	/* Close file and return success */
  fclose (pFileHandle);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CONFIG_Class::load()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::ParseLine()"
/*===========================================================================
 *
 * Class CConfigFile Method - boolean ParseLine (char* pString)
 *
 * Parses a line from the config file into the appropiate variable and
 * its values.
 *
 *=========================================================================*/
boolean CConfigFile::ParseLine (char* pString) {
  char*   pParse;
  boolean Result;

	/* Remove any trailing/leading whitespace */
  pParse = trim(pString);
	
	/* Parse out the variable name from the string */
  Result = ParseVariable(&pString);
  if (!Result) return (FALSE);

	/* Parse all the values from the string */
  while (*pString != NULL_CHAR) {
    Result = ParseValue(&pString);
    if (!Result) return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CConfigFile::ParseLine()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::ParseVariable()"
/*===========================================================================
 *
 * Class CConfigFile Method - boolean ParseVariable (char** ppString)
 *
 * Parses the variable from the given line buffer.  Returns FALSE on
 * any error.  Protected class method.
 *
 *=========================================================================*/
boolean CConfigFile::ParseVariable (char** ppString) {
  char*   pParse = *ppString;
  boolean Result;

	/* Parse until the variable or the end of string is reached */
  while (*pParse != NULL_CHAR) {

		/* Check for invalid characters */
    if (*pParse == '\'' || *pParse == '"' || *pParse == ValueSepChar) {
      SET_EXT_ERROR4(ERR_CUSTOM, "\t%5ld: Found invalid character '%c' in variable name!", LineCount, *pParse);
      return (FALSE);
     }
		/* Check for the end of the variable name */
    else if (*pParse == VariableSepChar || *pParse == CommentSepChar) {
      if (*pParse == CommentSepChar) *(pParse + 1) = NULL_CHAR;

		/* Create the variable name */
      *pParse = NULL_CHAR;
      *ppString = rtrim(*ppString);

		/* Attempt to add variable to class */
      Result = AddVariable(*ppString);
      if (Result < 0) return (FALSE);

      *ppString = ltrim(pParse + 1);
      return (TRUE);
     }

    pParse++;
   }

	/* Variable name reached EOF */
  *ppString = rtrim(*ppString);
  Result = AddVariable(*ppString);
  *ppString = pParse;
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CConfigFile::ParseVariable()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CConfigFile::ParseValue()"
/*===========================================================================
 *
 * Class CConfigFile Method - boolean ParseValue (char** ppString)
 *
 * Parses out the next value from the string and adds it to the last
 * variable in the array.  Returns FALSE on any error.  Protected class
 * method.
 *
 *=========================================================================*/
boolean CConfigFile::ParseValue (char** ppString) {
  char*   pValue = *ppString;
  char*   pParse = *ppString;
  char	  LastQuote = NULL_CHAR;
  boolean Result;
  boolean NoTrim = FALSE;

	/* Parse until the value or the end of string is reached */
  while (*pParse != NULL_CHAR) {

	/* Check for quotation characters */
    if ((*pParse == '\'' || *pParse == '"')) {

      if (LastQuote == *pParse) {
        LastQuote = NULL;
        *pParse = NULL_CHAR;
       }
      else if (LastQuote == NULL_CHAR) {
        LastQuote = *pParse;
        pValue = pParse + 1;
        NoTrim = TRUE;
       }
     }
		/* Do nothing if inside a quote */
    else if (LastQuote != NULL_CHAR) {
     }
       		/* Check for the end of the value */
    else if (*pParse == CommentSepChar || *pParse == ValueSepChar) {
      if (*pParse == CommentSepChar) *(pParse + 1) = NULL_CHAR;
    
		/* Create the value string */
      *pParse = NULL_CHAR;
      if (!NoTrim) pValue = trim(pValue);

		/* Attempt to add the value to the current variable */
      Result = pVariables[NumVariables - 1]->AddValue(pValue);
      *ppString = ltrim(pParse + 1);
      return (Result);
     }

    pParse++;
   }

	/* Value reached EOF */
  if (!NoTrim) pValue = trim(pValue);
  Result = pVariables[NumVariables - 1]->AddValue(pValue);
  *ppString = pParse;
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CConfigFile::ParseValue()
 *=========================================================================*/