//=========================================================================
//  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 "vtkCommonSerializationHelper.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkSerializationHelperMap.h"
#include "vtkSerializer.h"
#include "vtkTransform.h"
#include "vtkXMLElement.h"

vtkStandardNewMacro(vtkCommonSerializationHelper);

vtkCommonSerializationHelper::vtkCommonSerializationHelper()
{
}

int vtkCommonSerializationHelper::Serialize(vtkObject* object, vtkSerializer* serializer)
{
  if (vtkTransform::SafeDownCast(object))
  {
    this->SerializeTransform(vtkTransform::SafeDownCast(object), serializer);
    return 1;
  }
  // make sure object is a type of vtkDataArray that is supported
  else if (vtkDataArray::SafeDownCast(object) && this->GetSerializationType(object))
  {
    this->SerializeDataArray(vtkDataArray::SafeDownCast(object), serializer);
    return 1;
  }

  return 0;
}

void vtkCommonSerializationHelper::RegisterWithHelperMap()
{
  vtkSerializationHelperMap::RegisterHelperForClass("vtkTransform", this);
  vtkSerializationHelperMap::RegisterHelperForClass("vtkIdTypeArray", this);
  vtkSerializationHelperMap::RegisterHelperForClass("vtkDoubleArray", this);
  vtkSerializationHelperMap::RegisterHelperForClass("vtkIntArray", this);
}

void vtkCommonSerializationHelper::UnRegisterWithHelperMap()
{
  vtkSerializationHelperMap::UnRegisterHelperForClass("vtkTransform", this);
  vtkSerializationHelperMap::UnRegisterHelperForClass("vtkIdTypeArray", this);
  vtkSerializationHelperMap::UnRegisterHelperForClass("vtkDoubleArray", this);
  vtkSerializationHelperMap::UnRegisterHelperForClass("vtkIntArray", this);
}

void vtkCommonSerializationHelper::SerializeTransform(
  vtkTransform* transform, vtkSerializer* serializer)
{
  if (serializer->IsWriting())
  {
    unsigned int length = 16;
    double* elementsPtr = *transform->GetMatrix()->Element;
    // just the matrix for now... plan to add better support for serializing transforms
    serializer->Serialize("Matrix", elementsPtr, length);
  }
  else
  {
    unsigned int length = 0;
    double* elements = 0;
    serializer->Serialize("Matrix", elements, length);
    if (length > 0)
    {
      transform->Concatenate(elements);
      delete[] elements;
    }
  }
}

void vtkCommonSerializationHelper::SerializeDataArray(
  vtkDataArray* dataArray, vtkSerializer* serializer)
{
  if (serializer->IsWriting())
  {
    if (dataArray->HasInformation())
    {
      serializer->Serialize("ArrayInformation", dataArray->GetInformation());
    }

    int numberOfComponents = dataArray->GetNumberOfComponents();
    serializer->Serialize("NumberOfComponents", numberOfComponents);

    // Upon reading we are going to be limited to array of length vtkIdType,
    // which if not using 64-bit ID's means at most ~2^31 (~2 billion) values...
    // which is a LOT when considering this will be written as ASCII (this
    // serialization is NOT intended for storing LARGE data).  Therefore, for the
    // time being, we write out at most what can be represented by an int.
    unsigned long numberOfValuesLong = dataArray->GetDataSize();
    unsigned int numberOfValues = static_cast<unsigned int>(static_cast<int>(numberOfValuesLong));
    if (numberOfValues < numberOfValuesLong)
    {
      vtkErrorMacro("Unable to Serialize the entire array; too large!");
    }

    // now handle the type specfic
    if (vtkIdTypeArray::SafeDownCast(dataArray))
    {
      vtkIdType* arrayPtr = vtkIdTypeArray::SafeDownCast(dataArray)->GetPointer(0);
      serializer->Serialize("Array", arrayPtr, numberOfValues);
    }
    else if (vtkDoubleArray::SafeDownCast(dataArray))
    {
      double* arrayPtr = vtkDoubleArray::SafeDownCast(dataArray)->GetPointer(0);
      serializer->Serialize("Array", arrayPtr, numberOfValues);
    }
    else if (vtkIntArray::SafeDownCast(dataArray))
    {
      int* arrayPtr = vtkIntArray::SafeDownCast(dataArray)->GetPointer(0);
      serializer->Serialize("Array", arrayPtr, numberOfValues);
    }
  }
  else
  {
    // handle vtkInformation, if present
    vtkSmartPointer<vtkInformation> infoObject = vtkSmartPointer<vtkInformation>::New();
    serializer->Serialize("ArrayInformation", infoObject);
    if (infoObject->GetNumberOfKeys() > 0)
    {
      dataArray->GetInformation()->Copy(infoObject, 1);
    }

    // how many components
    int numberOfComponents;
    serializer->Serialize("NumberOfComponents", numberOfComponents);
    dataArray->SetNumberOfComponents(numberOfComponents);

    // now
    unsigned int numberOfValues = 0;
    if (vtkIdTypeArray::SafeDownCast(dataArray))
    {
      vtkIdType* arrayPtr = 0;
      serializer->Serialize("Array", arrayPtr, numberOfValues);
      if (numberOfValues > 0)
      {
        // the dataArray assumes reposibility for the array allocated
        // in Serialize(), indicated by the final arguement of "0"
        vtkIdTypeArray::SafeDownCast(dataArray)->SetArray(arrayPtr,
          static_cast<vtkIdType>(numberOfValues), 0, vtkIdTypeArray::VTK_DATA_ARRAY_DELETE);
      }
    }
    else if (vtkDoubleArray::SafeDownCast(dataArray))
    {
      double* arrayPtr = 0;
      serializer->Serialize("Array", arrayPtr, numberOfValues);
      if (numberOfValues > 0)
      {
        // the dataArray assumes reposibility for the array allocated
        // in Serialize(), indicated by the final arguement of "0"
        vtkDoubleArray::SafeDownCast(dataArray)->SetArray(arrayPtr,
          static_cast<vtkIdType>(numberOfValues), 0, vtkDoubleArray::VTK_DATA_ARRAY_DELETE);
      }
    }
    else if (vtkIntArray::SafeDownCast(dataArray))
    {
      int* arrayPtr = 0;
      serializer->Serialize("Array", arrayPtr, numberOfValues);
      if (numberOfValues > 0)
      {
        // the dataArray assumes reposibility for the array allocated
        // in Serialize(), indicated by the final arguement of "0"
        vtkIntArray::SafeDownCast(dataArray)->SetArray(
          arrayPtr, static_cast<vtkIdType>(numberOfValues), 0, vtkIntArray::VTK_DATA_ARRAY_DELETE);
      }
    }
  }
}

const char* vtkCommonSerializationHelper::GetSerializationType(vtkObject* object)
{
  if (vtkTransform::SafeDownCast(object))
  {
    return "vtkTransform";
  }
  if (vtkIdTypeArray::SafeDownCast(object))
  {
    return "vtkIdTypeArray";
  }
  if (vtkDoubleArray::SafeDownCast(object))
  {
    return "vtkDoubleArray";
  }
  if (vtkIntArray::SafeDownCast(object))
  {
    return "vtkIntArray";
  }
  return 0;
}

void vtkCommonSerializationHelper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Supported ClassTypes:\n";
  os << indent.GetNextIndent() << "vtkTransform\n";
  os << indent.GetNextIndent() << "vtkDoubleArray\n";
  os << indent.GetNextIndent() << "vtkIntArray\n";
  os << indent.GetNextIndent() << "vtkIdTypeArray\n";
}
