/*=========================================================================

 Program:   ParaView
 Module:    vtkPVArrayInformation.cxx

 Copyright (c) Kitware, Inc.
 All rights reserved.
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 cxx     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notice for more information.

 =========================================================================*/
#include "vtkPVArrayInformation.h"

#include "vtkAbstractArray.h"
#include "vtkClientServerStream.h"
#include "vtkDataArray.h"
#include "vtkObjectFactory.h"
#include "vtkInformation.h"
#include "vtkInformationKey.h"
#include "vtkInformationIterator.h"
#include "vtkNew.h"
#include "vtkStringArray.h"
#include "vtkStdString.h"
#include "vtkPVPostFilter.h"
#include "vtkVariant.h"
#include "vtkVariantArray.h"

#include <map>
#include <set>
#include <vector>
#include <vtksys/ios/sstream>

namespace
{
  typedef std::vector<vtkStdString*> vtkInternalComponentNameBase;

  struct vtkPVArrayInformationInformationKey
  {
    vtkStdString Location;
    vtkStdString Name;
  };

  typedef std::vector<vtkPVArrayInformationInformationKey> vtkInternalInformationKeysBase;
}

class vtkPVArrayInformation::vtkInternalComponentNames:
    public vtkInternalComponentNameBase
{
};

class vtkPVArrayInformation::vtkInternalInformationKeys:
    public vtkInternalInformationKeysBase
{
};

vtkStandardNewMacro(vtkPVArrayInformation);

//----------------------------------------------------------------------------
vtkPVArrayInformation::vtkPVArrayInformation()
{
  this->Name = 0;
  this->Ranges = 0;
  this->ComponentNames = 0;
  this->DefaultComponentName = 0;
  this->InformationKeys = 0;
  this->Initialize();
}

//----------------------------------------------------------------------------
vtkPVArrayInformation::~vtkPVArrayInformation()
{
  this->Initialize();
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::Initialize()
{
  this->SetName(0);
  this->DataType = VTK_VOID;
  this->NumberOfComponents = 0;
  this->NumberOfTuples = 0;

  if (this->ComponentNames)
    {
    for ( unsigned int i=0; i < this->ComponentNames->size(); ++i)
      {
      if ( this->ComponentNames->at(i) )
        {
        delete this->ComponentNames->at(i);
        }
      }
    this->ComponentNames->clear();
    delete this->ComponentNames;
    this->ComponentNames = 0;
    }

  if (this->DefaultComponentName)
    {
    delete this->DefaultComponentName;
    this->DefaultComponentName = 0;
    }

  if (this->Ranges)
    {
    delete[] this->Ranges;
    this->Ranges = 0;
    }
  this->IsPartial = 0;

  if(this->InformationKeys)
    {
    this->InformationKeys->clear();
    delete this->InformationKeys;
    this->InformationKeys = 0;
    }
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::PrintSelf(ostream& os, vtkIndent indent)
{
  int num, idx;
  vtkIndent i2 = indent.GetNextIndent();

  this->Superclass::PrintSelf(os, indent);
  if (this->Name)
    {
    os << indent << "Name: " << this->Name << endl;
    }
  os << indent << "DataType: " << this->DataType << endl;
  os << indent << "NumberOfComponents: " << this->NumberOfComponents << endl;
  if (this->ComponentNames)
    {
    os << indent << "ComponentNames:" << endl;
    for (unsigned int i = 0; i < this->ComponentNames->size(); ++i)
      {
      os << i2 << this->ComponentNames->at(i) << endl;
      }
    }
  os << indent << "NumberOfTuples: " << this->NumberOfTuples << endl;
  os << indent << "IsPartial: " << this->IsPartial << endl;

  os << indent << "Ranges :" << endl;
  num = this->NumberOfComponents;
  if (num > 1)
    {
    ++num;
    }
  for (idx = 0; idx < num; ++idx)
    {
    os << i2 << this->Ranges[2 * idx] << ", " << this->Ranges[2 * idx + 1]
        << endl;
    }

  os << indent << "InformationKeys :" << endl;
  if(this->InformationKeys)
    {
    num = this->GetNumberOfInformationKeys();
    for (idx = 0; idx < num; ++idx)
      {
      os << i2 << this->GetInformationKeyLocation(idx) << "::"
          << this->GetInformationKeyName(idx) << endl;
      }
    }
  else
    {
    os << i2 << "None" << endl;
    }
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::SetNumberOfComponents(int numComps)
{
  if (this->NumberOfComponents == numComps)
    {
    return;
    }
  if (this->Ranges)
    {
    delete[] this->Ranges;
    this->Ranges = NULL;
    }
  this->NumberOfComponents = numComps;
  if (numComps <= 0)
    {
    this->NumberOfComponents = 0;
    return;
    }
  if (numComps > 1)
    { // Extra range for vector magnitude (first in array).
    numComps = numComps + 1;
    }

  int idx;
  this->Ranges = new double[numComps * 2];
  for (idx = 0; idx < numComps; ++idx)
    {
    this->Ranges[2 * idx] = VTK_DOUBLE_MAX;
    this->Ranges[2 * idx + 1] = -VTK_DOUBLE_MAX;
    }
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::SetComponentName(vtkIdType component,
    const char *name)
{
  if (component < 0 || name == NULL)
    {
    return;
    }

  unsigned int index = static_cast<unsigned int> (component);
  if (this->ComponentNames == NULL)
    {
    //delayed allocate
    this->ComponentNames
        = new vtkPVArrayInformation::vtkInternalComponentNames();
    }

  if (index == this->ComponentNames->size())
    {
    //the array isn't large enough, so we will resize
    this->ComponentNames->push_back(new vtkStdString(name));
    return;
    }
  else if (index > this->ComponentNames->size())
    {
    this->ComponentNames->resize(index + 1, NULL);
    }

  //replace an exisiting element
  vtkStdString *compName = this->ComponentNames->at(index);
  if (!compName)
    {
    compName = new vtkStdString(name);
    this->ComponentNames->at(index) = compName;
    }
  else
    {
    compName->assign(name);
    }
}

//----------------------------------------------------------------------------
const char* vtkPVArrayInformation::GetComponentName(vtkIdType component)
{
  unsigned int index = static_cast<unsigned int> (component);
  //check signed component for less than zero
  if (this->ComponentNames && component >= 0 && index
      < this->ComponentNames->size())
    {
    vtkStdString *compName = this->ComponentNames->at(index);
    if (compName)
      {
      return compName->c_str();
      }
    }
  else if (this->ComponentNames && component == -1
      && this->ComponentNames->size() == 1)
    {
    // the array is a scalar i.e. single component. In that case, when one asks
    // for the magnitude (i.e. component == -1) it's same as asking for the
    // scalar. So return the scalar's component name.
    vtkStdString *compName = this->ComponentNames->at(0);
    if (compName)
      {
      return compName->c_str();
      }
    }
  //we have failed to find a user set component name, use the default component name
  this->DetermineDefaultComponentName(component, this->GetNumberOfComponents());
  return this->DefaultComponentName->c_str();
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::SetComponentRange(int comp, double min, double max)
{
  if (comp >= this->NumberOfComponents || this->NumberOfComponents <= 0)
    {
    vtkErrorMacro("Bad component");
    }
  if (this->NumberOfComponents > 1)
    { // Shift over vector mag range.
    ++comp;
    }
  if (comp < 0)
    { // anything less than 0 just defaults to the vector mag.
    comp = 0;
    }
  this->Ranges[comp * 2] = min;
  this->Ranges[comp * 2 + 1] = max;
}

//----------------------------------------------------------------------------
double* vtkPVArrayInformation::GetComponentRange(int comp)
{
  if (comp >= this->NumberOfComponents || this->NumberOfComponents <= 0)
    {
    vtkErrorMacro("Bad component");
    return NULL;
    }
  if (this->NumberOfComponents > 1)
    { // Shift over vector mag range.
    ++comp;
    }
  if (comp < 0)
    { // anything less than 0 just defaults to the vector mag.
    comp = 0;
    }
  return this->Ranges + comp * 2;
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::GetComponentRange(int comp, double *range)
{
  double *ptr;

  ptr = this->GetComponentRange(comp);

  if (ptr == NULL)
    {
    range[0] = VTK_DOUBLE_MAX;
    range[1] = -VTK_DOUBLE_MAX;
    return;
    }

  range[0] = ptr[0];
  range[1] = ptr[1];
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::GetDataTypeRange(double range[2])
{
  int dataType = this->GetDataType();
  switch (dataType)
    {
    case VTK_BIT:
      range[0] = VTK_BIT_MAX;
      range[1] = VTK_BIT_MAX;
      break;
    case VTK_UNSIGNED_CHAR:
      range[0] = VTK_UNSIGNED_CHAR_MIN;
      range[1] = VTK_UNSIGNED_CHAR_MAX;
      break;
    case VTK_CHAR:
      range[0] = VTK_CHAR_MIN;
      range[1] = VTK_CHAR_MAX;
      break;
    case VTK_UNSIGNED_SHORT:
      range[0] = VTK_UNSIGNED_SHORT_MIN;
      range[1] = VTK_UNSIGNED_SHORT_MAX;
      break;
    case VTK_SHORT:
      range[0] = VTK_SHORT_MIN;
      range[1] = VTK_SHORT_MAX;
      break;
    case VTK_UNSIGNED_INT:
      range[0] = VTK_UNSIGNED_INT_MIN;
      range[1] = VTK_UNSIGNED_INT_MAX;
      break;
    case VTK_INT:
      range[0] = VTK_INT_MIN;
      range[1] = VTK_INT_MAX;
      break;
    case VTK_UNSIGNED_LONG:
      range[0] = VTK_UNSIGNED_LONG_MIN;
      range[1] = VTK_UNSIGNED_LONG_MAX;
      break;
    case VTK_LONG:
      range[0] = VTK_LONG_MIN;
      range[1] = VTK_LONG_MAX;
      break;
    case VTK_FLOAT:
      range[0] = VTK_FLOAT_MIN;
      range[1] = VTK_FLOAT_MAX;
      break;
    case VTK_DOUBLE:
      range[0] = VTK_DOUBLE_MIN;
      range[1] = VTK_DOUBLE_MAX;
      break;
    default:
      // Default value:
      range[0] = 0;
      range[1] = 1;
      break;
    }
}
//----------------------------------------------------------------------------
void vtkPVArrayInformation::AddRanges(vtkPVArrayInformation *info)
{
  double *range;
  double *ptr = this->Ranges;
  int idx;

  if (this->NumberOfComponents != info->GetNumberOfComponents())
    {
    vtkErrorMacro("Component mismatch.");
    }

  if (this->NumberOfComponents > 1)
    {
    range = info->GetComponentRange(-1);
    if (range[0] < ptr[0])
      {
      ptr[0] = range[0];
      }
    if (range[1] > ptr[1])
      {
      ptr[1] = range[1];
      }
    ptr += 2;
    }

  for (idx = 0; idx < this->NumberOfComponents; ++idx)
    {
    range = info->GetComponentRange(idx);
    if (range[0] < ptr[0])
      {
      ptr[0] = range[0];
      }
    if (range[1] > ptr[1])
      {
      ptr[1] = range[1];
      }
    ptr += 2;
    }

  this->NumberOfTuples += info->GetNumberOfTuples();
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::DeepCopy( vtkPVArrayInformation* info )
{
  int num, idx;

  this->SetName(info->GetName());
  this->DataType = info->GetDataType();
  this->SetNumberOfComponents(info->GetNumberOfComponents());
  this->SetNumberOfTuples(info->GetNumberOfTuples());

  num = 2 * this->NumberOfComponents;
  if (this->NumberOfComponents > 1)
    {
    num += 2;
    }
  for (idx = 0; idx < num; ++idx)
    {
    this->Ranges[idx] = info->Ranges[idx];
    }

  //clear the vector of old data
  if (this->ComponentNames)
    {
    for ( unsigned int i=0; i < this->ComponentNames->size(); ++i)
      {
      if ( this->ComponentNames->at(i) )
        {
        delete this->ComponentNames->at(i);
        }
      }
    this->ComponentNames->clear();
    delete this->ComponentNames;
    this->ComponentNames = 0;
    }

  if (info->ComponentNames)
    {
    this->ComponentNames
        = new vtkPVArrayInformation::vtkInternalComponentNames();
    //copy the passed in components if they exist
    this->ComponentNames->reserve(info->ComponentNames->size());
    const char *name;
    for (unsigned i = 0; i < info->ComponentNames->size(); ++i)
      {
      name = info->GetComponentName(i);
      if (name)
        {
        this->SetComponentName(i, name);
        }
      }
    }

  if (!this->InformationKeys)
    {
    this->InformationKeys
        = new vtkPVArrayInformation::vtkInternalInformationKeys();
    }

  //clear the vector of old data
  this->InformationKeys->clear();

  if (info->InformationKeys)
    {
    //copy the passed in components if they exist
    for (unsigned i = 0; i < info->InformationKeys->size(); ++i)
      {
      this->InformationKeys->push_back(info->InformationKeys->at(i));
      }
    }
}

//----------------------------------------------------------------------------
int vtkPVArrayInformation::Compare(vtkPVArrayInformation *info)
{
  if (info == NULL)
    {
    return 0;
    }
  if (strcmp(info->GetName(), this->Name) == 0 && info->GetNumberOfComponents()
      == this->NumberOfComponents && this->GetNumberOfInformationKeys()
      == info->GetNumberOfInformationKeys())
    {
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::CopyFromObject(vtkObject* obj)
{
  if (!obj)
    {
    this->Initialize();
    }

  vtkAbstractArray* const array = vtkAbstractArray::SafeDownCast(obj);
  if (!array)
    {
    vtkErrorMacro("Cannot downcast to abstract array.");
    this->Initialize();
    return;
    }

  this->SetName(array->GetName());
  this->DataType = array->GetDataType();
  this->SetNumberOfComponents(array->GetNumberOfComponents());
  this->SetNumberOfTuples(array->GetNumberOfTuples());

  if (array->HasAComponentName())
    {
    const char *name;
    //copy the component names over
    for (int i = 0; i < this->GetNumberOfComponents(); ++i)
      {
      name = array->GetComponentName(i);
      if (name)
        {
        //each component doesn't have to be named
        this->SetComponentName(i, name);
        }
      }
    }

  if (vtkDataArray* const data_array = vtkDataArray::SafeDownCast(obj))
    {
    double range[2];
    double *ptr;
    int idx;

    ptr = this->Ranges;
    if (this->NumberOfComponents > 1)
      {
      // First store range of vector magnitude.
      data_array->GetRange(range, -1);
      *ptr++ = range[0];
      *ptr++ = range[1];
      }
    for (idx = 0; idx < this->NumberOfComponents; ++idx)
      {
      data_array->GetRange(range, idx);
      *ptr++ = range[0];
      *ptr++ = range[1];
      }
    }

  if(this->InformationKeys)
    {
    this->InformationKeys->clear();
    delete this->InformationKeys;
    this->InformationKeys = 0;
    }
  if (array->HasInformation())
    {
    vtkInformation* info = array->GetInformation();
    vtkInformationIterator* it = vtkInformationIterator::New();
    it->SetInformationWeak(info);
    it->GoToFirstItem();
    while (!it->IsDoneWithTraversal())
      {
      vtkInformationKey* key = it->GetCurrentKey();
      this->AddInformationKey(key->GetLocation(), key->GetName());
      it->GoToNextItem();
      }
    it->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::AddInformation(vtkPVInformation* info)
{
  if (!info)
    {
    return;
    }

  vtkPVArrayInformation* aInfo = vtkPVArrayInformation::SafeDownCast(info);
  if (!aInfo)
    {
    vtkErrorMacro("Could not downcast info to array info.");
    return;
    }
  if (aInfo->GetNumberOfComponents() > 0)
    {
    if (this->NumberOfComponents == 0)
      {
      // If this object is uninitialized, copy.
      this->DeepCopy(aInfo);
      }
    else
      {
      // Leave everything but ranges and unique values as original, add ranges and unique values.
      this->AddRanges(aInfo);
      this->AddInformationKeys(aInfo);
      }
    }
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::CopyToStream(vtkClientServerStream* css)
{
  css->Reset();
  *css << vtkClientServerStream::Reply;

  // Array name, data type, and number of components.
  *css << this->Name;
  *css << this->DataType;
  *css << this->NumberOfTuples;
  *css << this->NumberOfComponents;
  *css << this->IsPartial;

  // Range of each component.
  int num = this->NumberOfComponents;
  if (this->NumberOfComponents > 1)
    {
    // First range is range of vector magnitude.
    ++num;
    }
  for (int i = 0; i < num; ++i)
    {
    *css << vtkClientServerStream::InsertArray(this->Ranges + 2 * i, 2);
    }

  //add in the component names
  num = static_cast<int> (this->ComponentNames ? this->ComponentNames->size()
      : 0);
  *css << num;
  vtkStdString *compName;
  for (int i = 0; i < num; ++i)
    {
    compName = this->ComponentNames->at(i);
    *css << (compName? compName->c_str() : static_cast<const char*>(NULL));
    }

  int nkeys = this->GetNumberOfInformationKeys();
  *css << nkeys;
  for (int key = 0; key < nkeys; key++)
    {
    const char* location = this->GetInformationKeyLocation(key);
    const char* name = this->GetInformationKeyName(key);
    *css << location << name;
    }

  *css << vtkClientServerStream::End;
}

//----------------------------------------------------------------------------
void vtkPVArrayInformation::CopyFromStream(const vtkClientServerStream* css)
{
  // Array name.
  const char* name = 0;
  if (!css->GetArgument(0, 0, &name))
    {
    vtkErrorMacro("Error parsing array name from message.");
    return;
    }
  this->SetName(name);

  // Data type.
  if (!css->GetArgument(0, 1, &this->DataType))
    {
    vtkErrorMacro("Error parsing array data type from message.");
    return;
    }

  // Number of tuples.
  if (!css->GetArgument(0, 2, &this->NumberOfTuples))
    {
    vtkErrorMacro("Error parsing number of tuples from message.");
    return;
    }

  // Number of components.
  int num;
  if (!css->GetArgument(0, 3, &num))
    {
    vtkErrorMacro("Error parsing number of components from message.");
    return;
    }
  // This needs to be called since it allocates the this->Ranges array.
  this->SetNumberOfComponents(num);
  if (num > 1)
    {
    num++;
    }

  // Is Partial
  if (!css->GetArgument(0, 4, &this->IsPartial))
    {
    vtkErrorMacro("Error parsing IsPartial from message.");
    return;
    }

  // Range of each component.
  for (int i = 0; i < num; ++i)
    {
    if (!css->GetArgument(0, 5 + i, this->Ranges + 2 * i, 2))
      {
      vtkErrorMacro("Error parsing range of component.");
      return;
      }
    }
  int pos = 5 + num;
  int numOfComponentNames;
  if (!css->GetArgument(0, pos++, &numOfComponentNames))
    {
    vtkErrorMacro("Error parsing number of component names.");
    return;
    }

  if (numOfComponentNames > 0)
    {
      //clear the vector of old data
    if (this->ComponentNames)
      {
      for ( unsigned int i=0; i < this->ComponentNames->size(); ++i)
        {
        if ( this->ComponentNames->at(i) )
          {
          delete this->ComponentNames->at(i);
          }
        }
      this->ComponentNames->clear();
      delete this->ComponentNames;
      this->ComponentNames = 0;
      }
    this->ComponentNames
          = new vtkPVArrayInformation::vtkInternalComponentNames();

    this->ComponentNames->reserve(numOfComponentNames);

    for (int cc=0; cc < numOfComponentNames; cc++)
      {
      const char* comp_name;
      if (!css->GetArgument(0, pos++, &comp_name))
        {
        vtkErrorMacro("Error parsing component name from message.");
        return;
        }
      // note comp_name may be NULL, but that's okay.
      this->SetComponentName(cc, comp_name);
      }
    }

  // information keys
  int nkeys;
  if (!css->GetArgument(0, pos++, &nkeys))
    {
    return;
    }

  // Location and name for each key.
  if (this->InformationKeys)
    {
    this->InformationKeys->clear();
    delete this->InformationKeys;
    this->InformationKeys = 0;
    }
  for (int i = 0; i < nkeys; ++i)
    {
    if (!css->GetArgument(0, pos++, &name))
      {
      vtkErrorMacro("Error parsing information key location from message.");
      return;
      }
    vtkStdString key_location = name;

    if (!css->GetArgument(0, pos++, &name))
      {
      vtkErrorMacro("Error parsing information key name from message.");
      return;
      }
    vtkStdString key_name = name;
    this->AddInformationKey(key_location, key_name);
    }
}

//-----------------------------------------------------------------------------
void vtkPVArrayInformation::DetermineDefaultComponentName(
    const int &component_no, const int &num_components)
{
  if (!this->DefaultComponentName)
    {
    this->DefaultComponentName = new vtkStdString();
    }

  this->DefaultComponentName->assign(vtkPVPostFilter::DefaultComponentName(component_no, num_components));
}

void vtkPVArrayInformation::AddInformationKeys(vtkPVArrayInformation *info)
{
  for (int k = 0; k < info->GetNumberOfInformationKeys(); k++)
    {
    this->AddUniqueInformationKey(info->GetInformationKeyLocation(k),
        info->GetInformationKeyName(k));
    }
}

void vtkPVArrayInformation::AddInformationKey(const char* location,
    const char* name)
{
  if(this->InformationKeys == NULL)
    {
    this->InformationKeys = new vtkInternalInformationKeys();
    }
  vtkPVArrayInformationInformationKey info = vtkPVArrayInformationInformationKey();
  info.Location = location;
  info.Name = name;
  this->InformationKeys->push_back(info);
}

void vtkPVArrayInformation::AddUniqueInformationKey(const char* location,
    const char* name)
{
  if (!this->HasInformationKey(location, name))
    {
    this->AddInformationKey(location, name);
    }
}

int vtkPVArrayInformation::GetNumberOfInformationKeys()
{
  return static_cast<int>(this->InformationKeys ? this->InformationKeys->size() : 0);
}

const char* vtkPVArrayInformation::GetInformationKeyLocation(int index)
{
  if (index < 0 || index >= this->GetNumberOfInformationKeys())
    return NULL;

  return this->InformationKeys->at(index).Location;
}

const char* vtkPVArrayInformation::GetInformationKeyName(int index)
{
  if (index < 0 || index >= this->GetNumberOfInformationKeys())
    return NULL;

  return this->InformationKeys->at(index).Name;
}

int vtkPVArrayInformation::HasInformationKey(const char* location,
    const char* name)
{
  for (int k = 0; k < this->GetNumberOfInformationKeys(); k++)
    {
    const char* key_location = this->GetInformationKeyLocation(k);
    const char* key_name = this->GetInformationKeyName(k);
    if (strcmp(location, key_location) == 0 && strcmp(name, key_name) == 0)
      {
      return 1;
      }
    }
  return 0;
}
