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

  Program:   Visualization Toolkit
  Module:    vtkMapArrayValues.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkMapArrayValues.h"

#include "vtkAbstractArray.h"
#include "vtkCellData.h"
#include "vtkDataArray.h"
#include "vtkDataSet.h"
#include "vtkDataSetAttributes.h"
#include "vtkGraph.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkVariant.h"
#include <ctype.h>

#include <vtkstd/map>
#include <vtkstd/utility>

vtkStandardNewMacro(vtkMapArrayValues);

typedef vtkstd::map< vtkVariant, vtkVariant, vtkVariantLessThan > MapBase;
class vtkMapType : public MapBase {};

vtkMapArrayValues::vtkMapArrayValues()
{
  this->InputArrayName = 0;
  this->OutputArrayName = 0;
  this->SetOutputArrayName("ArrayMap");
  this->FieldType = vtkMapArrayValues::POINT_DATA;
  this->OutputArrayType = VTK_INT;
  this->PassArray = 0;
  this->FillValue = -1;

  this->Map = new vtkMapType;
}

vtkMapArrayValues::~vtkMapArrayValues()
{
  this->SetInputArrayName(0);
  this->SetOutputArrayName(0);
  delete this->Map;
}

void vtkMapArrayValues::AddToMap(char *from, int to)
{
  this->Map->insert(vtkstd::make_pair< vtkVariant, vtkVariant >(from, to));

  this->Modified();
}

void vtkMapArrayValues::AddToMap(int from, int to)
{
  this->Map->insert(vtkstd::make_pair< vtkVariant, vtkVariant >(from, to));

  this->Modified();
}

void vtkMapArrayValues::AddToMap(int from, char *to)
{
  this->Map->insert(vtkstd::make_pair< vtkVariant, vtkVariant >(from, to));

  this->Modified();
}

void vtkMapArrayValues::AddToMap(char *from, char *to)
{
  this->Map->insert(vtkstd::make_pair< vtkVariant, vtkVariant >(from, to));

  this->Modified();
}

void vtkMapArrayValues::AddToMap(vtkVariant from, vtkVariant to)
{
  this->Map->insert(vtkstd::make_pair< vtkVariant, vtkVariant >(from, to));

  this->Modified();
}

void vtkMapArrayValues::ClearMap()
{
  this->Map->clear();

  this->Modified();
}

int vtkMapArrayValues::GetMapSize()
{
  return static_cast<int>(this->Map->size());
}

int vtkMapArrayValues::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the input and output
  vtkDataObject *input = inInfo->Get(vtkDataObject::DATA_OBJECT());
  vtkDataObject *output = outInfo->Get(vtkDataObject::DATA_OBJECT());
  
  if(!this->InputArrayName)
    {
    //vtkErrorMacro(<<"Input array not specified.");
    output->ShallowCopy(input);
    return 1;
    }

  vtkDataSetAttributes* ods=0;
  if (vtkDataSet::SafeDownCast(input))
    {
    vtkDataSet *dsInput = vtkDataSet::SafeDownCast(input);
    vtkDataSet *dsOutput = vtkDataSet::SafeDownCast(output);
    // This has to be here because it initialized all field datas.
    dsOutput->CopyStructure( dsInput );

    if ( dsOutput->GetFieldData() && dsInput->GetFieldData() )
      {
      dsOutput->GetFieldData()->PassData( dsInput->GetFieldData() );
      }
    dsOutput->GetPointData()->PassData( dsInput->GetPointData() );
    dsOutput->GetCellData()->PassData( dsInput->GetCellData() );
    switch (this->FieldType)
      {
      case vtkMapArrayValues::POINT_DATA:
        ods = dsOutput->GetPointData();
        break;
      case vtkMapArrayValues::CELL_DATA:
        ods = dsOutput->GetCellData();
        break;
      default:
        vtkErrorMacro(<<"Data must be point or cell for vtkDataSet");
        return 0;
      }
    }
  else if (vtkGraph::SafeDownCast(input))
    {
    vtkGraph *graphInput = vtkGraph::SafeDownCast(input);
    vtkGraph *graphOutput = vtkGraph::SafeDownCast(output);
    graphOutput->ShallowCopy( graphInput );
    switch (this->FieldType)
      {
      case vtkMapArrayValues::VERTEX_DATA:
        ods = graphOutput->GetVertexData();
        break;
      case vtkMapArrayValues::EDGE_DATA:
        ods = graphOutput->GetEdgeData();
        break;
      default:
        vtkErrorMacro(<<"Data must be vertex or edge for vtkGraph");
        return 0;
      }
    }
  else if (vtkTable::SafeDownCast(input))
    {
    vtkTable *tableInput = vtkTable::SafeDownCast(input);
    vtkTable *tableOutput = vtkTable::SafeDownCast(output);
    tableOutput->ShallowCopy( tableInput );
    switch (this->FieldType)
      {
      case vtkMapArrayValues::ROW_DATA:
        ods = tableOutput->GetRowData();
        break;
      default:
        vtkErrorMacro(<<"Data must be row for vtkTable");
        return 0;
      }
    }

  vtkAbstractArray *inputArray = ods->GetAbstractArray(this->InputArrayName);
  if (!inputArray)
    {
    return 1;
    }
  vtkAbstractArray *outputArray = 
      vtkAbstractArray::CreateArray(this->OutputArrayType);
  vtkDataArray *outputDataArray = vtkDataArray::SafeDownCast(outputArray);
  vtkStringArray *outputStringArray = 
      vtkStringArray::SafeDownCast(outputArray);
  outputArray->SetName(this->OutputArrayName);

  // Are we copying the input array values to the output array before 
  // the mapping?
  if(this->PassArray)
    {
    // Make sure the DeepCopy will succeed
    if((inputArray->IsA("vtkDataArray") && outputArray->IsA("vtkDataArray"))
         || (inputArray->IsA("vtkStringArray")
             && outputArray->IsA("vtkStringArray")))
      {
      outputArray->DeepCopy(inputArray);
      }
    else
      {
      vtkIdType numComps = inputArray->GetNumberOfComponents();
      vtkIdType numTuples = inputArray->GetNumberOfTuples();
      outputArray->SetNumberOfComponents(numComps);
      outputArray->SetNumberOfTuples(numTuples);
      for (vtkIdType i = 0; i < numTuples; ++i)
        {
        for (vtkIdType j = 0; j < numComps; ++j)
          {
          outputArray->InsertVariantValue(
            i*numComps + j, inputArray->GetVariantValue(i*numComps + j));
          }
        }
      }
    }
  else
    {
    outputArray->SetNumberOfComponents(inputArray->GetNumberOfComponents());
    outputArray->SetNumberOfTuples(inputArray->GetNumberOfTuples());

    // Fill the output array with a default value
    if(outputDataArray)
      {
      outputDataArray->FillComponent(0, this->FillValue);
      }
    }

  // Use the internal map to set the mapped values in the output array
  vtkIdList* results = vtkIdList::New();
  for(MapBase::iterator i = this->Map->begin(); i != this->Map->end(); ++i)
    {
    inputArray->LookupValue(i->first, results);
    for(vtkIdType j=0; j<results->GetNumberOfIds(); ++j)
      {
      if(outputDataArray)
        {
        outputDataArray->SetComponent(results->GetId(j), 0, i->second.ToDouble());
        }
      else if(outputStringArray)
        {
        outputStringArray->SetValue(results->GetId(j), i->second.ToString());
        }
      }
    }

  // Finally, add the array to the appropriate vtkDataSetAttributes
  ods->AddArray(outputArray);

  results->Delete();
  outputArray->Delete();

  return 1;
}

int vtkMapArrayValues::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
{
  // This algorithm may accept a vtkPointSet or vtkGraph.
  info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkGraph");
  info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkTable");
  return 1;  
}

void vtkMapArrayValues::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Input array name: ";
  if (this->InputArrayName)
    {
    os << this->InputArrayName << endl;
    }
  else
    {
    os << "(none)" << endl;
    }

  os << indent << "Output array name: ";
  if (this->OutputArrayName)
    {
    os << this->OutputArrayName << endl;
    }
  else
    {
    os << "(none)" << endl;
    }
  os << indent << "Field type: " << this->FieldType << endl;
  os << indent << "Output array type: " << this->OutputArrayType << endl;
  os << indent << "PassArray: " << this->PassArray << endl;
  os << indent << "FillValue: " << this->FillValue << endl;
}
