/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            miav_config.cc
 *
 *  Sat Feb 19 14:13:19 CET 2005
 *  Copyright  2005 Bent Bisballe
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This program 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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <config.h>
#include "miav_config.h"

MiavConfig::MiavConfig(char *file)
{
  configs = NULL;

  // Read config file
  FILE* fp = fopen(file, "r");
  
  if(!fp) {
    fprintf(stderr, "Error reading configuration file %s\n", file);
    return;
  }
  fseek(fp, 0, SEEK_END);
  int fsz = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  
  char *raw = (char*)calloc(fsz, 1);
  fread(raw, 1, fsz, fp);

  fclose(fp);

  parse(raw);
  free(raw);
}

MiavConfig::~MiavConfig()
{
  _cfg *die = NULL;
  _cfg *cfg = configs;

  while(cfg) {
    if(die) free(die);
    die = cfg;
    cfg = cfg->next;
  }
  if(die) free(die);
}

/** 
 * Adds one configuration entry, from a single zero terminated line.
 */
_cfg *MiavConfig::addConfig(_cfg *parent, char* conf)
{

  //
  // FIXME: Check for wellformedness
  //

  _cfg *cfg;

  printf("[%s]\n", conf);

  cfg = (_cfg*) malloc(sizeof(_cfg));
  if(!parent) configs = cfg;

  int namelen = strchr(conf, '=') - conf;
  char* name = (char*)calloc(namelen + 1, 1);
  strncpy(name, conf, namelen);
  printf("name [%s]#%d - ", name, namelen);

  int vallen = conf + strlen(conf) - (strchr(conf, '=') + 1);
  char* val = (char*)calloc(vallen + 1, 1);
  strncpy(val, conf + strlen(conf) - vallen, vallen);
  printf("val [%s]#%d\n", val, vallen);

  cfg->name = new string((const char*)name);
  free(name);

  cfg->stringval = new string((const char*)val);
  cfg->intval = atoi(val);
  cfg->floatval = atof(val);
  cfg->boolval = atoi(val) != 0;
  free(val);

  cfg->next = NULL;

  if(parent) parent->next = cfg;
  return cfg;
}

/**
 * Main parse function, iterates the lines of the file.
 */
int MiavConfig::parse(char* raw)
{
  // Strip the string
  char *conf = strip(raw);
  char *conf_end = conf + strlen(conf);
  char *start = conf;
  char *p;

  _cfg *cfg = NULL;

  // Iterate the lines in the string
  for(p = conf; p < conf_end; p++) {
    if(*p == '\n') {
      *p = '\0';
      cfg = addConfig(cfg, start);
      start = p+1;
    }
  }
  // Allocated in strip
  free(conf);
  printf("done!\n");
  return 0;
}

/**
 * Strip all unwanted data from the string, initial to parsing.
 */
char* MiavConfig::strip(char* conf)
{
  // Freed in parse!!!
  char *stripped = (char*)calloc(strlen(conf) + 2, 1);
  char *r;
  char *w = stripped;

  bool instring = false;
  bool incomment = false;

  // Iterate over the characters in the input string.
  for(r = conf; r < conf + strlen(conf); r++) {
    if(strchr("#", *r)) incomment = true;
    if(strchr("\n", *r)) incomment = false;

    if(!incomment) {
      if(instring) {
        // When in a string, accept anything, except ".
        if(*r != '\"') {
          *w = *r;
          w++;
        }
      } else {
        // Only copy valid characters
        if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_,.=", *r)) {
          // Change comma into dot
          if(*r == ',') *r = '.';
          *w = *r;
          w++;
        }
        // We don't want double newlines and initial newline!
        if((*r == '\n') && (*(w-1) != '\n') && (w != stripped)) {
          *w = *r;
          w++;
        }
      }
    }

    if(strchr("\"", *r)) instring = !instring;
  }

  // If we are not ending on a newline, we better append one
  if(*(w-1) != '\n') {
    *w = '\n';
    w++;
  }

  // Make sure we are nullterminated.
  *w = '\0';

  return stripped;
}

int MiavConfig::readInt(char *node)
{
  return findNode(node)->intval;
}

bool MiavConfig::readBool(char *node)
{
  return findNode(node)->boolval;
}

string *MiavConfig::readString(char *node)
{
  return findNode(node)->stringval;
}

float MiavConfig::readFloat(char *node)
{
  return findNode(node)->floatval;
}

_cfg *MiavConfig::findNode(char* node)
{
  _cfg *cfg = configs;

  while(cfg) {
    if(!strcmp(node, cfg->name->c_str())) return cfg;
    cfg = cfg->next;
  }
  fprintf(stderr, "ERROR: Request for nonexisting node \"%s\"!\n", node);
  exit(1);
}