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

#include "vtkCollection.h"
#include "vtkDiscreteModel.h"
#include "vtkDiscreteModelEdge.h"
#include "vtkDiscreteModelEntityGroup.h"
#include "vtkDiscreteModelFace.h"
#include "vtkDiscreteModelRegion.h"
#include "vtkDiscreteModelVertex.h"
#include "vtkInformation.h"
#include "vtkInformationDoubleKey.h"
#include "vtkInformationDoubleVectorKey.h"
#include "vtkInformationIdTypeKey.h"
#include "vtkInformationIntegerKey.h"
#include "vtkInformationIntegerVectorKey.h"
#include "vtkInformationKeyMap.h"
#include "vtkInformationObjectBaseKey.h"
#include "vtkInformationStringKey.h"
#include "vtkModelMaterial.h"
#include "vtkModelUserName.h"
#include "vtkModelXMLParser.h"
#include "vtkXMLElement.h"
#
#include "vtkModelEdgeUse.h"
#include "vtkModelFaceUse.h"
#include "vtkModelItemIterator.h"
#include "vtkModelLoopUse.h"
#include "vtkModelShellUse.h"
#include "vtkModelVertexUse.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkType.h"
#
#include <map>
#include <sstream>
#include <vtksys/SystemTools.hxx>

vtkStandardNewMacro(vtkXMLModelReader);

vtkCxxSetObjectMacro(vtkXMLModelReader, RootElement, vtkXMLElement);
vtkCxxSetObjectMacro(vtkXMLModelReader, Model, vtkDiscreteModel);

namespace
{

template <typename KeyType, typename ValueType>
void SerializeScalarKey(vtkInformation* info, KeyType* key, vtkXMLElement* elem)
{
  ValueType val;
  if (elem->GetScalarAttribute("value", &val))
  {
    info->Set(key, val);
  }
}

template <typename KeyType, typename ValueType>
void SerializeVectorKey(vtkInformation* info, KeyType* key, vtkXMLElement* elem)
{
  int length;
  if (!elem->GetScalarAttribute("length", &length))
  {
    return;
  }
  if (length > 0)
  {
    ValueType* vals = new ValueType[length];
    if (elem->GetVectorAttribute("values", length, vals))
    {
      info->Set(key, vals, length);
    }
    delete[] vals;
  }
}
}

vtkXMLModelReader::vtkXMLModelReader()
{
  // Register keys with the map. This makes it possible for the
  // archiver to access this key when reading an archive
  vtkInformationKeyMap::RemoveAllKeys();
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::UNIQUEPERSISTENTID());
  vtkInformationKeyMap::RegisterKey(vtkModelUserName::USERNAME());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::COLOR());
  vtkInformationKeyMap::RegisterKey(vtkDiscreteModelRegion::POINTINSIDE());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::VISIBILITY());
  vtkInformationKeyMap::RegisterKey(vtkDiscreteModelEdge::LINERESOLUTION());
  vtkInformationKeyMap::RegisterKey(vtkModelEdgeUse::DIRECTION());
  vtkInformationKeyMap::RegisterKey(vtkDiscreteModelVertex::POINTID());
  vtkInformationKeyMap::RegisterKey(vtkDiscreteModelRegion::SOLIDFILENAME());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::DISPLAY_PROPERTY());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::PICKABLE());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::USERDATA());
  vtkInformationKeyMap::RegisterKey(vtkModelEntity::SHOWTEXTURE());

  this->RootElement = 0;
  this->CurrentElement = 0;
  this->Model = 0;
}

vtkXMLModelReader::~vtkXMLModelReader()
{
  this->SetRootElement(0);
  this->SetModel(0);
}

void vtkXMLModelReader::GetElementsByType(
  vtkXMLElement* element, const char* type, vtkCollection* elements)
{
  if (!elements)
  {
    vtkErrorMacro("Elements cannot be NULL.");
    return;
  }
  if (!type)
  {
    vtkErrorMacro("Type cannot be NULL.");
    return;
  }

  unsigned int numChildren = element->GetNumberOfNestedElements();
  unsigned int cc;
  for (cc = 0; cc < numChildren; cc++)
  {
    vtkXMLElement* child = element->GetNestedElement(cc);
    if (child && child->GetAttribute("type") && strcmp(child->GetAttribute("type"), type) == 0)
    {
      elements->AddItem(child);
    }
  }

  for (cc = 0; cc < numChildren; cc++)
  {
    vtkXMLElement* child = element->GetNestedElement(cc);
    if (child)
    {
      this->GetElementsByType(child, type, elements);
    }
  }
}

void vtkXMLModelReader::Serialize(istream& str, const char*)
{
  this->ParseStream(str);

  int version;
  if (this->RootElement->GetScalarAttribute("version", &version))
  {
    this->SetArchiveVersion(version);
  }

  vtkModel* model = this->GetModel();
  bool blockEvent = model->GetBlockModelGeometricEntityEvent();
  model->BlockModelGeometricEntityEventOn();

  this->SerializeModel();
  this->Serialize("vtkModelMaterial");
  this->Serialize("vtkDiscreteModelVertex");
  this->Serialize("vtkModelVertexUse");
  this->Serialize("vtkDiscreteModelEdge");
  this->Serialize("vtkModelEdgeUse");
  this->Serialize("vtkDiscreteModelFace");
  this->Serialize("vtkModelFaceUse");
  this->Serialize("vtkModelLoopUse");
  this->Serialize("vtkDiscreteModelRegion");
  this->Serialize("vtkModelShellUse");
  this->Serialize("vtkDiscreteModelEntityGroup");

  model->SetBlockModelGeometricEntityEvent(blockEvent);
  int types[4] = { vtkModelVertexType, vtkModelEdgeType, vtkModelFaceType, vtkModelRegionType };
  for (int i = 0; i < 4; i++)
  {
    if (this->GetModel()->GetNumberOfAssociations(types[i]))
    {
      vtkModelItemIterator* entities = this->GetModel()->NewIterator(types[i]);
      for (entities->Begin(); !entities->IsAtEnd(); entities->Next())
      {
        this->GetModel()->InvokeModelGeometricEntityEvent(
          ModelGeometricEntityCreated, entities->GetCurrentItem());
      }
      entities->Delete();
    }
  }
}

void vtkXMLModelReader::SerializeModel()
{
  vtkSmartPointer<vtkCollection> collection = vtkSmartPointer<vtkCollection>::New();
  this->GetElementsByType(this->RootElement, "vtkDiscreteModel", collection);

  if (collection->GetNumberOfItems() != 1)
  {
    vtkWarningMacro("Could not find proper vtkDiscreteModel XML element.");
    return;
  }
  this->CurrentElement = vtkXMLElement::SafeDownCast(collection->GetItemAsObject(0));
  vtkIdType id = 0;
  if (!this->CurrentElement->GetScalarAttribute("id", &id))
  {
    vtkWarningMacro("Could not get object id");
    return;
  }

  vtkDiscreteModel* model = this->GetModel();
  if (!model)
  {
    model = vtkDiscreteModel::New();
    this->SetModel(model);
    model->Delete();
  }
  model->Serialize(this);
}

void vtkXMLModelReader::Serialize(const char* ObjectName)
{
  if (!this->Model)
  {
    vtkErrorMacro("Model needs to be set before model entities can be added.");
    return;
  }
  vtkSmartPointer<vtkCollection> collection = vtkSmartPointer<vtkCollection>::New();
  this->GetElementsByType(this->RootElement, ObjectName, collection);

  for (int i = 0; i < collection->GetNumberOfItems(); i++)
  {
    this->CurrentElement = vtkXMLElement::SafeDownCast(collection->GetItemAsObject(i));
    vtkIdType id = 0;
    if (!this->CurrentElement->GetScalarAttribute("id", &id))
    {
      vtkWarningMacro("Could not get object id");
      continue;
    }
    vtkSerializableObject* obj = NULL;
    if (!strcmp(ObjectName, "vtkModelMaterial"))
    {
      obj = this->Model->BuildMaterial(id);
    }
    else if (!strcmp(ObjectName, "vtkDiscreteModelFace"))
    {
      obj = this->ConstructModelFace(id);
    }
    else if (!strcmp(ObjectName, "vtkModelFaceUse"))
    {
      obj = this->ConstructModelFaceUse(id);
      if (!obj)
      {
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkModelLoopUse"))
    {
      obj = this->ConstructModelLoopUse(id);
      if (!obj)
      {
        vtkErrorMacro("Could not construct model loop use.");
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkDiscreteModelRegion"))
    {
      obj = this->ConstructModelRegion(id);
      if (!obj)
      {
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkDiscreteModelEdge"))
    {
      obj = this->ConstructModelEdge(id);
      if (!obj)
      {
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkModelEdgeUse"))
    {
      obj = this->ConstructModelEdgeUse(id);
      if (!obj)
      {
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkModelShellUse"))
    {
      obj = this->ConstructModelShellUse(id);
      if (!obj)
      {
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkDiscreteModelVertex"))
    {
      obj = this->ConstructModelVertex(id);
    }
    else if (!strcmp(ObjectName, "vtkModelVertexUse"))
    {
      obj = this->ConstructModelVertexUse(id);
      if (!obj)
      {
        vtkErrorMacro("Could not construct model vertex use.");
        return;
      }
    }
    else if (!strcmp(ObjectName, "vtkDiscreteModelEntityGroup"))
    {
      obj = this->ConstructModelEntityGroup(id);
      if (!obj)
      {
        return;
      }
    }
    else
    {
      vtkErrorMacro("Unknown object name: " << ObjectName);
      return;
    }
    obj->Serialize(this);
  }
}

vtkModelLoopUse* vtkXMLModelReader::ConstructModelLoopUse(int /*id*/)
{
  std::vector<vtkIdType> associatedModelFaceUse;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelFaceUseType, associatedModelFaceUse);
  if (associatedModelFaceUse.size() != 1)
  {
    vtkErrorMacro("ModelLoopUse has incorrect number of adjacent model faces uses.");
    return 0;
  }
  // because the model doesn't directly know about face uses we have to go through
  // all of the faces and then look there for the face use we want
  vtkModelItemIterator* faces = this->Model->NewIterator(vtkModelFaceType);
  vtkModelFaceUse* faceUse = 0;
  for (faces->Begin(); !faces->IsAtEnd() && faceUse == 0; faces->Next())
  {
    vtkModelFace* face = vtkModelFace::SafeDownCast(faces->GetCurrentItem());
    faceUse = vtkModelFaceUse::SafeDownCast(
      face->GetModelEntity(vtkModelFaceUseType, associatedModelFaceUse[0]));
  }
  faces->Delete();
  if (!faceUse)
  {
    vtkErrorMacro("ModelFaceUse is not yet available for needed ModelLoopUse.");
    return 0;
  }
  // now build up the model edge uses
  std::vector<vtkIdType> associatedModelEdgeUses;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelEdgeUseType, associatedModelEdgeUses);
  if (associatedModelEdgeUses.size() == 0)
  {
    vtkErrorMacro("ModelLoopUse has incorrect number of adjacent model edge uses.");
    return 0;
  }

  vtkModelLoopUse* loopUse = vtkModelLoopUse::New();
  faceUse->AddLoopUse(loopUse);
  loopUse->Delete();

  for (size_t i = 0; i < associatedModelEdgeUses.size(); i++)
  {
    // get edge use adjacencies
    std::ostringstream idstr;
    idstr << associatedModelEdgeUses[i] << ends;
    vtkXMLElement* edgeUseElement = this->RootElement->FindNestedElement(idstr.str().c_str());
    std::vector<vtkIdType> edgeUseEdge;
    this->GetAssociations(
      edgeUseElement->FindNestedElementByName("Associations"), vtkModelEdgeType, edgeUseEdge);
    if (edgeUseEdge.size() != 1)
    {
      vtkErrorMacro("Could not find edge of edge use.");
    }
    vtkModelEdge* edge =
      vtkModelEdge::SafeDownCast(this->Model->GetModelEntity(vtkModelEdgeType, edgeUseEdge[0]));
    vtkModelEntity* edgeUse = edge->GetModelEntity(vtkModelEdgeUseType, associatedModelEdgeUses[i]);
    loopUse->AddAssociation(edgeUse);
  }

  return loopUse;
}

vtkModelFace* vtkXMLModelReader::ConstructModelFace(int id)
{
  std::vector<vtkIdType> modelFaceUses;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelFaceUseType, modelFaceUses);
  if (modelFaceUses.size() != 2)
  {
    vtkErrorMacro("Model face does not have two uses.");
    return 0;
  }
  vtkDiscreteModelFace* modelFace = vtkDiscreteModelFace::New();
  this->Model->AddAssociation(modelFace);
  modelFace->Delete();
  modelFace->SetUniquePersistentId(id);
  modelFace->GetModelFaceUse(0)->SetUniquePersistentId(modelFaceUses[0]);
  modelFace->GetModelFaceUse(1)->SetUniquePersistentId(modelFaceUses[1]);

  // if this model face is not adjacent to any model regions then
  // there will be a material associated with it
  std::vector<vtkIdType> materials;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), vtkModelMaterialType, materials);
  if (materials.size() == 1)
  {
    vtkModelMaterial* material = vtkModelMaterial::SafeDownCast(
      this->Model->GetModelEntity(vtkModelMaterialType, materials[0]));
    material->AddModelGeometricEntity(modelFace);
  }

  return modelFace;
}

vtkModelFaceUse* vtkXMLModelReader::ConstructModelFaceUse(int id)
{
  vtkModelFaceUse* faceUse = 0;
  std::vector<vtkIdType> associatedModelFace;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelFaceType, associatedModelFace);
  if (associatedModelFace.size() != 1)
  {
    vtkErrorMacro("ModelFaceUse has incorrect number of adjacent model faces.");
    return 0;
  }
  vtkModelFace* face = vtkModelFace::SafeDownCast(
    this->Model->GetModelEntity(vtkModelFaceType, associatedModelFace[0]));
  if (!face)
  {
    vtkErrorMacro("ModelFace is not yet available for needed ModelFaceUse.");
    return 0;
  }
  if (face->GetModelFaceUse(0)->GetUniquePersistentId() == id)
  {
    faceUse = face->GetModelFaceUse(0);
  }
  else if (face->GetModelFaceUse(1)->GetUniquePersistentId() == id)
  {
    faceUse = face->GetModelFaceUse(1);
  }
  else
  {
    vtkErrorMacro("ModelFaceUse associated with wrong model face.");
    return 0;
  }
  return faceUse;
}

vtkModelRegion* vtkXMLModelReader::ConstructModelRegion(int id)
{
  vtkModelRegion* region = 0;
  std::map<int, std::vector<vtkIdType> > associations;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), associations);
  if (associations[vtkModelMaterialType].size() != 1 ||
    associations[vtkModelShellUseType].size() != 1)
  {
    // Currently serialization only supports a single shell
    vtkErrorMacro("Model region has wrong associations.");
    return 0;
  }
  vtkModelMaterial* material = vtkModelMaterial::SafeDownCast(
    this->Model->GetModelEntity(vtkModelMaterialType, associations[vtkModelMaterialType][0]));

  // get shell use adjacencies
  std::ostringstream idstr;
  idstr << associations[vtkModelShellUseType][0] << ends;
  vtkXMLElement* shellElement = this->RootElement->FindNestedElement(idstr.str().c_str());
  std::vector<vtkIdType> shellFaceUses;
  this->GetAssociations(
    shellElement->FindNestedElementByName("Associations"), vtkModelFaceUseType, shellFaceUses);
  size_t numFaces = shellFaceUses.size();
  std::vector<vtkModelFace*> faces(numFaces);
  std::vector<int> faceSides(numFaces);
  for (size_t j = 0; j < numFaces; j++)
  {
    vtkModelFaceUse* faceUse =
      vtkModelFaceUse::SafeDownCast(this->Model->GetModelEntity(shellFaceUses[j]));
    faces[j] = faceUse->GetModelFace();
    faceSides[j] = (faces[j]->GetModelFaceUse(0) == faceUse) ? 0 : 1;
  }

  region = this->Model->BuildModelRegion(
    static_cast<int>(numFaces), &faces[0], &faceSides[0], id, material);
  vtkModelItemIterator* shellUses = region->NewModelShellUseIterator();
  int counter = 0;
  for (shellUses->Begin(); !shellUses->IsAtEnd(); shellUses->Next(), counter++)
  {
    vtkModelShellUse* shellUse = vtkModelShellUse::SafeDownCast(shellUses->GetCurrentItem());
    shellUse->SetUniquePersistentId(associations[vtkModelShellUseType][0]);
  }
  shellUses->Delete();

  std::vector<vtkIdType> floatingEdges;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), vtkModelEdgeType, floatingEdges);
  if (floatingEdges.size() != 0)
  {
    vtkModelItem* edge = this->Model->GetModelEntity(vtkModelEdgeType, floatingEdges[0]);
    region->AddAssociation(edge);
  }
  return region;
}

vtkModelVertex* vtkXMLModelReader::ConstructModelVertex(int id)
{
  vtkModelVertex* vertex = this->Model->BuildModelVertex(-2, id, false);

  // add in the vertex uses here so that we keep the order
  std::vector<vtkIdType> associatedVertexUses;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelVertexUseType, associatedVertexUses);
  for (std::vector<vtkIdType>::iterator it = associatedVertexUses.begin();
       it != associatedVertexUses.end(); it++)
  {
    vtkModelVertexUse* vertexUse = vertex->BuildModelVertexUse();
    vertexUse->SetUniquePersistentId(*it);
  }
  return vertex;
}

vtkModelVertexUse* vtkXMLModelReader::ConstructModelVertexUse(int id)
{
  std::vector<vtkIdType> associatedVertex;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelVertexType, associatedVertex);
  if (associatedVertex.size() != 1)
  {
    vtkErrorMacro("Could not find proper vertex for vertex use.");
    return 0;
  }
  vtkModelVertex* vertex = vtkModelVertex::SafeDownCast(
    this->Model->GetModelEntity(vtkModelVertexType, associatedVertex[0]));
  vtkModelVertexUse* vertexUse =
    vtkModelVertexUse::SafeDownCast(vertex->GetModelEntity(vtkModelVertexUseType, id));

  return vertexUse;
}

vtkModelEdge* vtkXMLModelReader::ConstructModelEdge(int id)
{
  std::map<int, std::vector<vtkIdType> > associations;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), associations);

  vtkDiscreteModelEdge* edge = vtkDiscreteModelEdge::New();
  edge->SetUniquePersistentId(id);
  this->Model->AddAssociation(edge);
  edge->Delete();
  // we add in the model edge uses now so that we make sure
  // to get them in in the proper order
  for (std::vector<vtkIdType>::iterator it = associations[vtkModelEdgeUseType].begin();
       it != associations[vtkModelEdgeUseType].end(); it++)
  { // iterate through the edge uses
    vtkModelEdgeUse* edgeUse = vtkModelEdgeUse::New();
    edgeUse->SetUniquePersistentId(*it);
    edge->AddAssociation(edgeUse);
    edgeUse->Delete();
  }

  return edge;
}

vtkModelEdgeUse* vtkXMLModelReader::ConstructModelEdgeUse(int id)
{
  std::map<int, std::vector<vtkIdType> > associations;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), associations);
  if (associations[vtkModelEdgeType].size() != 1)
  {
    vtkErrorMacro("Model edge use has wrong associations.");
    return 0;
  }

  vtkModelEntity* edge =
    this->Model->GetModelEntity(vtkModelEdgeType, associations[vtkModelEdgeType][0]);
  vtkModelItemIterator* edgeUses = edge->NewIterator(vtkModelEdgeUseType);
  vtkModelEdgeUse* edgeUse = 0;
  for (edgeUses->Begin(); !edgeUses->IsAtEnd() && edgeUse == 0; edgeUses->Next())
  {
    vtkModelEdgeUse* tmp = vtkModelEdgeUse::SafeDownCast(edgeUses->GetCurrentItem());
    if (tmp->GetUniquePersistentId() == id)
    {
      edgeUse = tmp;
    }
  }
  edgeUses->Delete();

  // hook up the edge use pair
  vtkIdType edgeUsePairId = associations[vtkModelEdgeUseType][0];
  if (edgeUse->GetPairedModelEdgeUse() == 0)
  {
    vtkModelEntity* edgeUsePair = edge->GetModelEntity(vtkModelEdgeUseType, edgeUsePairId);
    if (edgeUsePair)
    {
      edgeUse->AddAssociation(edgeUsePair);
    }
  }

  // now hook up the vertex uses to this edge use
  for (size_t i = 0; i < associations[vtkModelVertexUseType].size(); i++)
  {
    // get vertex use adjacencies
    std::ostringstream idstr;
    idstr << associations[vtkModelVertexUseType][i] << ends;
    vtkXMLElement* vertexUseElement = this->RootElement->FindNestedElement(idstr.str().c_str());
    std::vector<vtkIdType> vertexId;
    this->GetAssociations(
      vertexUseElement->FindNestedElementByName("Associations"), vtkModelVertexType, vertexId);
    vtkModelEntity* vertex = this->Model->GetModelEntity(vtkModelVertexType, vertexId[0]);
    vtkModelVertexUse* vertexUse = vtkModelVertexUse::SafeDownCast(
      vertex->GetModelEntity(vtkModelVertexUseType, associations[vtkModelVertexUseType][i]));
    if (vertexUse == 0)
    {
      vtkErrorMacro("Could not find vertex use for edge use.");
      return 0;
    }
    edgeUse->AddAssociation(vertexUse);
  }

  return edgeUse;
}

vtkModelShellUse* vtkXMLModelReader::ConstructModelShellUse(int id)
{
  std::vector<vtkIdType> associatedRegion;
  this->GetAssociations(this->CurrentElement->FindNestedElementByName("Associations"),
    vtkModelRegionType, associatedRegion);
  if (associatedRegion.size() != 1)
  {
    vtkErrorMacro("ModelRegion is not yet available for needed ModelShellUse.");
    return 0;
  }
  vtkModelShellUse* shellUse = 0;
  vtkModelItemIterator* iter =
    vtkDiscreteModelRegion::SafeDownCast(
      this->Model->GetModelEntity(vtkModelRegionType, associatedRegion[0]))
      ->NewModelShellUseIterator();
  for (iter->Begin(); !iter->IsAtEnd() && !shellUse; iter->Next())
  {
    if (id == vtkModelShellUse::SafeDownCast(iter->GetCurrentItem())->GetUniquePersistentId())
    {
      shellUse = vtkModelShellUse::SafeDownCast(iter->GetCurrentItem());
    }
  }
  iter->Delete();
  if (!shellUse)
  {
    vtkErrorMacro("Model shell use cannot find the proper region.");
    return 0;
  }
  return shellUse;
}

vtkDiscreteModelEntityGroup* vtkXMLModelReader::ConstructModelEntityGroup(int id)
{
  std::map<int, std::vector<vtkIdType> > associations;
  this->GetAssociations(
    this->CurrentElement->FindNestedElementByName("Associations"), associations);
  // the entity group should only contain one type of entity
  // so get rid of the association to the model to get it
  associations.erase(this->Model->GetType());
  if (associations.size() == 1)
  {
    std::map<int, std::vector<vtkIdType> >::iterator it = associations.begin();
    int type = it->first;
    int numberOfEntities = static_cast<int>(it->second.size());
    std::vector<vtkDiscreteModelEntity*> entities(numberOfEntities);
    for (int j = 0; j < numberOfEntities; j++)
    {
      vtkModelEntity* ent = this->Model->GetModelEntity(type, it->second[j]);
      switch (type)
      {
        case vtkModelEdgeType:
        {
          entities[j] = vtkDiscreteModelEdge::SafeDownCast(ent);
          break;
        }
        case vtkModelFaceType:
        {
          entities[j] = vtkDiscreteModelFace::SafeDownCast(ent);
          break;
        }
        case vtkModelRegionType:
        {
          entities[j] = vtkDiscreteModelRegion::SafeDownCast(ent);
          break;
        }
        default:
        {
          vtkErrorMacro("Wrong type of object in model entity group.");
          return 0;
        }
      }
    }
    return this->Model->BuildModelEntityGroup(
      type, numberOfEntities, numberOfEntities ? (&entities[0]) : NULL, id);
  }
  else if (associations.size() == 0)
  {
    return this->Model->BuildModelEntityGroup(-1, 0, NULL, id);
  }

  vtkErrorMacro("Model entity group contains more than one type.");
  return 0;
}

void vtkXMLModelReader::Serialize(const char* name, vtkInformation* info)
{
  info->Clear();
  vtkXMLElement* elem = this->CurrentElement->FindNestedElementByName(name);
  if (!elem)
  {
    return;
  }

  unsigned int nnested = elem->GetNumberOfNestedElements();
  for (unsigned int i = 0; i < nnested; i++)
  {
    vtkXMLElement* keyElem = elem->GetNestedElement(i);
    vtkInformationKey* key = vtkInformationKeyMap::FindKey(keyElem->GetName());
    if (!key)
    {
      continue;
    }

    if (key->IsA("vtkInformationIntegerKey"))
    {
      SerializeScalarKey<vtkInformationIntegerKey, int>(
        info, static_cast<vtkInformationIntegerKey*>(key), keyElem);
    }
    else if (key->IsA("vtkInformationDoubleKey"))
    {
      SerializeScalarKey<vtkInformationDoubleKey, double>(
        info, static_cast<vtkInformationDoubleKey*>(key), keyElem);
    }
    else if (key->IsA("vtkInformationIdTypeKey"))
    {
      SerializeScalarKey<vtkInformationIdTypeKey, vtkIdType>(
        info, static_cast<vtkInformationIdTypeKey*>(key), keyElem);
    }
    else if (key->IsA("vtkInformationStringKey"))
    {
      const char* val = keyElem->GetAttribute("value");
      if (val)
      {
        info->Set(static_cast<vtkInformationStringKey*>(key), val);
      }
    }
    else if (key->IsA("vtkInformationDoubleVectorKey"))
    {
      SerializeVectorKey<vtkInformationDoubleVectorKey, double>(
        info, static_cast<vtkInformationDoubleVectorKey*>(key), keyElem);
    }
    else if (key->IsA("vtkInformationIntegerVectorKey"))
    {
      SerializeVectorKey<vtkInformationIntegerVectorKey, int>(
        info, static_cast<vtkInformationIntegerVectorKey*>(key), keyElem);
    }
    else if (key->IsA("vtkInformationObjectBaseKey"))
    {
      vtkIdType id;
      if (keyElem->GetScalarAttribute("to_id", &id))
      {
        vtkObject* obj = this->ReadObject(id, false);
        info->Set(static_cast<vtkInformationObjectBaseKey*>(key), obj);
        // ReadObject incremented the ReferenceCount (weakPtr = false), or
        // created the object (ReferenceCount = 1); we then stuff it in the
        // information object , which is where we were trying to get it but
        // has the side effect of further incrementing the ReferenceCount.
        // To prevent a leak we need to decrment the ReferenceCount.
        obj->UnRegister(0);
      }
    }
  }
}

void vtkXMLModelReader::Serialize(const char* name, vtkObject*& obj, bool weakPtr /*=false*/)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  obj = 0;
  vtkIdType id;
  if (elem->GetScalarAttribute("to_id", &id))
  {
    obj = this->ReadObject(id, weakPtr);
  }
}

// -------------integers---------------
void vtkXMLModelReader::Serialize(const char* name, int& val)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  elem->GetScalarAttribute("value", &val);
}

void vtkXMLModelReader::Serialize(const char* name, int*& val, unsigned int& length)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }
  if (val)
  {
    delete[] val;
    val = 0;
  }
  length = 0;
  elem->GetScalarAttribute("length", &length);
  if (length > 0)
  {
    val = new int[length];
    elem->GetVectorAttribute("values", length, val);
  }
}

// -------------unsigned longs---------------
void vtkXMLModelReader::Serialize(const char* name, unsigned long& val)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  elem->GetScalarAttribute("value", &val);
}

void vtkXMLModelReader::Serialize(const char* name, unsigned long*& val, unsigned int& length)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }
  if (val)
  {
    delete[] val;
    val = 0;
  }
  length = 0;
  elem->GetScalarAttribute("length", &length);
  if (length > 0)
  {
    val = new unsigned long[length];
    elem->GetVectorAttribute("values", length, val);
  }
}

// -------------vtkIdTypes---------------
#if defined(VTK_USE_64BIT_IDS)
void vtkXMLModelReader::Serialize(const char* name, vtkIdType& val)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  elem->GetScalarAttribute("value", &val);
}

void vtkXMLModelReader::Serialize(const char* name, vtkIdType*& val, unsigned int& length)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }
  if (val)
  {
    delete[] val;
    val = 0;
  }
  length = 0;
  elem->GetScalarAttribute("length", &length);
  if (length > 0)
  {
    val = new vtkIdType[length];
    elem->GetVectorAttribute("values", length, val);
  }
}
#endif // if defined(VTK_USE_64BIT_IDS)

// -------------doubles---------------
void vtkXMLModelReader::Serialize(const char* name, double& val)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  elem->GetScalarAttribute("value", &val);
}

void vtkXMLModelReader::Serialize(const char* name, double*& val, unsigned int& length)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }
  if (val)
  {
    delete[] val;
    val = 0;
  }
  length = 0;
  elem->GetScalarAttribute("length", &length);
  if (length > 0)
  {
    val = new double[length];
    elem->GetVectorAttribute("values", length, val);
  }
}

void vtkXMLModelReader::Serialize(const char* name, char*& str)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  str = 0;
  const char* val = elem->GetAttribute("value");
  if (val)
  {
    str = vtksys::SystemTools::DuplicateString(val);
  }
}

void vtkXMLModelReader::Serialize(const char* name, std::string& str)
{
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  const char* val = elem->GetAttribute("value");
  if (val)
  {
    str = val;
  }
  else
  {
    str = std::string();
  }
}

void vtkXMLModelReader::Serialize(
  const char* name, std::vector<vtkSmartPointer<vtkObject> >& objs, bool /*weakPtr*/)
{
  objs.clear();
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  unsigned int nnested = elem->GetNumberOfNestedElements();
  for (unsigned int i = 0; i < nnested; i++)
  {
    vtkXMLElement* objElem = elem->GetNestedElement(i);
    vtkIdType to_id;
    if (objElem->GetScalarAttribute("to_id", &to_id))
    {
      vtkSerializableObject* obj = this->Model->GetModelEntity(to_id);
      if (obj)
      {
        objs.push_back(obj);
        // ReadObject incremented the ReferenceCount (weakPtr = false), or
        // created the object (ReferenceCount = 1); we then stuff it in the
        // vector , which is where we were trying to get it but has the
        // side effect of further incrementing the ReferenceCount.  To prevent
        // a leak we need to decrment the ReferenceCount.
        //obj->UnRegister(0);
      }
    }
  }
}

void vtkXMLModelReader::Serialize(
  const char* name, std::map<int, std::vector<vtkSmartPointer<vtkObject> > >& objs)
{
  objs.clear();
  vtkXMLElement* elem =
    this->CurrentElement->FindNestedElementByName(name); //BaseSerialize(this->Internal, name);
  if (!elem)
  {
    return;
  }

  unsigned int nnested = elem->GetNumberOfNestedElements();
  for (unsigned int i = 0; i < nnested; i++)
  {
    vtkXMLElement* vecElem = elem->GetNestedElement(i);
    // Take out Key_ and convert to int
    int key = atoi(vecElem->GetName() + 4);
    this->Serialize(vecElem->GetName(), objs[key]);
  }
}

void vtkXMLModelReader::GetAssociations(
  vtkXMLElement* elem, std::map<int, std::vector<vtkIdType> >& objs)
{
  // this should be an Associations elemement
  objs.clear();
  unsigned int nnested = elem->GetNumberOfNestedElements();
  for (unsigned int i = 0; i < nnested; i++)
  {
    vtkXMLElement* vecElem = elem->GetNestedElement(i);
    // Take out Key_ and convert to int
    int key = atoi(vecElem->GetName() + 4);
    this->GetAssociations(elem, key, objs[key]);
  }
}

void vtkXMLModelReader::GetAssociations(
  vtkXMLElement* elem, int entityType, std::vector<vtkIdType>& objs)
{
  // this should be an Associations elemement
  objs.clear();
  unsigned int nnested = elem->GetNumberOfNestedElements();
  for (unsigned int i = 0; i < nnested; i++)
  {
    vtkXMLElement* vecElem = elem->GetNestedElement(i);
    // Take out Key_ and convert to int
    int key = atoi(vecElem->GetName() + 4);
    if (key == entityType)
    {
      unsigned int numObjects = vecElem->GetNumberOfNestedElements();
      for (unsigned int j = 0; j < numObjects; j++)
      {
        vtkXMLElement* objectElem = vecElem->GetNestedElement(j);
        vtkIdType to_id;
        if (objectElem->GetScalarAttribute("to_id", &to_id))
        {
          objs.push_back(to_id);
        }
      }
    }
  }
}

vtkObject* vtkXMLModelReader::ReadObject(vtkIdType /*id*/, bool /*weakPtr*/)
{
  return 0;
}

int vtkXMLModelReader::ParseStream(istream& str)
{
  vtkSmartPointer<vtkModelXMLParser> parser = vtkSmartPointer<vtkModelXMLParser>::New();
  parser->SetStream(&str);
  parser->Parse();
  this->SetRootElement(parser->GetRootElement());
  return 1;
}

void vtkXMLModelReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "RootElement: " << this->RootElement << "\n";
  os << indent << "CurrentElement: " << this->CurrentElement << "\n";
  os << indent << "Model: " << this->Model << "\n";
}
