// ===================================================================
// $Id: color.cpp,v 1.3 2002/12/20 20:36:09 havran Exp $
//
// color.cpp
//
//     This is the implementation of color concepts in general
//      that is suitable for photorealistic rendering
//
// Licence and usage conditions in November 2003:
// This file can be used and modified only with written permission of
// Vlastimil Havran, Pohodli 27, 57001 Litomysl, the Czech Republic.
// This library is granted to be used by Polina Kondratieva within
// her MPI HIWI job working on BRDFLIB library. The usage condition
// may change in the next version of the library. At the moment,
// the library development is supported by Max-Planck-Institut for
// Informatik, Stuhlsatzenhausweg 85, 66123 Saarbruecken Germany
// See WWW: http://www.mpi-sb.mpg.de
// REPLACEMENT_STRING
//
// Initial coding by Vlasta Havran, November, 1999
//
// Given to BRDFLIB project for the Polina Kondratieva's
// HIWI JOB assignment in November 2003.


// BRDFLIB headers
#include "config.h"
#include "color.h"


__BEGIN_BRDFLIB_SOURCE

//-------------------------------------------------
// static variables

// This is the only possible spectrum for RGB colors
const CSpectrum *
CValues::_spectrumRGB = &SpectrumRGB;


// --------------------------------------------------
// constructors

// copy constructor
CValues::CValues(const CValues &val)
{
  // copy the type of the result
  _type = val._type;

  if (val._type & ALLOCATED_VALUES) { // allocated components
    int N = val.GetCount();
    _Alloc(N);
#ifndef __CVALUES_HACK_COPY
    for (int i = 0; i < N; i++)
      v._valsN[i] = val.v._valsN[i];
#else
    memcpy(&(v._valsN[0]), &(val.v._valsN[0]), sizeof(float) * N);
#endif
    v._spectrum = val.v._spectrum;
    return;
  }
  else {
    // only copy the values to common variables
#ifndef __CVALUES_HACK_COPY
    for (int i = 0; i < C_FixedCount; i++)
      _vals[i] = val._vals[i];
#else
    // this can be dangerous !!! - be carefull
    memcpy(&(_vals[0]), &(val._vals[0]), sizeof(float) * C_FixedCount );
#endif
  }
}

// implicit constructor to construct a color according to the spectrum
// 1) Color1 2) Color2 3) Color3 4) Color4 5) ColorN
// When constructing Color3, the spectrum is changed to the new one
CValues::CValues(const CSpectrum &spectrum)
{
  int N = spectrum.GetCount();

  switch (N) {
    case 0: {
      FATAL << "Constructor for CValues failed\n";
      FATAL_ABORT;      
    }
    case 1: {
      _type = EE_Color1;
      _vals[0] = 0.0;
      v._spectrum = &spectrum;
      return;
    }
    case 2: {
      _type = EE_Color2;
      _vals[0] = _vals[1] =0.0;
      v._spectrum = &spectrum;
      return;
    }
    case 3: {
      _type = EE_Color3;
      _vals[0] = _vals[1] = _vals[2] = 0.0;
      _spectrumRGB = &spectrum; // spectrum for RGB is changed to new one
      return;
    }
    //    case 4: {
    //      _type = EE_Color4;
    //      v._spectrum = &spectrum; // spectrum for RGB is changed to new one
    //      _Alloc(4);
    //      return;
    //    }
    default: { // N-colors >4 (>=4)
      _type = EType(EE_ColorN + N);
      v._spectrum = &spectrum;
      _Alloc(N);
      return;
    }
  }
}

// implicit constructor to construct weights:
// 1) Weigth1 2) Weigth2 3) Weigth3 4) Weigth4 5) WeigthN
CValues::CValues(int N)
{
  assert( unsigned(N) < 65536U);
  
  switch (N) {
    case 0: {
      FATAL << "Constructor for CValues failed\n";
      FATAL_ABORT;      
    }
    case 1: {
      _type = EE_Weigth1;
      _vals[0] = 0.0;
      return;
    }
    case 2: {
      _type = EE_Weigth2;
      _vals[0] = _vals[1] =0.0;
      return;
    }
    case 3: {
      _type = EE_Weigth3;
      _vals[0] = _vals[1] = _vals[2] = 0.0;
      return;
    }
    //    case 4: {
    //      _type = EE_Weigth4;
    //      _Alloc(4);
    //      return;
    //    }
    default: { // Number of values >4 (>=4)
      _type = EType(EE_WeigthN + N);
      _Alloc(N);
      return;
    }
  }
}

// destructor
CValues::~CValues()
{
  if (_type & ALLOCATED_VALUES) {// free the allocated memory
    delete [] v._valsN;
    v._valsN = NULL;
  }
}

#if 0
// assignment operator
CValues&
CValues::operator=(const CValues &val)
{
  if (this == &val)
    return *this; // OK
  
  if (_type != val._type) {
    // This function sets to new required representation
    SetRepresentation(val._type, val.GetSpectrum());
  }

  // now only copy the values to this instance
    
  if (_type & ALLOCATED_VALUES) {
    // values in the allocated array
#ifndef __CVALUES_HACK_COPY
    float *dst = &(v._valsN[0]);
    const float *src = &(val.v._valsN[0]);
    for (int i = val.GetCount(); i--;)
      *dst++ = *src++;
#else
    memcpy(&(v._valsN[0]), &(val.v._valsN[0]), sizeof(float) * GetCount());
#endif
    // copy the spectrum pointer as well
    v._spectrum = val.v._spectrum;
  }
  else {
    // the common array of values 
#ifndef __CVALUES_HACK_COPY
    const float *src = &(val._vals[0]);
    float *dst = &(_vals[0]);
    for (int i = val.GetCount(); i--;)
      *dst++ = *src++;
#else
    memcpy(&(_vals[0]), &(val._vals[0]), sizeof(float) * GetCount());
#endif
  }
  
  return *this; // OK, everything finished
}
#else
// the second version of assignment operator, it can be more efficient
CValues&
CValues::operator=(const CValues &val)
{
  if (this == &val)
    return *this;

  if (val._type & ALLOCATED_VALUES) { // allocated components
    int N = val.GetCount();
    if (_type & ALLOCATED_VALUES) {// you have something to delete
      // if the number of components is not equal
      if ( GetCount() != val.GetCount() ) {
	delete [] v._valsN; // then really delete
	v._valsN = NULL;
	_Alloc(N);
      }
    }
    else
      _Alloc(N);

    // copy the type
    _type = val._type;
#ifndef __CVALUES_HACK_COPY
    for (int i = 0; i < N; i++)
      v._valsN[i] = val.v._valsN[i];
#else
    memcpy(&(v._valsN[0]), &(val.v._valsN[0]), sizeof(float) * N);
#endif
    v._spectrum = val.v._spectrum;
  }
  else {
    // the new type has no help
    if (_type & ALLOCATED_VALUES) {
      delete [] v._valsN;
      v._valsN = NULL;
    }

    // copy the type of the result
    _type = val._type;
    // only copy the values to common variables
#ifndef __CVALUES_HACK_COPY
    for (int i = 0; i < GetCount(); i++)
      _vals[i] = val._vals[i];
#else
    // this can be dangerous !!! - be carefull
    memcpy(&(_vals[0]), &(val._vals[0]), sizeof(float) * GetCount() );
#endif
  }
  return *this;
}
#endif



// This enables to set to another representation, the values
// are preserved if the number of the components is the same.
// When the change is performed to color with other than C_FixedCount
// components, corresponding spectrum must be provided !
// Otherwise, the content is not guaranted to have a meaning, please
// use explicit call of Init() above.
void
CValues::SetRepresentation(EType newType, const CSpectrum *newSpectrum)
{
  if (newType == _type)
    return; // nothing to do

  // the required number of components
  int numComponents = newType & 0xFFFF;
  
  if (numComponents == GetCount()) {
    // the number of components is the same

    // check if the spectrum has corresponding number of components
    if ( (newType != EE_Color3) && (newSpectrum)) {
      if (newSpectrum->GetCount() != numComponents) {
	FATAL << "SetRepresentation: inconsistent spectrum.";
	FATAL_ABORT;
      }
      // set new spectrum
      v._spectrum = newSpectrum;
    }
    _type = newType; // change the meaning
    return; // the content of values is preserved
  }

  // we have to possibly deallocate and realocate again
  if (_type & ALLOCATED_VALUES)  {// more than three components
    delete [] v._valsN;
    v._valsN = NULL;
  }

  // like the constructor
  _type = newType;

  if (_type & 0x200000) {
    // This is the color, spectrum must be set for non-RGB colors
    if (_type != EE_Color3) {
      if (newSpectrum) {
	if (newSpectrum->GetCount() != numComponents) {
	  FATAL << "SetRepresentation: inconsistent spectrum.\n";
	  FATAL_ABORT;
	}
      }
      else {
	FATAL << "SetRepresentation: spectrum is not specified for color\n";
	FATAL_ABORT;
      }

      // set new spectrum for color that has correct number of bands
      v._spectrum = newSpectrum;
    }
  }

  // allocate array if necessary
  if (numComponents > C_FixedCount)
    _Alloc(numComponents);  
}

#if 0
// this enables to set the spectrum for RGB color. This spectrum has
// to have exactly three components, otherwise spectrum is not changed.
// Returns true on success.
bool
CValues::SetSpectrumRGB(const CSpectrum *newSpectrum)
{
  if ( (newSpectrum) &&
       (newSpectrum->GetCount() == C_FixedCount) ) {
    // sets the static variable
    _spectrumRGB = (Spectrum)newSpectrum;
    return true; // ok
  }
  return false; // error, spectrum is not changed
}
#endif

// output to stream
ostream&
operator<< (ostream &s, const CValues &A)
{
  int N = A.GetCount();

  if (A.GetBaseType() == EE_ValuesColor)
    s << "ColorN=" << N  << " (";
  else
    s << "WeightN=" << N  << " (";    

  for (int i = 0; i < N; i++) {
    s << A[i];
    if (i < N-1)
      s << ", ";
  }
  s << ")";
  return s;
}

CValues::EType
CValues::GetColorType(int channels)
{
  assert( (channels > 0) && (channels < 65536));
  
  switch (channels) {
    case 1: return CValues::EE_Color1;
    case 2: return CValues::EE_Color2;
    case 3: return CValues::EE_Color3;
    // case 4: return CValues::EE_Color4;
    default: return CValues::EType(CValues::EE_ColorN + channels);
  }
}

CValues::EType
CValues::GetWeigthType(int channels)
{
  assert( (channels > 0) && (channels < 65536));

  switch (channels) {
    case 1: return CValues::EE_Weigth1;
    case 2: return CValues::EE_Weigth2;
    case 3: return CValues::EE_Weigth3;
    // case 4: return CValues::EE_Weigth4;
    default: return CValues::EType(CValues::EE_WeigthN + channels);
  }
}

// -----------------------------------------------------------------------
// Class CStandardValues
// -----------------------------------------------------------------------

// constructor
CStandardValues::CStandardValues()
{
  // maximum number of standard values bounded by this number. This
  // should be enough for ever
  _colorValuesNum = _weigthValuesNum = 100;
  
  _colorValues = new CValues *[_colorValuesNum];
  _spectrums = new CSpectrum *[_colorValuesNum];
  _weigthValues = new CValues *[_weigthValuesNum];
  
  int i;
  for (i = 0; i < _colorValuesNum; i++) {
    _colorValues[i] = NULL;
    _spectrums[i] = NULL;
  }    
  for (i = 0; i < _weigthValuesNum; i++) 
    _weigthValues[i] = NULL;
}

// destructor
CStandardValues::~CStandardValues()
{
  // counter
  int i;
  
  for (i = 0; i < _colorValuesNum; i++) 
    if (_colorValues[i]) {
      delete _colorValues[i];
      delete _spectrums[i];
    }

  for (i = 0; i < _weigthValuesNum; i++) 
    if (_weigthValues[i]) 
      delete _weigthValues[i];

  delete [] _colorValues;
  delete [] _spectrums;
  delete [] _weigthValues;
}

// sets new standard value
void
CStandardValues::SetValue(const CValues &newValue)
{
  // number of channels
  int channels = newValue.GetCount();
  
  if (newValue.GetBaseType() == EE_ValuesColor) {
    if (channels > _colorValuesNum) {
      // we must enlarge arrays "_colorValues" and "_spectrums"
      CValues **newColorValues = new CValues *[channels];
      CSpectrum **newSpectrums = new CSpectrum *[channels];
      // copy older informations and reset new informations
      memcpy(newColorValues, _colorValues,
	     _colorValuesNum * sizeof(CValues *));
      memcpy(newSpectrums, _spectrums, _colorValuesNum * sizeof(CSpectrum *));
      memset(newColorValues + _colorValuesNum, 0,
             (channels - _colorValuesNum) * sizeof(CValues *));
      memset(newSpectrums + _colorValuesNum, 0,
             (channels - _colorValuesNum) * sizeof(CSpectrum *));
      // delete old arrays
      delete [] _colorValues;
      delete [] _spectrums;
      // set new arrays
      _colorValues = newColorValues;
      _spectrums = newSpectrums;
      _colorValuesNum = channels;
    }
    
    // index of new value to the array  
    int selValue = channels - 1;

    if (!_colorValues[selValue]) {
      // we must alloc new CValues and new CSpectrum
      _colorValues[selValue] = new CValues(0.0f);
      _spectrums[selValue] = new CSpectrum(*(newValue.GetSpectrum()));
    }  

    // we set new standard value now
    *(_spectrums[selValue]) = *(newValue.GetSpectrum());
    // assign the color, use the representation of a new spectrum
    *(_colorValues[selValue]) = newValue;
    _colorValues[selValue]->SetRepresentation(newValue.GetType(),
                                              _spectrums[selValue]);
  }
  else {
    // type == EE_ValuesWeigth
    if (channels > _weigthValuesNum) {
      // we must enlarge array "_weigthValues"
      CValues **newWeigthValues = new CValues *[channels];
      // copy older informations and reset new informations
      memcpy(newWeigthValues, _weigthValues, 
             _weigthValuesNum * sizeof(CValues *));
      memset(newWeigthValues + _weigthValuesNum, 0,
             (channels - _weigthValuesNum) * sizeof(CValues *));
      // delete old array
      delete [] _weigthValues;
      // set new array
      _weigthValues = newWeigthValues;
      _weigthValuesNum = channels;
    }
    
    if (!_weigthValues[channels - 1]) { 
      // we must alloc new CValues
      _weigthValues[channels - 1] = new CValues(newValue.GetType());
    }
    // assign the value itself
    *(_weigthValues[channels - 1]) = newValue;
  }
}

// returns standard value for given number of channels 
// and for given type of the values
const CValues *
CStandardValues::GetValue(int channels, EValuesBaseType type)
{
  if (type == EE_ValuesColor) {
    // number of channels must be positive
    assert(channels > 0);
    // standard values for this number of channels must be set
    assert((channels <= _colorValuesNum) && (_colorValues[channels - 1]));
    // the selected standard value
    return _colorValues[channels - 1];
  }
  else {
    // type == EE_ValuesWeigth
    // number of channels must be positive
    assert(channels > 0);

    if ((channels > _weigthValuesNum) || (!_weigthValues[channels - 1])) {
      // standart value for this number of channels isn't defined
      // we try to generate it
      SetValue(CValues(CValues::GetWeigthType(channels)));
    }
    // the selected standard value
    return _weigthValues[channels - 1];
  }
}

__END_BRDFLIB_SOURCE
