#include "SFScalarField.h"
#include <iostream.h>

// The below block of macros is needed if the code is going to be portable
// across Coin and Inventor.  If you don't care about this portability
// problem then either comment out the conditionals.  
// Required Inventor macros for subclassing SoSField
#if HAVE_COIN
// This means we are using Coin and these macros must be referenced
PRIVATE_TYPEID_SOURCE(SFScalarField);
PRIVATE_EQUALITY_SOURCE(SFScalarField);
#else
// This means we are using Inventor and these macros must be referenced
SO__FIELD_ID_SOURCE(SFScalarField);
SO__FIELD_EQ_SAME_SOURCE(SFScalarField);
#endif

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    Default constructor which sets all variables to zero.
//
//////////////////////////////////////////////////////////////////////////////
SFScalarField::SFScalarField ()
{
  dims[0] = dims[1] = dims[2] = 0;
  data = myData   = NULL;
  numValues       = 0;
  valuesAllocated = 0;
  dataCopied      = false;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    deletes all allocated arrays
//
//////////////////////////////////////////////////////////////////////////////
SFScalarField::~SFScalarField ()
{
  if (myData)
    delete [] myData;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    needed for Inventor to recognize the class.  This must be called at
//    startup time and is best to do it immediately following the
//    initialization of Inventor.
//
//////////////////////////////////////////////////////////////////////////////
void
SFScalarField::initClass ()
{
  SO_SFIELD_INIT_CLASS(SFScalarField, SoSField);
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    set the value of the field.  This will set the dimensions
//    and the data itself.  If the copyData argument is false then only a
//    pointer to the data is saved, otherwise all the data is copied over to
//    this field.  (Note: copying the data can slow performance when dealing
//    with large arrays.  If the data is being passed from an Engine it is
//    best to set copyData to false.)
//
//////////////////////////////////////////////////////////////////////////////
void
SFScalarField::setValue (const int dm[3], float *ptr, bool copyData)
{

  // if we are not to copy the data then just save the pointer
  if (!copyData) {
    dims[0] = dm[0];
    dims[1] = dm[1];
    dims[2] = dm[2];
    
    data = ptr;
    
    if (ptr == myData)
      dataCopied = true;
    else
      dataCopied = false;
  }
  // otherwise allocate memory if needed, and copy the data
  else {
    setSize(dm);

    // copy the data to the local array
    if (ptr != myData) {
      memcpy(myData, ptr, numValues*sizeof(float));
      data = myData;
    }

    dataCopied = true;
  }

  numValues = dims[0]*dims[1]*dims[2];

  // notify database of changes
  valueChanged();
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    return the data and notify Inventor that there is a request for this
//    field.
//
//////////////////////////////////////////////////////////////////////////////
float *
SFScalarField::getValue (int dm[3]) const
{
  // Inventor function
  evaluate();
  //cout << "SFScalarField::getValue()" << endl;

  dm[0] = dims[0];
  dm[1] = dims[1];
  dm[2] = dims[2];
  
  return data;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    return the minimum and maximum value of the data. 
//
//////////////////////////////////////////////////////////////////////////////
void
SFScalarField::getMinMax (float &min, float &max) const
{
  if (numValues > 0) {
    min = data[0];
    max = data[0];
  }
  
  for (int i = 1; i < numValues; i++) {
    if (data[i] < min)
      min = data[i];
    else if (data[i] > max)
      max = data[i];
  }
}

void
SFScalarField::setSize (const int dm[3])
{
  int nextValuesAlloc;

  dims[0] = dm[0];
  dims[1] = dm[1];
  dims[2] = dm[2];

  // compute how many values the data needs
  nextValuesAlloc = dims[0]*dims[1]*dims[2];

  // check to see if we need more memory
  if (nextValuesAlloc > valuesAllocated) {
    valuesAllocated = nextValuesAlloc;

    if (myData)
      delete [] myData;

    myData = new float[nextValuesAlloc*sizeof(float)];
  }

  // set the number of values currently being used
  numValues = nextValuesAlloc;

  data = myData;
}

float *
SFScalarField::startEditing (int dm[3])
{
  dm[0] = dims[0];
  dm[1] = dims[1];
  dm[2] = dims[2];

  return data;
}

void
SFScalarField::finishEditing ()
{
  valueChanged();
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    assignment operator.  If the data from 'd' was copied then it is copied
//    to this field
//
//////////////////////////////////////////////////////////////////////////////
const SFScalarField &
SFScalarField::operator = (const SFScalarField &d)
{
  int dm[3];
  float *ptr = d.getValue(dm);

  setValue(dm, ptr, d.getDataCopied());

  return *this;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    equality operator.
//
//////////////////////////////////////////////////////////////////////////////
int
SFScalarField::operator == (const SFScalarField &d) const
{
  if (dims[0] != d.dims[0] ||
      dims[1] != d.dims[1] ||
      dims[2] != d.dims[2])
    return false;

  if (!dataCopied && data != d.data)
    return false;
  else if (dataCopied && memcmp(data, d.data, numValues*sizeof(float)))
    return false;

  return true;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    Reads value from file. Returns FALSE on error.
//
//////////////////////////////////////////////////////////////////////////////
SbBool
SFScalarField::readValue (SoInput *in)
{
  if (!in->read(dims[0]) ||
      !in->read(dims[1]) ||
      !in->read(dims[2]))
    return FALSE;

  setSize(dims);

  if (in->isBinary()) {
    if (!in->readBinaryArray(data, numValues))
      return FALSE;
  }
  else {
    for (int i = 0; i < numValues; i++)
      if (!in->read(data[i]))
	return FALSE;
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
//  Description:
//    Writes value of field to file.
//
//////////////////////////////////////////////////////////////////////////////
void
SFScalarField::writeValue (SoOutput *out) const
{
  int i;

  for (i = 0; i < 3; i++) {
    out->write(dims[i]);
    if (!out->isBinary())
      out->write(' ');
  }

  if (out->isBinary())
    out->writeBinaryArray(data, numValues);
  else {
    for (i = 0; i < numValues; i++) {
      out->write(' ');
      out->write(data[i]);
    }
  }
}
