#include <direct.h>
#include <stdlib.h>
#include <xvt.h>

#include <applicat.h>
#include <config.h>
#include <prefix.h>
#include <utility.h>
#include <scanner.h>

extern "C"
{
  int rename(const char*, const char*);
};

bool TConfig::_read_paragraph()
  // ritorna TRUE se il paragrafo c'era, FALSE altrimenti
{
  bool itwas = FALSE;
  _data.destroy();
  TScanner scan(_file);
  if (scan.paragraph(_paragraph))
  {
    itwas = TRUE;
    // populate array
    TString key, val;
    for (;;)
    {
      const TString& l = scan.line();
      if (l == "" || l[0] == '[') break;             // Fine paragrafo
      if (l[0] == '#' || l[0] == '/') continue;      // Riga di commento

      const int ind = l.find('=');
      if (ind == -1) 
      {
        error_box("Errore configurazione: file %s, vicino alla riga %ud: %s",
                  (const char*)_file, scan.linenum(), (const char*)l);
        continue;
      }
      
      key = l.left(ind);  key.trim();
      val = l.mid(ind+1); val.trim();
      
      if (val[0] == '%')
      {
        if (val == "%yr%") val.format("%04d", TDate(TODAY).year()); else
          if (val == "%frm%") val.format("%05ld", main_app().get_firm()); 
      }  
      // sostituzione abilitata
      _data.add(key,val,TRUE);
    }
  }
  return itwas;
}

void TConfig::_write_paragraph(ofstream& out)
{
  _data.restart();            
  TString cnf(16);
  cnf << '[' << _paragraph << ']';
  out << cnf << '\n';  
  for (int i = 0; i < _data.items(); i++)
  {
    THash_object* o = _data.get_hashobj();
    out << o->key() << "\t= " << (TString&)(o->obj()) << '\n';
  }
  out << '\n';
}

void TConfig::_write_file()
{
  ifstream in(_file);       
  TFilename temp;
  temp.temp("cnf");
  ofstream out(temp);
  
  TFixed_string l(__tmp_string, sizeof(__tmp_string)); 
  TString cnf(16);
  cnf << '[' << _paragraph << ']';
  bool skip = FALSE, done = FALSE;

  while (!in.eof())
  {
    in.getline(__tmp_string,sizeof(__tmp_string)-1);
    l.trim();

    if (cnf == l)
    {
      // write paragraph and all variables
      _write_paragraph(out);
      skip = TRUE; done = TRUE;
    }
    else 
    { 
      if (skip)  skip = l[0] != '[';
      if (!skip) out << l << '\n';
    }
  }
  // new paragraph
  if (!done) _write_paragraph(out);

  out.close(); in.close();
  
  if (_file[0] > 'B' || _file[1] != ':')        // Non creare il .bak su dischetto
  {
    TFilename bak(_file); bak.ext("bak");
    rename(_file, bak);
  }
  fcopy(temp, _file);      // Copia dalla tempdir al nuovo .ini
  remove(temp);            // Cancella file temporaneo
}


void TConfig::_check_paragraph(const char* section)
{
  if (section != NULL && section != _paragraph)
  {
    if (_dirty) _write_file();
    _paragraph = section; 
    _dirty = FALSE;
    _read_paragraph();
  }
}

bool TConfig::exist(const char* var, int index)
{
  TString80 vvar(var); 
  if (index != -1) vvar << '(' << index << ')';
  return _data.is_key(vvar); 
}

TString& TConfig::get(const char* var, const char* section, int index, const char* def)
{
  // ritorna valore di variabile nella sezione corrente o in
  // quella specificata
  static TFixed_string s(&__tmp_string[256], 256);
  TString80 vvar(var); if (index != -1) vvar << '(' << index << ')';
  
  _check_paragraph(section);

  if (_data.is_key(vvar)) 
    s = (TString&)_data[vvar];
  else           
  {
    s = def;
    if (s.not_empty())
      set(var, s, section, TRUE, index);
  }
  return s;
}

long TConfig::get_long(const char* var, const char* section, int index, long def)
{                                       
  const char* n = get(var,section,index);
  if (*n) 
    def = atol(n);
  else
    set(var, format("%ld", def), section, TRUE, index);
  return def; 
}

int TConfig::get_int(const char* var, const char* section, int index, int def)
{
  return (int)get_long(var, section, index, def);
}


bool TConfig::get_bool(const char* var, const char* section, int index, bool def)
{ 
  if (def) strcpy(__tmp_string, "X");
  else *__tmp_string = '\0';

  const TString& s = get(var, section, index, __tmp_string).upper();
  return s != "" && (s == "X" || s == "ON" || s == "YES" || s == "OK" || s == "TRUE");
}


HIDDEN void RGB_COLOR(COLOR c, int& r, int& g, int& b)
{
  r = int(c >> 16) & 0xFF;
  g = int(c >> 8) & 0xFF;
  b = int(c) & 0xFF;
}

COLOR TConfig::get_color(const char* var, const char* section, int index, COLOR def)
{            
  const char* c = get(var, section, index);
  if (*c)
  {           
    TToken_string s(c, ',');
    const int r = s.get_int();
    const int g = s.get_int();
    const int b = s.get_int();
    def = MAKE_COLOR(r, g, b);
  }
  else
  {
    int r, g, b; RGB_COLOR(def, r, g, b);
    set(var, format("%d,%d,%d", r, g, b), section, TRUE, index);
  }  
  return def;
}

bool TConfig::set(const char* var, const char* value, const char* section, 
                  bool force, int index)
{
  // setta variabile nella sezione corrente o specificata
  // se force == TRUE crea la variabile se non esiste; altrimenti
  // da' errore; ritorna TRUE se la variabile c'era, FALSE diversamente

  _check_paragraph(section);
  TString vvar(var); if (index != -1) vvar << '(' << index << ')';

  bool itwas = _data.is_key(vvar);
  
  if (itwas && !force)
    warning_box("Tentativo di ridefinizione simbolo: %s", (const char*)vvar);
  else
  {
    _dirty = TRUE;
    _data.add(vvar, new TString(value), force);
  }
  return itwas;
}

bool TConfig::set(const char* var, long value, const char* section, 
                  bool force, int index)
{
  TString16 t; t << value;
  return set(var,t,section,force,index);
}

word TConfig::items(const char* var, const char* section)
{
  _check_paragraph(section);
  TString vvar(16);
  for (int cnt = 0; /* uncazzo */ ;cnt++)
  {
    vvar = var; vvar << '(' << cnt << ')';
    if (!_data.is_key(var))
      break;
  }
  return cnt;
}

void TConfig::init(const char *fn, const char* pa) 
{
  _file = fn;
  _paragraph = pa;
  _dirty = FALSE; 
  
  if (!fexist(_file))
    fatal_box("Impossibile aprire il file di configurazione %s", fn );
  
  if (_paragraph.empty())
  {
    _paragraph = main_app().name();
    _paragraph.cut(2);
  }
  _ispresent = _read_paragraph();
}


TConfig::TConfig(int which_config, const char* paragraph)
{             
  switch (which_config)
  {
  case CONFIG_DITTA:
    _file << main_app().get_firm_dir() << '/' << "prassid.ini";
    if (!fexist(_file)) fcopy("prassid.ini", _file);   
    break;
  case CONFIG_STUDIO:
    _file = "prassis.ini";
    break;
  case CONFIG_USER:
    _file = main_app().get_firm_dir();
    if (_file.not_empty())
    {
      for (int i = _file.len()-1; i >= 0; i--)
        if (_file[i] == '/' || _file[i] == '\\') break;
      _file.cut(i+1);
    }  
    _file << "config";
    if (!fexist(_file))
    {
#if XVT_OS==XVT_OS_SCOUNIX
      mkdir(_file, 0777);
#else
      mkdir(_file);
#endif       
    }
    _file << '/' << main_app().user() << ".ini";
    break;
  default:
    _file = "prassi.ini";
    break;
  }  

  if (!fexist(_file))
  {
    FILE* c = fopen(_file, "w");
    CHECKS(c, "Impossibile aprire il file di configurazione %s", (const char*)_file);
    fclose(c);
  }  
  
  init(_file, paragraph);
}

TConfig::TConfig(const char *fn, const char* pa) 
{ init(fn, pa); }


TConfig::~TConfig()
{
  // il distruttore riscrive il file con le modifiche se necessario 
  if (_dirty) _write_file();
}