//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#include "vtkDiscreteLookupTable.h"

#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"

#include <vector>

#define TABLE_CIRCLE_DEGREE 800

vtkStandardNewMacro(vtkDiscreteLookupTable);

vtkDiscreteLookupTable::vtkDiscreteLookupTable()
{
  this->Discretize = 1;
  this->NumberOfValues = 256;
  this->SetColorSpaceToHSV();

  this->HueDelta = 101.0; // degree in 360 degree circle.

  this->SaturationMax = 0.99;
  this->SaturationMin = 0.2;
  this->SaturationDelta = 0.2;

  this->ValueMax = 0.99;
  this->ValueMin = 0.2;
  this->ValueDelta = 0.2;
}

vtkDiscreteLookupTable::~vtkDiscreteLookupTable()
{
}

void vtkDiscreteLookupTable::Build()
{
  // skip the vtkPVLookupTable::Build()
  this->vtkColorTransferFunction::Build();

  this->LookupTable->SetVectorMode(this->VectorMode);
  this->LookupTable->SetVectorComponent(this->VectorComponent);

  if (this->Discretize &&
    (this->GetMTime() > this->BuildTime || this->GetMTime() > this->BuildTime))
  {
    unsigned char* lut_ptr = this->LookupTable->WritePointer(0, this->NumberOfValues);
    double* table = new double[this->NumberOfValues * 3];

    int numColors = 0;
    double h = 0.0, s = this->SaturationMax, v = this->ValueMax;
    double deltaH = this->HueDelta / TABLE_CIRCLE_DEGREE;
    while (numColors < this->NumberOfValues)
    {
      this->AddHSVPoint(numColors, h, s, v);
      h += deltaH;
      s = h > 1.0 ? s - this->SaturationDelta : s;
      s = s < this->SaturationMin ? this->SaturationMax : s;

      v = h > 1.0 ? v - this->ValueDelta : v;
      v = v < this->ValueMin ? this->ValueMax : s;

      h = h > 1.0 ? h - 1.0 : h;
      numColors++;
    }

    double range[2];
    this->GetRange(range);
    bool logRangeValid = true;
    if (this->UseLogScale)
    {
      logRangeValid = range[0] > 0.0 || range[1] < 0.0;
      if (!logRangeValid && this->LookupTable->GetScale() == VTK_SCALE_LOG10)
      {
        this->LookupTable->SetScaleToLinear();
      }
    }

    this->LookupTable->SetRange(range);
    if (this->UseLogScale && logRangeValid && this->LookupTable->GetScale() == VTK_SCALE_LINEAR)
    {
      this->LookupTable->SetScaleToLog10();
    }

    //this->SetColorSpaceToHSV();
    //this->AddHSVPoint(range[0], 0.66, 1.0, 1.0);
    //this->AddHSVPoint((range[0] + range[1]) * 0.5, 0.0, 1.0, 1.0);
    //this->AddHSVPoint(range[1], 0.33, 1.0, 1.0);

    this->GetTable(range[0], range[1], this->NumberOfValues, table);
    // Now, convert double to unsigned chars and fill the LUT.
    for (int cc = 0; cc < this->NumberOfValues; cc++)
    {
      lut_ptr[4 * cc] = static_cast<unsigned char>(255.0 * table[3 * cc] + 0.5);
      lut_ptr[4 * cc + 1] = static_cast<unsigned char>(255.0 * table[3 * cc + 1] + 0.5);
      lut_ptr[4 * cc + 2] = static_cast<unsigned char>(255.0 * table[3 * cc + 2] + 0.5);
      lut_ptr[4 * cc + 3] = 255;
    }
    delete[] table;

    this->BuildTime.Modified();
  }
}

void vtkDiscreteLookupTable::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "HueDelta: " << this->HueDelta << endl;
  os << indent << "SaturationDelta: " << this->SaturationDelta << endl;
  os << indent << "ValueDelta: " << this->ValueDelta << endl;
  os << indent << "SaturationMin: " << this->SaturationMin << endl;
  os << indent << "SaturationMax: " << this->SaturationMax << endl;
  os << indent << "ValueMin: " << this->ValueMin << endl;
  os << indent << "ValueMax: " << this->ValueMax << endl;
}
