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

  Program:   ParaView
  Module:    vtkPVDataSetAttributesInformation.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
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

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

#include "vtkClientServerStream.h"
#include "vtkCollection.h"
#include "vtkDataSetAttributes.h"
#include "vtkObjectFactory.h"
#include "vtkPVArrayInformation.h"

#include <vtkstd/vector>

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPVDataSetAttributesInformation);
vtkCxxRevisionMacro(vtkPVDataSetAttributesInformation, "1.2");

//----------------------------------------------------------------------------
vtkPVDataSetAttributesInformation::vtkPVDataSetAttributesInformation()
{
  int idx;

  this->ArrayInformation = vtkCollection::New();
  for (idx = 0; idx < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx)
    {
    this->AttributeIndices[idx] = -1;
    }
}

//----------------------------------------------------------------------------
vtkPVDataSetAttributesInformation::~vtkPVDataSetAttributesInformation()
{
  this->ArrayInformation->Delete();
  this->ArrayInformation = NULL;
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkPVArrayInformation *ai;
  vtkIndent i2 = indent.GetNextIndent();
  this->Superclass::PrintSelf(os,indent);

  int num, idx;
  num = this->GetNumberOfArrays();
  os << indent << "ArrayInformation " << num << endl;
  for (idx = 0; idx < num; ++idx)
    {
    ai = this->GetArrayInformation(idx);
    ai->PrintSelf(os, i2);
    }
}

//----------------------------------------------------------------------------
void vtkPVDataSetAttributesInformation::Initialize()
{
  int idx;

  this->ArrayInformation->RemoveAllItems();
  for (idx = 0; idx < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx)
    {
    this->AttributeIndices[idx] = -1;
    }
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation
::DeepCopy(vtkPVDataSetAttributesInformation *dataInfo)
{
  int idx, num;
  vtkPVArrayInformation* arrayInfo;
  vtkPVArrayInformation* newArrayInfo;

  // Copy array information.
  this->ArrayInformation->RemoveAllItems();
  num = dataInfo->GetNumberOfArrays();
  for (idx = 0; idx < num; ++idx)
    {
    arrayInfo = dataInfo->GetArrayInformation(idx);
    newArrayInfo = vtkPVArrayInformation::New();
    newArrayInfo->DeepCopy(arrayInfo);
    this->ArrayInformation->AddItem(newArrayInfo);
    newArrayInfo->Delete();
    newArrayInfo = NULL;
    }
  // Now the default attributes.
  for (idx = 0; idx < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx)
    {
    this->AttributeIndices[idx] = dataInfo->AttributeIndices[idx];
    }
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation
::CopyFromDataSetAttributes(vtkDataSetAttributes *da)
{
  int idx;
  int num;
  vtkDataArray *array;
  short infoArrayIndex;
  int attribute;

  // Clear array information.
  this->ArrayInformation->RemoveAllItems();
  for (idx = 0; idx < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx)
    {
    this->AttributeIndices[idx] = -1;
    }

  // Copy Point Data
  num = da->GetNumberOfArrays();
  infoArrayIndex = 0;
  for (idx = 0; idx < num; ++idx)
    {
    array = da->GetArray(idx);
    if (array->GetName() && strcmp(array->GetName(),"vtkGhostLevels") != 0)
      {
      vtkPVArrayInformation *info = vtkPVArrayInformation::New();
      info->CopyFromObject(array);
      this->ArrayInformation->AddItem(info);
      info->Delete();
      // Record default attributes.
      attribute = da->IsArrayAnAttribute(idx);
      if (attribute > -1)
        {
        this->AttributeIndices[attribute] = infoArrayIndex;
        }
      ++infoArrayIndex;
      }
    }
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation
::AddInformation(vtkPVDataSetAttributesInformation *info)
{
  int idx1, idx2;
  int num1 = this->GetNumberOfArrays();
  int num2 = info->GetNumberOfArrays();
  short  newAttributeIndices[vtkDataSetAttributes::NUM_ATTRIBUTES];

  for (idx1 = 0; idx1 < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx1)
    {
    newAttributeIndices[idx1] = -1;
    }

  // First add ranges from all common arrays
  for (idx1 = 0; idx1 < num1; ++idx1)
    {
    int found=0;
    vtkPVArrayInformation* ai1 = this->GetArrayInformation(idx1);
    for (idx2 = 0; idx2 < num2; ++idx2)
      {
      vtkPVArrayInformation* ai2 = info->GetArrayInformation(idx2);
      if ( ai1->Compare(ai2) )
        {
        // Take union of range.
        ai1->AddRanges(ai2);
        found = 1;
        // Record default attributes.
        int attribute1 = this->IsArrayAnAttribute(idx1);
        int attribute2 = info->IsArrayAnAttribute(idx2);
        if (attribute1 > -1 && attribute1 == attribute2)
          {
          newAttributeIndices[attribute1] = idx1;
          }
        break;
        }
      }
    if (!found)
      {
      ai1->SetIsPartial(1);
      }
    }

  for (idx1 = 0; idx1 < vtkDataSetAttributes::NUM_ATTRIBUTES; ++idx1)
    {
    this->AttributeIndices[idx1] = newAttributeIndices[idx1];
    }

  // Now add arrays that don't exist
  for (idx2 = 0; idx2 < num2; ++idx2)
    {
    vtkPVArrayInformation* ai2 = info->GetArrayInformation(idx2);
    int found=0;
    for (idx1 = 0; idx1 < this->GetNumberOfArrays(); ++idx1)
      {
      vtkPVArrayInformation* ai1 = this->GetArrayInformation(idx1);
      if ( ai1->Compare(ai2) )
        {
        found=1;
        break;
        }
      }
    if (!found)
      {
      ai2->SetIsPartial(1);
      this->ArrayInformation->AddItem(ai2);
      }
    }
}

//----------------------------------------------------------------------------
void vtkPVDataSetAttributesInformation::AddInformation(vtkPVInformation* info)
{
  vtkPVDataSetAttributesInformation* p =
    vtkPVDataSetAttributesInformation::SafeDownCast(info);
  if(p)
    {
    this->AddInformation(p);
    }
  else
    {
    vtkErrorMacro("AddInformation called with object of type "
                  << (info? info->GetClassName():"<unknown>"));
    }
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation
::AddInformation(vtkDataSetAttributes *da)
{
  vtkPVDataSetAttributesInformation* info =
    vtkPVDataSetAttributesInformation::New();

  info->CopyFromDataSetAttributes(da);
  this->AddInformation(info);
  info->Delete();
}

//----------------------------------------------------------------------------
int vtkPVDataSetAttributesInformation::IsArrayAnAttribute(int arrayIndex)
{
  int i;

  for (i = 0; i < vtkDataSetAttributes::NUM_ATTRIBUTES; ++i)
    {
    if (this->AttributeIndices[i] == arrayIndex)
      {
      return i;
      }
    }
  return -1;
}

//----------------------------------------------------------------------------
vtkPVArrayInformation*
vtkPVDataSetAttributesInformation::GetAttributeInformation(int attributeType)
{
  int arrayIdx = this->AttributeIndices[attributeType];

  if (arrayIdx < 0)
    {
    return NULL;
    }
  return this->GetArrayInformation(arrayIdx);
}

//----------------------------------------------------------------------------
int vtkPVDataSetAttributesInformation::GetNumberOfArrays() const
{
  return this->ArrayInformation->GetNumberOfItems();
}

//----------------------------------------------------------------------------
vtkPVArrayInformation*
vtkPVDataSetAttributesInformation::GetArrayInformation(int idx) const
{
  return static_cast<vtkPVArrayInformation*>(this->ArrayInformation->GetItemAsObject(idx));
}

//----------------------------------------------------------------------------
vtkPVArrayInformation*
vtkPVDataSetAttributesInformation::GetArrayInformation(const char *name) const
{
  vtkPVArrayInformation* info;

  if (name == NULL)
    {
    return NULL;
    }

  this->ArrayInformation->InitTraversal();
  while ( (info = static_cast<vtkPVArrayInformation*>(this->ArrayInformation->GetNextItemAsObject())) )
    {
    if (strcmp(info->GetName(), name) == 0)
      {
      return info;
      }
    }
  return NULL;
}

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

  // Default attributes.
  *css << vtkClientServerStream::InsertArray(this->AttributeIndices, vtkDataSetAttributes::NUM_ATTRIBUTES);

  // Number of arrays.
  *css << this->GetNumberOfArrays();

  // Serialize each array's information.
  vtkClientServerStream acss;
  for(int idx=0; idx < this->GetNumberOfArrays(); ++idx)
    {
    const unsigned char* data;
    size_t length;
    this->GetArrayInformation(idx)->CopyToStream(&acss);
    acss.GetData(&data, &length);
    *css << vtkClientServerStream::InsertArray(data, length);
    acss.Reset();
    }

  *css << vtkClientServerStream::End;
}

//----------------------------------------------------------------------------
void
vtkPVDataSetAttributesInformation
::CopyFromStream(const vtkClientServerStream* css)
{
  this->ArrayInformation->RemoveAllItems();

  // Default attributes.
  if(!css->GetArgument(0, 0, this->AttributeIndices, vtkDataSetAttributes::NUM_ATTRIBUTES))
    {
    vtkErrorMacro("Error parsing default attributes from message.");
    return;
    }

  // Number of arrays.
  int numArrays = 0;
  if(!css->GetArgument(0, 1, &numArrays))
    {
    vtkErrorMacro("Error parsing number of arrays from message.");
    return;
    }

  // Each array's information.
  vtkClientServerStream acss;
  vtkstd::vector<unsigned char> data;
  for(int i=0; i < numArrays; ++i)
    {
    vtkTypeUInt32 length;
    if(!css->GetArgumentLength(0, i+2, &length))
      {
      vtkErrorMacro("Error parsing length of information for array number "
                    << i << " from message.");
      return;
      }
    data.resize(length);
    if(!css->GetArgument(0, i+2, &*data.begin(), length))
      {
      vtkErrorMacro("Error parsing information for array number "
                    << i << " from message.");
      return;
      }
    acss.SetData(&*data.begin(), length);
    vtkPVArrayInformation* ai = vtkPVArrayInformation::New();
    ai->CopyFromStream(&acss);
    this->ArrayInformation->AddItem(ai);
    ai->Delete();
    }
}
