#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>

#include "file_struct.h"
#include "file_class.h"
#include <vcl.h>

//powinna zwracac wskaznik na znalezione sekcje
sec_list DataFile::SecExists(AnsiString name) const
  {
  sec_list tmp=parameters;
  while ((tmp != 0) && (AnsiCompareStr(name,tmp->name)))
    tmp=tmp->next;
  return tmp;
  }

reg_list DataFile::RegExists(reg_list section, AnsiString name) const
  {
  while ((section != 0) && (AnsiCompareStr(name,section->name)))
    section=section->next;
  return section;
  }

DataFile::DataFile()
  {
  parameters=0;
  nobjects=0;
  obj_classes=0;
  data=new Matrix(0,0,0,2);
  this->filename="";
  }

DataFile::DataFile(AnsiString filename)
  {
  parameters=0;
  nobjects=0;
  obj_classes=0;
  data=new Matrix(0,0,0,2);
  this->filename="";
  LoadFile(filename);
  }

DataFile::DataFile(const DataFile & input_file, const Matrix & output_matrix)
  {
  parameters=0;
  if (input_file.nobjects == output_matrix.nrows())
    {
    this->nobjects=input_file.nobjects;
    sec_list in_sec=input_file.parameters;
    sec_list out_sec;
    AddRegistry("COMMENTS","DERIVED FROM FILE "+input_file.filename,"");
    while (in_sec != 0)
      {
      out_sec=AddSection(in_sec->name);
      reg_list reg=in_sec->list;
      while (reg != 0)
        {
        AddRegistry(out_sec,reg->name,reg->value);
        reg=reg->next;
        }
      in_sec=in_sec->next;
      }

    obj_classes=new AnsiString[nobjects];
    for (unsigned int i=0; i < nobjects; i++)
      obj_classes[i]=input_file.obj_classes[i];

    data=new Matrix(output_matrix);

    AddRegistry("GENERAL","NRFEATURES",IntToStr(data->ncols()));
    AddRegistry("GENERAL","CLASS POSITION",IntToStr(data->ncols()+1));
    }
    else
      {
      this->nobjects=0;
      obj_classes=0;
      data=new Matrix(0,0,0,2);
      this->filename="";
      }
  }

DataFile::~DataFile()
  {
  DelSections();
  delete data;
  delete [] obj_classes;
  }

void DataFile::Clear()
  {
  DelSections();
  delete data;
  
  parameters=0;
  nobjects=0;
  obj_classes=0;
  data=new Matrix(0,0,0,2);
  this->filename="";
  }

Matrix & DataFile::rData()
  {
  return *data;
  }

Matrix * DataFile::pData()
  {
  return data;
  }

AnsiString * DataFile::rClasses()
  {
  return obj_classes;
  }

sec_list DataFile::AddSection(AnsiString sec_name)
  {
  sec_list psec=SecExists(sec_name);
  if (psec == 0)
    {
    psec=new file_sec;
    psec->name=sec_name;
    psec->list=0;
    psec->next=0;
    sec_list *tmp=&parameters;
    while (*tmp != 0)
      tmp=&(*tmp)->next;
    *tmp=psec;
    }
  return psec;
  }

void DataFile::Combine(const DataFile & input_file, const Matrix & output_matrix)
  {
  parameters=0;
  if (input_file.nobjects == output_matrix.nrows())
    {
    this->nobjects=input_file.nobjects;
    sec_list in_sec=input_file.parameters;
    sec_list out_sec;
    

    while (in_sec != 0)
      {
      out_sec=AddSection(in_sec->name);
      reg_list reg=in_sec->list;
      while (reg != 0)
        {
        AddRegistry(out_sec,reg->name,reg->value);
        reg=reg->next;
        }
      in_sec=in_sec->next;
      }

    obj_classes=new AnsiString[nobjects];
    for (unsigned int i=0; i < nobjects; i++)
      obj_classes[i]=input_file.obj_classes[i];

    *data=output_matrix;

    AddRegistry("GENERAL","NRFEATURES",IntToStr(data->ncols()));
    AddRegistry("GENERAL","CLASS POSITION",IntToStr(data->ncols()+1));
    AddRegistry("COMMENTS",DateTimeToStr(Now())+": DERIVED FROM FILE "+input_file.filename,"");

    /*
    DelRegister(SecExists("FEATURES"));
    for (unsigned int i=1; i <= data->ncols(); i++)
      AddRegistry("FEATURES","NAME["+IntToStr(i)+"]","feature nr "+IntToStr(i));
    for (unsigned int i=1; i <= data->ncols(); i++)
      AddRegistry("FEATURES","ORDERED",RegValue("FEATURES","ORDERED")+" "+IntToStr(i));
    */
    }
    else
      {
      DelSections();
      delete data;
      delete [] obj_classes;
      this->nobjects=0;
      obj_classes=0;
      data=new Matrix(0,0,0,2);
      this->filename="";
      }
  }

void DataFile::ReplaceFeaturesNames()
  {
  if (parameters!=0)
    {
    DelRegister(SecExists("FEATURES"));
    for (unsigned int i=1; i <= data->ncols(); i++)
      AddRegistry("FEATURES","NAME["+IntToStr(i)+"]","feature nr "+IntToStr(i));
    for (unsigned int i=1; i <= data->ncols(); i++)
      AddRegistry("FEATURES","ORDERED",RegValue("FEATURES","ORDERED")+" "+IntToStr(i));
    }
  }

void DataFile::AddComment(AnsiString comment)
  {
  AddRegistry("COMMENTS",DateTimeToStr(Now())+": "+comment,"");
  }

reg_list DataFile::AddRegistry(sec_list section, AnsiString reg_name, AnsiString reg_value)
  {
  reg_list preg=RegExists(section->list, reg_name);
  if (preg == 0)
    {
    preg=new file_reg;
    preg->name=reg_name;
    preg->value=reg_value;
    preg->next=0;
    reg_list *tmp=&(section->list);
    while (*tmp != 0)
      tmp=&(*tmp)->next;
    *tmp=preg;
    }
    else
      preg->value=reg_value;
  return preg;
  }

reg_list DataFile::AddRegistry(AnsiString section, AnsiString reg_name, AnsiString reg_value)
  {
  return AddRegistry(AddSection(section),reg_name,reg_value);
  }

void DataFile::PrintSections(TMemo *memo)
  {
  sec_list tmp=parameters;
  while (tmp!=0)
    {
    memo->Lines->Add("["+tmp->name+"]");
    PrintRegister(tmp,memo);
    tmp=tmp->next;
    }
  }

void DataFile::PrintRegister(sec_list section, TMemo *memo)
  {
  if (section != 0)
    {
    reg_list reg=section->list;
    while (reg != 0)
      {
      if (AnsiCompareStr(section->name,"COMMENTS") == 0)
        memo->Lines->Add("; "+reg->name);
        else
          memo->Lines->Add("  "+reg->name+" = "+reg->value);
      reg=reg->next;
      }
    }
  }

void DataFile::PrintSection(AnsiString section_name, TMemo *memo)
  {
  sec_list section=SecExists(section_name);
  if (section != 0)
    {
    PrintRegister(section,memo);
    }
  }

unsigned int DataFile::PrintFeatures(TStringGrid *tsg)
  {
  sec_list features=SecExists("FEATURES");
  tsg->RowCount=1;
  unsigned int row=0;
  if (features !=0)
    {
    tsg->Cells[0][0]="Feature";
    tsg->Cells[1][0]="Description";
    reg_list reg=features->list;
    
    while (reg != 0)
      {
      if (reg->name.SubString(1,5) == "NAME[")
        {
        row++;
        tsg->RowCount+=1;
        tsg->Cells[0][row]="x"+reg->name.SubString(6,reg->name.Length()-6);
        tsg->Cells[1][row]=reg->value;
        }
      reg=reg->next;
      }
    }

  if (tsg->RowCount > 1)
    {
    tsg->FixedRows=1;
    }
    else
      {
      tsg->RowCount=18;
      for (int i=0; i < tsg->RowCount; i++)
        {
        tsg->Cells[0][i]="";
        tsg->Cells[1][i]="";
        }
      }
      
  if (tsg->RowCount < 18)
    tsg->ColWidths[1]=296;
    else
      tsg->ColWidths[1]=280;

  return row;
  }

void DataFile::DelRegister(sec_list section)
  {
  if (section != 0)
    {
    reg_list tmp;
    reg_list del=section->list;
    section->list=0;
    while (del != 0)
      {
      tmp=del->next;
      delete del;
      del=tmp;
      }
    }
  }

void DataFile::DelSections()
  {
  sec_list tmp;
  while (parameters != 0)
    {
    DelRegister(parameters);
    tmp=parameters->next;
    delete parameters;
    parameters=tmp;
    }
  }

AnsiString DataFile::RegValue(AnsiString section, AnsiString registry)
  {
  AnsiString s="";
  sec_list sec=SecExists(section);
  if (sec != 0)
    {
    reg_list reg=RegExists(sec->list,registry);
    if (reg != 0)
      s=reg->value;
    }
  return s;
  }

AnsiString DataFile::ObjectClassName(unsigned int object_number)
  {
  //AnsiString sec="CLASSES";
  //AnsiString reg="NAME["+IntToStr(object_number)+"]";
  //return RegValue(sec,reg);
  if (object_number < nobjects)
    return obj_classes[object_number];
    else
      return "";
  }

void DataFile::LoadFile(AnsiString filename)
  {
  ifstream fin;
  fin.open(filename.c_str(),ios::binary);

  if (fin.is_open())
    {
    this->filename=filename;

    AnsiString s="",n="";
    char c;

    DelSections();

    //--- wczytywanie ustawien do sekcji DATA
    sec_list current_sec=parameters;
    while ((fin.eof() == false) && (s != "DATA"))
      {
      c=fin.peek();
      switch (c)
        {
        case '[':
          s=ReadSecName(fin);
          current_sec=AddSection(s);
          ReadToEOL(fin);
          break;
        case '\;':
          AddRegistry("COMMENTS",ReadComment(fin),"");
          ReadToEOL(fin);
          break;
        default:
          n=ReadRegName(fin);
          if ((n != "") && (current_sec != 0))
            AddRegistry(current_sec,n,ReadRegValue(fin));
          ReadToEOL(fin);
          break;
        }
      }

    //--- ustawianie odpowiednich zmiennych
    unsigned int nrfeatures=RegValue("GENERAL","NRFEATURES").ToInt()+1;
    AnsiString separator=RegValue("GENERAL","SEPARATOR");
    separator=separator.SubString(2,separator.Length()-2);
    int class_position=RegValue("GENERAL","CLASS POSITION").ToInt()-1;
    AnsiString missing_value=RegValue("GENERAL","MISSING VALUE");

    if (class_position < 0)
      nrfeatures--;

    //--- ustawienie znacznika pliku i zliczenie wpisow
    ifstream::pos_type position = fin.tellg();
    unsigned int counter=0;

    bool err=false;
    while ((fin.peek() != EOF) && (err == false))
      {
      s="";
      for (int i=0,column=0; i < nrfeatures; i++)
        {
        if (i == class_position)
          ReadClass(fin,separator);
          else
            {
            s=ReadValueStr(fin,separator,missing_value);
            if (s == "")
              err=true;
            column++;
            }
        }
      ReadToEOL(fin);
      
      if (err == false)
        counter++;
      }
    nobjects=counter;

    //--- powrot do znacznika i wczytanie danych do macierzy
    //--- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //--- ALE PO CO TEN CLEAR!!!!!!!!
    //--- po dojsciu do konca pliku jest ustawiany eofbit i trzeba go skasowac ;-)
    //--- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    fin.clear();
    fin.seekg(position);
    data->Resize(nobjects,nrfeatures-1);
    obj_classes=new AnsiString[nobjects];
    counter=0;
    for (counter=0; counter < nobjects; counter++)
      {
      for (int i=0,column=0; i < nrfeatures; i++)
        {
        if (i == class_position)
          obj_classes[counter]=ReadClass(fin,separator);
          else
            {
            (*data)[counter][column]=ReadValue(fin,separator,missing_value);
            column++;
            }
        }
      ReadToEOL(fin);
      }

    fin.close();
    }
  }

void DataFile::SaveFile(AnsiString filename)
  {
  ofstream fout;
  fout.open(filename.c_str());
  if (fout.is_open())
    {
    sec_list sec=parameters;

    sec_list comm=SecExists("COMMENTS");
    if (comm != 0)
      {
      reg_list comms=comm->list;
      while (comms != 0)
        {
        fout << "; " << (comms->name).c_str() << endl;
        comms=comms->next;
        }
      }
    while (sec!=0)
      {
      if (AnsiCompareStr(sec->name,"COMMENTS") != 0)
        {
        fout << endl << "[" << (sec->name).c_str() << "]" << endl;
        reg_list reg=sec->list;
        while (reg != 0)
          {
          fout << (reg->name).c_str() << "=" << (reg->value).c_str() << endl;
          reg=reg->next;
          }
        }
      sec=sec->next;
      }

    unsigned int nrows=data->nrows();
    unsigned int ncols=data->ncols();
    char separator=RegValue("GENERAL","SEPARATOR")[2];

    for (unsigned int i=0; i < nrows; i++)
      {
      for (unsigned int j=0; j < ncols; j++)
       {
       fout << setw(22) << right << FloatToStrF((*data)[i][j],ffExponent,15,2).c_str() << separator;
       }
      fout << obj_classes[i].c_str() << endl;
      }

    fout.close();

    this->filename=filename;
    }
  
  }

AnsiString DataFile::ReadSecName(ifstream &fin)
  {
  char c;
  AnsiString s="";
  if (fin.peek() == '[')
    {
    fin.get(c);
    while (((c=fin.peek()) != ']') && (c != 10) && (c != 13) && (fin.eof() == false))
      {
      fin.get(c);
      s+=c;
      }
    }
  return s.Trim();
  }
AnsiString DataFile::ReadRegName(ifstream &fin)
  {
  char c;
  AnsiString s="";
  while (((c=fin.peek()) != '=') && (c != 10) && (c != 13) && (fin.eof() == false))
    {
    fin.get(c);
    s+=c;
    }
  return s.Trim();
  }
AnsiString DataFile::ReadRegValue(ifstream &fin)
  {
  char c;
  AnsiString s="";
  if (fin.peek() == '=')
    {
    fin.get(c);
    while (((c=fin.peek()) != 10) && (c != 13) && (fin.eof() == false))
      {
      fin.get(c);
      s+=c;
      }
    }
  return s.Trim();
  }
AnsiString DataFile::ReadComment(ifstream &fin)
  {
  char c;
  AnsiString s="";
  if (fin.peek() == '\;')
    {
    fin.get(c);
    while (((c=fin.peek()) != 10) && (c != 13) && (fin.eof() == false))
      {
      fin.get(c);
      s+=c;
      }
    }
  return s.Trim();
  }
AnsiString DataFile::ReadToEOL(ifstream &fin)
  {
  char c;
  AnsiString s="";
  do
    {
    fin.get(c);
    s+=c;
    }
    while ((c != 10) && (fin.eof() == false));
  return s;
  }

AnsiString DataFile::ReadClass(ifstream &fin, AnsiString separator)
  {
  char c;
  AnsiString s="";
  while (fin.peek() == ' ')
    fin.get(c);
  while (((c=fin.peek()) != separator[1]) && (c != 10) && (c != 13) && (fin.eof() == false))
    {
    fin.get(c);
    s+=c;
    }
  if (fin.peek() == separator[1])
    fin.get(c);
  return s.Trim();
  }

double DataFile::ReadValue(ifstream &fin, AnsiString separator, AnsiString missing_value)
  {
  char c;
  AnsiString s="";

  //--- pominiecie poczatkowych spacji
  while (fin.peek() == ' ')
    fin.get(c);

  //--- odczytywanie do znalezienia separatora lub EOL lub EOF
  while (((c=fin.peek()) != separator[1]) && (c != 10) && (c != 13) && (fin.eof() == false))
    {
    fin.get(c);
    s+=c;
    }

  //--- przesuniecie znacznika za separator
  if (fin.peek() == separator[1])
    fin.get(c);

  //-- obciecie spacji na poczatku i koncu, jesli to konieczne
  s=s.Trim();

  //--- zastapienie brakujacej wartosci przez "0"
  if ((s == missing_value) || (s.Length() == 0))
    s="0";

  return s.ToDouble();
  }

AnsiString DataFile::ReadValueStr(ifstream &fin, AnsiString separator, AnsiString missing_value)
  {
  char c;
  AnsiString s="";

  //--- pominiecie poczatkowych spacji
  while (fin.peek() == ' ')
    fin.get(c);

  //--- odczytywanie do znalezienia separatora lub EOL lub EOF
  while (((c=fin.peek()) != separator[1]) && (c != 10) && (c != 13) && (fin.eof() == false))
    {
    fin.get(c);
    s+=c;
    }

  //--- przesuniecie znacznika za separator
  if (fin.peek() == separator[1])
    fin.get(c);

  //-- obciecie spacji na poczatku i koncu, jesli to konieczne
  s=s.Trim();

  //--- zastapienie brakujacej wartosci przez "0"
  if (s == missing_value)
    s="0";

  return s;
  }
