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

  Program:   Visualization Toolkit
  Module:    vtkMergeFields.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 "vtkMergeFields.h"

#include "vtkCellData.h"
#include "vtkDataSet.h"
#include "vtkDataSetAttributes.h"
#include "vtkFloatArray.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"

vtkCxxRevisionMacro(vtkMergeFields, "1.16");
vtkStandardNewMacro(vtkMergeFields);

char vtkMergeFields::FieldLocationNames[3][12] 
= { "DATA_OBJECT",
    "POINT_DATA",
    "CELL_DATA" };

typedef vtkMergeFields::Component Component;

vtkMergeFields::vtkMergeFields()
{
  this->FieldName = 0;
  this->FieldLocation = -1;
  this->NumberOfComponents = 0;

  this->Head = 0;
  this->Tail = 0;
}

vtkMergeFields::~vtkMergeFields()
{
  delete[] this->FieldName;
  this->FieldName = 0;
  this->DeleteAllComponents();
}

void vtkMergeFields::SetOutputField(const char* name, int fieldLoc)
{
  if (!name)
    {
    return;
    }

  if ( (fieldLoc !=  vtkMergeFields::DATA_OBJECT) &&
       (fieldLoc !=  vtkMergeFields::POINT_DATA) &&
       (fieldLoc !=  vtkMergeFields::CELL_DATA) )
    {
    vtkErrorMacro("The source for the field is wrong.");
    return;
    }

  this->Modified();
  this->FieldLocation = fieldLoc;

  delete[] this->FieldName;
  this->FieldName = new char[strlen(name)+1];
  strcpy(this->FieldName, name);
}


void vtkMergeFields::SetOutputField(const char* name, const char* fieldLoc)
{
  if ( !name || !fieldLoc)
    {
    return;
    }

  int numFieldLocs = 3;
  int i;

  // Convert fieldLoc to int an call the other SetOutputField()
  int loc=-1;
  for(i=0; i<numFieldLocs; i++)
    {
    if (!strcmp(fieldLoc, FieldLocationNames[i]))
      {
      loc = i;
      break;
      }
    }
  if (loc == -1)
    {
    vtkErrorMacro("Location for the field is invalid.");
    return;
    }

  this->SetOutputField(name, loc);

}

void vtkMergeFields::Merge(int component, const char* arrayName, 
                           int sourceComp)
{
  if (!arrayName)
    {
    return;
    }

  this->Modified();
  Component* comp = this->FindComponent(component);
  if ( comp )
    {
    // If component already exists, replace information
    comp->SetName(arrayName);
    comp->SourceIndex = sourceComp;
    }
  else
    {
    // otherwise create a new one
    comp = new Component;
    comp->SetName(arrayName);
    comp->Index = component;
    comp->SourceIndex = sourceComp;
    this->AddComponent(comp);
    }
}

void vtkMergeFields::Execute()
{
  vtkDataSet *input = static_cast<vtkDataSet*>(this->GetInput());
  vtkDataSet *output = static_cast<vtkDataSet*>(this->GetOutput());

  // This has to be here because it initialized all field datas.
  output->CopyStructure( input );
  
  // Pass all. (data object's field data is passed by the
  // superclass after this method)
  output->GetPointData()->PassData( input->GetPointData() );
  output->GetCellData()->PassData( input->GetCellData() );

  vtkFieldData* fd = 0;
  vtkFieldData* outputFD = 0;
  Component* cur = this->GetFirst();
  Component* before;

  if (!cur) { return; }

  // Get the input and output field data
  if ( this->FieldLocation == vtkMergeFields::DATA_OBJECT)
    {
    fd = input->GetFieldData();
    outputFD = output->GetFieldData();
    if (!fd || !outputFD)
      {
      vtkErrorMacro("No field data in vtkDataObject.");
      return;
      }
    }
  else if ( this->FieldLocation == vtkMergeFields::POINT_DATA )
    {
    fd = input->GetPointData();
    outputFD = output->GetPointData();
    }
  else if ( this->FieldLocation == vtkMergeFields::CELL_DATA )
    {
    fd = input->GetCellData();
    outputFD = output->GetCellData();
    }

  // Check if the data types of the input fields are the same
  // Otherwise warn the user.
  // Check if the number of tuples are the same for all arrays.
  vtkDataArray* inputArray = 0;
  int dataType=-1;
  int sameDataType=1;
  int numTuples=-1;
  int sameNumTuples=1;
  do
    {
    before = cur;
    cur = cur->Next;
    inputArray = fd->GetArray(before->FieldName);
    if (!inputArray)
      {
      continue;
      }
    else
      {
      if (dataType == -1)
        {
        dataType = inputArray->GetDataType();
        }
      else
        {
        if ( inputArray->GetDataType() != dataType )
          {
          sameDataType = 0;
          }
        }
      if (numTuples == -1)
        {
        numTuples = inputArray->GetNumberOfTuples();
        }
      else
        {
        if ( inputArray->GetNumberOfTuples() != numTuples )
          {
          sameNumTuples = 0;
          }
        }
      }
    } 
  while (cur);
  if (!sameNumTuples)
    {
    vtkErrorMacro("The number of tuples in the input arrays do not match.");
    return;
    }
  if ( dataType == -1 )
    {
    vtkErrorMacro("No input array(s) were found.");
    return;
    }
  vtkDataArray* outputArray;
  if (!sameDataType)
    {
    vtkWarningMacro("The input data types do not match. The output will be "<<
                    "float. This will potentially cause accuracy and speed.");
    outputArray = vtkFloatArray::New();
    }
  else
    {
    outputArray = vtkDataArray::CreateDataArray(dataType);
    }

  if (this->NumberOfComponents <= 0)
    {
    vtkErrorMacro("NumberOfComponents has be set prior to the execution of "
                  "this filter");
    }

  outputArray->SetNumberOfComponents(this->NumberOfComponents);
  outputArray->SetNumberOfTuples(numTuples);
  outputArray->SetName(this->FieldName);

  // Merge
  cur = this->GetFirst();
  do
    {
    before = cur;
    cur = cur->Next;
    inputArray = fd->GetArray(before->FieldName);
    if (inputArray)
      {
      if (!this->MergeArray(inputArray, outputArray, 
                            before->SourceIndex, before->Index))
        {
        outputArray->Delete();
        outputArray = 0;
        return;
        }
      }
    else
      {
      if (before->FieldName)
        {
        vtkWarningMacro("Input array " << before->FieldName 
                        << " does not exist.");
        }
      continue;
      }
    } 
  while (cur);
  outputFD->AddArray(outputArray);
  outputArray->Delete();
}

// fast pointer copy
template <class T>
void vtkMergeFieldsCopyTuples(T* input, T* output, vtkIdType numTuples, 
                int numInComp, int numOutComp, int inComp, int outComp)
{
  for (int i=0; i<numTuples; i++)
    {
    output[numOutComp*i+outComp] = input[numInComp*i+inComp];
    }
}

int vtkMergeFields::MergeArray(vtkDataArray* in, vtkDataArray* out,
                               int inComp, int outComp)
{
  if ( (inComp < 0) || (inComp > in->GetNumberOfComponents()) ||
       (outComp < 0) || (outComp > out->GetNumberOfComponents()) )
    {
    vtkErrorMacro("Invalid component. Can not merge.");
    return 0;
    }

  int numTuples = in->GetNumberOfTuples();
  int i;

  if ( numTuples > 0 )
    {
    // If data types match, use templated, fast method
    if ( in->GetDataType() == out->GetDataType() )
      {
      switch (out->GetDataType())
        {
        vtkTemplateMacro7(vtkMergeFieldsCopyTuples, (VTK_TT *)in->GetVoidPointer(0), 
                          (VTK_TT *)out->GetVoidPointer(0), numTuples,
                          in->GetNumberOfComponents(), 
                          out->GetNumberOfComponents(), inComp, outComp );
        // This is not supported by the template macro.
        // Switch to using the float interface.
        case VTK_BIT:
        {
        for(i=0; i<numTuples; i++)
          {
          out->SetComponent(i, outComp, in->GetComponent(i, inComp));
          }
        }
        break;
        default:
          vtkErrorMacro(<<"Sanity check failed: Unsupported data type.");
          return 0;
        }
      }
    // otherwise use slow float copy
    else
      {
      for(i=0; i<numTuples; i++)
        {
        out->SetComponent(i, outComp, in->GetComponent(i, inComp));
        }
      }
    }
  
  return 1;

}

// linked list methods
void vtkMergeFields::AddComponent(Component* op)
{
  op->Next = 0;

  if (!this->Head)
    {
    this->Head = op;
    this->Tail = op;
    return;
    }
  this->Tail->Next = op;
  this->Tail = op;
}

Component* vtkMergeFields::FindComponent(int index)
{
  Component* cur = this->GetFirst();
  if (!cur) { return 0; }

  if (cur->Index == index) { return cur; }
  while (cur->Next)
    {
    if (cur->Next->Index == index)
      {
      return cur->Next;
      }
    cur = cur->Next;
    }
  return 0;
}

void vtkMergeFields::DeleteAllComponents()
{
  Component* cur = this->GetFirst();
  if (!cur) {return;}
  Component* before;
  do 
    {
    before = cur;
    cur = cur->Next;
    delete before;
    } 
  while (cur);
  this->Head = 0;
  this->Tail = 0;
}

void vtkMergeFields::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Field name: ";
  if (this->FieldName)
    {
    os << this->FieldName << endl;
    }
  else
    {
    os << "(none)" << endl;
    }
  os << indent << "Field location: " << this->FieldLocation << endl;
  os << indent << "Linked list head: " << this->Head << endl;
  os << indent << "Linked list tail: " << this->Tail << endl;
  os << indent << "NumberOfComponents: " << this->NumberOfComponents << endl;
  os << indent << "Components: " << endl;
  this->PrintAllComponents(os, indent.GetNextIndent());
}

void vtkMergeFields::PrintComponent(Component* op, ostream& os, vtkIndent indent)
{
  os << indent << "Field name: " << op->FieldName << endl;
  os << indent << "Component index: " << op->Index << endl;
  os << indent << "Source component index: " << op->SourceIndex << endl;
}

void vtkMergeFields::PrintAllComponents(ostream& os, vtkIndent indent)
{
  Component* cur = this->GetFirst();
  if (!cur) { return; }
  Component* before;
  do
    {
    before = cur;
    cur = cur->Next;
    os << endl;
    this->PrintComponent(before, os, indent);
    } 
  while (cur);
}
