// ----------------------------------------------------------------------------
//
// Copyright (C) Columbia University, 1994. All Rights Reserved.
// Sameer A. Nene, Shree K. Nayar, Hiroshi Murase
//
// See file LICENSE for details of software license agreement.
//
// ----------------------------------------------------------------------------
//
// dataset.C
//
// Author:               Sameer Nene
// Date:                 05/24/94
// Version:              1.0
// Modification History:
//   06/26/94: Changes necessary because of change in interface
//
// Bugs:
//   It is possible to iterate over the last element in a list using
//   DataSetIter. When then is done, the program coredumps.
//
//   There are no out of memory checks.
//
// Classes:
//   DataSet
//   DataSetIter
//
// Notes:
//   This module contains implementation of classes declared in dataset.h
//   
// ----------------------------------------------------------------------------

#include <ctype.h>
#include <memory.h>
#include <string.h>
#include "errorscope.h"
#include "persistent.h"
#include "registry.h"
#include "dataset.h"

#ifndef SEEK_SET
#define SEEK_SET 0
#endif

void* DataSet::d_protocol = (void*)(&DataSet::d_protocol);
char* DataSet::d_name_p = "DataSet";
Registrar DataSet::d_registrar(DataSet::d_name_p,
			       &(DataSet::createFunc));

Persistent* DataSet::createFunc()
{
  return new DataSet;
}

int DataSet::hasProtocol(void* protocol) const
{
  return d_protocol == protocol;
}

DataSet::DataSet() : d_lsizes(0), d_sizes_p(0), d_va_p(new VectAry)
{
}

DataSet::DataSet(int n, int l) : d_va_p(new VectAry(l)), d_lsizes(n),
d_sizes_p(new int[n])
{
  memset(d_sizes_p, 0, n * sizeof(int));
}

DataSet::DataSet(int l) : d_va_p(new VectAry(l)), d_lsizes(0), d_sizes_p(0)
{
}

DataSet::~DataSet()
{
  delete[] d_sizes_p;
  delete d_va_p;
}

DataSet* DataSet::safeCast(Persistent *p)
{
  return p -> hasProtocol(d_protocol) ? (DataSet*)p : 0;
}

VectAry& DataSet::operator()() const
{
  return *d_va_p;
}

int DataSet::getSize() const
{
  return d_lsizes;
}

int DataSet::getSize(int i) const
{
  return d_sizes_p[i];
}

ErrorScope::Error DataSet::put(FILE *file) const
{
  Error error;

  if(fwrite((char *)&d_lsizes, sizeof(int), 1, file) != 1)
    return FILE_WRITE_ERROR;

  if(fwrite((char *)d_sizes_p, sizeof(int), d_lsizes, file) != d_lsizes)
    return FILE_WRITE_ERROR;

  if((error = d_va_p -> put(file)) != OK)
    return error;

  return OK;
}

ErrorScope::Error DataSet::putText(const char *fileName) const
{
  int i, j, k, l;
  FILE *file;

  if((file = fopen(fileName, "w")) == NULL)
    return FILE_OPEN_ERROR;

  if(fprintf(file, "# DataSet\n# Creator: SLAM V1.0 Rev: 07/10/94\n\n") == EOF) {
    fclose(file);
    return FILE_WRITE_ERROR;
  }

  for(i = 0; i < d_lsizes; ++i)
    if(fprintf(file, "%u\n", d_sizes_p[i]) == EOF) {
      fclose(file);
      return FILE_WRITE_ERROR;
    }

  if(fprintf(file, "\n") == EOF) {
    fclose(file);
    return FILE_WRITE_ERROR;
  }

  for(i = 0, j = d_va_p -> getSize(); i < j; ++i) {
    Vector &v = (*d_va_p)[i];
    for(l = 0, k = v.length(); l < k; ++l)
      if(fprintf(file, "%1.16le ", v.element(l)) == EOF) {
	fclose(file);
	return FILE_WRITE_ERROR;
      }
    if(fprintf(file, "\n") == EOF) {
      fclose(file);
      return FILE_WRITE_ERROR;
    }
  }

  return fclose(file) ? FILE_CLOSE_ERROR : OK;
}

const char* DataSet::name() const
{
  return d_name_p;
}

void DataSet::setDimension(int n)
{
  delete[] d_sizes_p;
  d_sizes_p = new int[d_lsizes = n];
  memset(d_sizes_p, 0, n * sizeof(int));
}

void DataSet::setSize(int n, int l)
{
  d_sizes_p[n] = l;
}

ErrorScope::Error DataSet::get(FILE *file)
{
  int i, size, t;
  Error error;

  if(fread((char *)&size, sizeof(int), 1, file) != 1)
    return FILE_READ_ERROR;

  if(d_lsizes != size) {
    delete[] d_sizes_p;
    d_sizes_p = new int[d_lsizes = size];
  }
  
  if(fread((char *)d_sizes_p, sizeof(int), d_lsizes, file) != d_lsizes)
    return FILE_READ_ERROR;

  for(i = 0, t = 1; i < size; t *= d_sizes_p[i++]);

  if(t != d_va_p -> getSize()) {
    delete d_va_p;
    d_va_p = new VectAry(t);
  }

  if((error = d_va_p -> get(file)) != OK)
    return error;

  return OK;
}

ErrorScope::Error DataSet::getText(const char *fileName)
{
  char *p, buf[256];
  int c, l, m, n, blankLines, prevState, state = 0;
  long dataPos;
  double d;
  FILE *file;

  if((file = fopen(fileName, "r")) == 0)
    return FILE_OPEN_ERROR;

  do {
    if((c = fgetc(file)) == EOF) {
      fclose(file);
      return FILE_READ_ERROR;
    }

    switch(state) {
    case 0:
      if(isspace(c))
	break;
      if(c == '#')
	state = 1;
      else if(isdigit(c) || c == '-' || c == '.') {
	ungetc(c, file);
	state = 2;
      }
      else
	state = 3;
      break;
    case 1:
      if(c == '\n')
	state = 0;
    }
  }
  while(state < 2);

  if(state == 3) {
    fclose(file);
    return INCORRECT_FORMAT;
  }

  dataPos = ftell(file);
  m = blankLines = 0;
  prevState = l = 1;
  while(1) {
    if((p = fgets(buf, 256, file)) == 0)
      break;
    for(state = 0; *p != '\0'; ++p)
      if(!isspace(*p))
	if(prevState != 0) {
	  if(sscanf(buf, "%u", &n) != 1) {
	    fclose(file);
	    return INCORRECT_FORMAT;
	  }
	  l *= n;
	  ++m;
	  state = 1;
	  break;
	}
	else {
	  state = 1;
	  break;
	}
    if(state == 1 && prevState == 0)
      break;
    if(state == 0)
      ++blankLines;
    prevState = state;
  }

  if(m == 0) {
    fclose(file);
    return INCORRECT_FORMAT;
  }

  if(fseek(file, dataPos, SEEK_SET) == -1) {
    fclose(file);
    return FILE_READ_ERROR;
  }
  
  if(m != d_lsizes) {
    delete[] d_sizes_p;
    d_sizes_p = new int[d_lsizes = m];
  }

  for(c = 0; c < m; ++c)
    if(fscanf(file, "%u", &d_sizes_p[c]) != 1) {
      fclose(file);
      return FILE_READ_ERROR;
    }

  // Take care of last \n
  if(fgets(buf, 256, file) == 0) {
    fclose(file);
    return FILE_READ_ERROR;
  }
  
  for(n = 0; n < blankLines; ++n)
    if(fgets(buf, 256, file) == 0) {
      fclose(file);
      return FILE_READ_ERROR;
    }

  if(l != d_va_p -> getSize()) {
    delete d_va_p;
    d_va_p = new VectAry(l);
  }

  for(c = 0; c < l; ++c) {
    dataPos = ftell(file);

    n = 0;
    while(1) {
      if((m = getc(file)) == EOF) {
	fclose(file);
	return FILE_READ_ERROR;
      }
      if(m == '\n')
	break;
      ++n;
    }
    
    if(fseek(file, dataPos, SEEK_SET) == -1) {
      fclose(file);
      return FILE_READ_ERROR;
    }

    dataPos = ftell(file);

    p = new char[n + 2];
    if(fgets(p, n + 2, file) == 0) {
      delete[] p;
      fclose(file);
      return FILE_READ_ERROR;
    }
    p[n] = '\0';

    n = 0;
    if(strtok(p, " \t") != NULL)
      for(++n; strtok(NULL, " \t") != NULL; ++n);

    Vector &v = (*d_va_p)[c];

    if(fseek(file, dataPos, SEEK_SET) == -1) {
      delete[] p;
      fclose(file);
      return FILE_READ_ERROR;
    }
    
    for(m = 0, v.length(n); m < n; ++m) {
      if(fscanf(file, "%le", &d) != 1) {
	delete[] p;
	fclose(file);
	return FILE_READ_ERROR;
      }
      v.element(m, d);
    }

    // Take care of last \n
    if(fgets(buf, 256, file) == 0) {
      delete[] p;
      fclose(file);
      return FILE_READ_ERROR;
    }

    delete[] p;
  }
  
  fclose(file);
  return OK;
}

DataSetIter::DataSetIter(const DataSet &ds) : d_it(*(ds.d_va_p))
{
}

Vector& DataSetIter::operator()() const
{
  return d_it();
}

DataSetIter::operator const void*() const
{
  return d_it;
}

void DataSetIter::operator++()
{
  ++d_it;
}

void DataSetIter::reset()
{
  d_it.set(0);
}
