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

   Program: ConceptualModelBuilder
   Module:    pqCMBSceneV2Writer.cxx

   Copyright (c) Kitware Inc.
   All rights reserved.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/
#include "pqCMBSceneV2Writer.h"
#include "pqCMBSceneTree.h"
#include "pqCMBSceneNode.h"
#include "pqCMBSceneNodeIterator.h"
#include "pqCMBConicalRegion.h"
#include "pqCMBFacetedObject.h"
#include "pqCMBGlyphObject.h"
#include "pqCMBUniformGrid.h"
#include "pqCMBLine.h"
#include "pqCMBPlane.h"
#include "pqCMBPoints.h"
#include "pqCMBArc.h"
#include "pqCMBPolygon.h"
#include "pqCMBVOI.h"
#include "pqCMBSolidMesh.h"
#include "pqApplicationCore.h"
#include "pqObjectBuilder.h"
#include "pqOutputPort.h"
#include "pqPipelineSource.h"
#include "pqSMAdaptor.h"
#include "pqServer.h"
#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkSMSourceProxy.h"
#include "vtkXMLDataElement.h"
#include "vtkXMLUtilities.h"
#include "vtkStringWriter.h"
#include "vtkUUID.h"
#include <vtksys/SystemTools.hxx>
#include <sstream>

#include <QList>

#include "vtkNew.h"
#include "vtkSMStringVectorProperty.h"
#include "vtkPVCMBSceneV2WriterInformation.h"
#include "vtkStringList.h"
#include "vtkProcessModule.h"

//----------------------------------------------------------------------------
pqCMBSceneV2Writer::pqCMBSceneV2Writer()
{
  this->Tree = NULL;
  this->WriterSource = NULL;
}

//----------------------------------------------------------------------------
pqCMBSceneV2Writer::~pqCMBSceneV2Writer()
{
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Writer::write(bool packageScene)
{
  // generate the server writer
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();
  this->WriterSource = builder->createSource("sources", "CmbSceneV2WriterHelper",
    core->getActiveServer());
  vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast(this->WriterSource->getProxy());

  //generate the contour inputs collection
  this->ContourInputs = new QList<pqOutputPort*>;

  //push the file name to the server
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("FileName"), this->FileName.c_str());
  proxy->UpdateProperty("FileName");

  this->UserDefinedObjectTypes.clear();
  vtkXMLDataElement *scene = vtkXMLDataElement::New();
  scene->SetName("Scene");
  scene->SetAttribute("Version", "2.0");
  vtkXMLDataElement *part;
  part = vtkXMLDataElement::New();
  part->SetName("Units");
  part->SetAttribute("Value",
                     cmbSceneUnits::convertToString(this->Tree->getUnits()).c_str());
  scene->AddNestedElement(part);
  part->Delete();
  part= this->getTypes();
  scene->AddNestedElement(part);
  part->Delete();

  part= this->getDirectory(packageScene);
  scene->AddNestedElement(part);
  part->Delete();

  part= this->getTextureDirectory(packageScene);
  scene->AddNestedElement(part);
  part->Delete();

  part = this->getObjects();
  scene->AddNestedElement(part);
  part->Delete();

  //note: this also causes the server to write the contour file
  if (this->ContourInputs->size() > 0)
    {
    part = this->generateContourFile( packageScene );
    scene->AddNestedElement(part);
    part->Delete();
    }
  part = this->getUserDefinedObjectTypes();
  scene->AddNestedElement(part);
  part->Delete();
  vtkIndent indent;

  //now that the xml is generated we are going to push it to the server as a
  //single string
  std::stringstream buffer;
  vtkXMLUtilities::FlattenElement(scene, buffer, &indent);

  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("Text"), buffer.str().c_str());
  //set the mode to be writing of the xml file
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("Mode"), 2);

  proxy->UpdateVTKObjects();
  proxy->UpdatePipeline();

  builder->destroy( this->WriterSource );

  scene->Delete();


  //generate the contour inputs collection
  delete this->ContourInputs;
  this->ContourInputs = NULL;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getUserDefinedObjectTypes()
{
  vtkXMLDataElement *typeSection = vtkXMLDataElement::New();
  typeSection->SetName("UserDefinedTypeDefinitions");
  std::map<std::string, int>::iterator it;
  vtkXMLDataElement *te;
  for (it = this->UserDefinedObjectTypes.begin();
       it != this->UserDefinedObjectTypes.end(); ++it)
    {
    te = vtkXMLDataElement::New();
    te->SetName("UserType");
    te->SetAttribute("Name", (*it).first.c_str());
    typeSection->AddNestedElement(te);
    te->Delete();
    }
  return typeSection;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getTypes()
{
  vtkXMLDataElement *typeSection = vtkXMLDataElement::New();
  typeSection->SetName("TypeDefinitions");
  pqCMBSceneNodeIterator iter(this->Tree->getRoot());
  pqCMBSceneNode *n;
  vtkXMLDataElement *te, *ce;
  double color[4];

  while((n = iter.next()))
    {
    if (!n->isTypeNode())
      {
      continue;
      }
    te = vtkXMLDataElement::New();
    te->SetName("Type");
    te->SetAttribute("Name", n->getName());

    if (n->hasExplicitColor())
      {
      ce = vtkXMLDataElement::New();
      ce->SetName("Color");
      n->getColor(color);
      ce->SetVectorAttribute("Value", 4, color);
      te->AddNestedElement(ce);
      ce->Delete();
      }

    if (!n->isVisible())
      {
      vtkXMLDataElement *ve = vtkXMLDataElement::New();
      ve->SetName("Invisible");
      te->AddNestedElement(ve);
      ve->Delete();
      }

    if (n->isLocked())
      {
      vtkXMLDataElement *ve = vtkXMLDataElement::New();
      ve->SetName("Locked");
      te->AddNestedElement(ve);
      ve->Delete();
      }

    if (n->getParent())
      {
      te->SetAttribute("Parent", n->getParent()->getName());
      }
    typeSection->AddNestedElement(te);
    te->Delete();
    }
  return typeSection;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getDirectory(bool packageScene)
{
  //the goal is to push to the server a list of files that need to be checked
  //and potentially copied. After this process is over we will fetch
  //from the server the updated list of files and add it to the xml tree
  vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast(this->WriterSource->getProxy());

  //set if we are packaging the scene
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("PackageScene"), packageScene);
  proxy->UpdateProperty("PackageScene");

  //set the mode to be directory parsing
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("Mode"), 1);
  proxy->UpdateProperty("Mode");

  pqCMBSceneNodeIterator iter(this->Tree->getRoot());
  pqCMBSceneNode *n;
  int nextEntry = 0;
  vtkStringList *absFiles = vtkStringList::New();
  std::string fname;
  while((n = iter.next()))
    {
    if (!n->getDataObject())
      {
      continue;
      }
    // is this a file based object (Points or FacetedObject), and image or glyph object?
    fname = "";

    // Is this a points object?
    pqCMBPoints *pobj = dynamic_cast<pqCMBPoints*>(n->getDataObject());
    if (pobj != NULL)
      {
      this->updateDirectoryInfo(pobj->getFileName(), absFiles, nextEntry);
      continue;
      }

    // Is this a faceted Object?
    pqCMBFacetedObject *fobj =
      dynamic_cast<pqCMBFacetedObject*>(n->getDataObject());

    if (fobj != NULL)
      {
      this->updateDirectoryInfo(fobj->getFileName(), absFiles, nextEntry);
      continue;
      }

    // Is this a solid mesh?
    pqCMBSolidMesh *solobj =
      dynamic_cast<pqCMBSolidMesh*>(n->getDataObject());

    if (solobj != NULL)
      {
      this->updateDirectoryInfo(solobj->getFileName(), absFiles, nextEntry);
      continue;
      }

    // Is this an Uniform Grid Object?
    pqCMBUniformGrid *uobj =
      dynamic_cast<pqCMBUniformGrid*>(n->getDataObject());

    if (uobj != NULL)
      {
      this->updateDirectoryInfo(uobj->getFileName(), absFiles, nextEntry);
      continue;
      }

    // Is this a Glyph Object?
    pqCMBGlyphObject *gobj =
      dynamic_cast<pqCMBGlyphObject*>(n->getDataObject());
    if (gobj != NULL)
      {
      this->updateDirectoryInfo(gobj->getGlyphFileName(), absFiles, nextEntry);
      // Do we have to create a file for the point infomation?
      fname = gobj->getPointsFileName();
      if (fname == "")
        {
        unsigned char uuid[16];
        if (vtkUUID::GenerateUUID(uuid) == -1)
          {
          // We need to use the less unique Id
          vtkUUID::ConstructUUID(uuid);
          }
        // Get the path that the Scene is being written to
        fname = vtksys::SystemTools::GetFilenamePath(this->FileName);
        //now add on the glyph component
        fname += "/GlyphPoints_";
        std::string uname;
        vtkUUID::ConvertBinaryUUIDToString(uuid, uname);
        fname += uname;
        fname+=".vtk";
        gobj->setPointsFileName(fname.c_str());
        }
      this->updateDirectoryInfo(fname, absFiles, nextEntry);
      continue;
      }

    pqCMBPolygon *pgObject =
      dynamic_cast<pqCMBPolygon*>(n->getDataObject());
    if(pgObject != NULL)
      {
      unsigned char uuid[16];
      if (vtkUUID::GenerateUUID(uuid) == -1)
        {
        // We need to use the less unique Id
        vtkUUID::ConstructUUID(uuid);
        }
      // Get the path that the Scene is being written to
      fname = vtksys::SystemTools::GetFilenamePath(this->FileName);
      //now add on the polygon component
      fname += "/PolygonMesh_";
      std::string uname;
      vtkUUID::ConvertBinaryUUIDToString(uuid, uname);
      fname += uname;
      fname+=".vtp";
      pgObject->setFileName(fname);
      this->updateDirectoryInfo(fname, absFiles, nextEntry);
      continue;
      }
    }

  vtkSMStringVectorProperty* dirProperty =
    vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("AddObjectFileName"));
  dirProperty->SetElements( absFiles );

  proxy->UpdateProperty("AddObjectFileName");
  proxy->UpdatePipeline();

  //get the updated paths from the server
  proxy->UpdatePropertyInformation(dirProperty);
  vtkNew<vtkPVCMBSceneV2WriterInformation> info;

  proxy->GatherInformation(info.GetPointer());

  vtkXMLDataElement *dirSection = vtkXMLDataElement::New();
  dirSection->SetName("ObjectFileDirectory");
  vtkXMLDataElement *fe;
  for ( int i=0; i < absFiles->GetNumberOfStrings(); ++i)
    {
    fe = vtkXMLDataElement::New();
    fe->SetName("File");
    fe->SetAttribute("Name", info->GetObjectFileName(i) );
    dirSection->AddNestedElement(fe);
    fe->Delete();
    }

  absFiles->Delete();
  return dirSection;
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Writer::updateDirectoryInfo(const std::string &fname,
                                           vtkStringList *absFiles, int &nextEntry)
{
  if (fname == "")
    {
    return;
    }

  if ( this->DirectoryMap.find(fname) == this->DirectoryMap.end() )
    {
    //only pass to the server unique strings
    //we need a directory mapping for lookup
    this->DirectoryMap[fname] = nextEntry++;
    absFiles->AddString( fname.c_str() );
    }
}
//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getTextureDirectory(bool packageScene)
{

  //the goal is to push to the server a list of files that need to be checked
  //and potentially copied. After this process is over we will fetch
  //from the server the updated list of files and add it to the xml tree
  vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast(this->WriterSource->getProxy());

  //set if we are packaging the scene
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("PackageScene"), packageScene);
  proxy->UpdateProperty("PackageScene");

  //set the mode to be directory parsing
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("Mode"), 1);
  proxy->UpdateProperty("Mode");

  pqCMBSceneNodeIterator iter(this->Tree->getRoot());
  pqCMBSceneNode *n;

  int nextEntry = 0;
  vtkStringList *absFiles = vtkStringList::New();
  pqCMBTexturedObject *tobj;
  while((n = iter.next()))
    {
    if (!n->getDataObject())
      {
      continue;
      }
    tobj = dynamic_cast<pqCMBTexturedObject*>(n->getDataObject());
    // Skip objects w/o textures
    if ((tobj == NULL) || (!tobj->hasTexture()))
      {
      continue;
      }
    std::string fname = tobj->getTextureFileName().toStdString();
    if ( this->TextureDirectoryMap.find(fname) == this->TextureDirectoryMap.end() )
      {
      //only pass to the server unique strings
      //we need a directory mapping for lookup
      this->TextureDirectoryMap[fname] = nextEntry++;
      absFiles->AddString( fname.c_str() );
      }
    }

  vtkSMStringVectorProperty* dirProperty =
    vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("AddObjectFileName"));
  dirProperty->SetElements( absFiles );

  proxy->UpdateProperty("AddObjectFileName");
  proxy->UpdatePipeline();

  //get the updated paths from the server
  proxy->UpdatePropertyInformation(dirProperty);
  vtkNew<vtkPVCMBSceneV2WriterInformation> info;

  proxy->GatherInformation(info.GetPointer());

  vtkXMLDataElement *dirSection = vtkXMLDataElement::New();
  dirSection->SetName("TextureFileDirectory");
  vtkXMLDataElement *fe;
  for ( int i=0; i < absFiles->GetNumberOfStrings(); ++i)
    {
    fe = vtkXMLDataElement::New();
    fe->SetName("File");

    QString relativePath = pqSMAdaptor::getMultipleElementProperty(
      dirProperty, i ).toString();
    fe->SetAttribute("Name", info->GetObjectFileName(i));
    dirSection->AddNestedElement(fe);
    fe->Delete();
    }

  absFiles->Delete();
  return dirSection;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getObjects()
{
  vtkXMLDataElement *objSection = vtkXMLDataElement::New();
  vtkXMLDataElement *oe;
  objSection->SetName("ObjectDefinitions");
  SceneObjectNodeIterator iter(this->Tree->getRoot());
  pqCMBSceneNode *n;
  while((n = iter.next()))
    {
    oe = getObjectDescription(n);
    objSection->AddNestedElement(oe);
    oe->Delete();
    }
  return objSection;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::getObjectDescription(pqCMBSceneNode *node)
{
  pqCMBSceneObjectBase *obj = node->getDataObject();
  if (!obj)
    {
    return NULL;
    }

  vtkXMLDataElement *elem = vtkXMLDataElement::New();
  elem->SetName("Object");
  elem->SetAttribute("Name", node->getName());
  elem->SetAttribute("NodeType", node->getParent()->getName());
  elem->SetAttribute("ObjectType", obj->getTypeAsString().c_str());
  std::string uname = obj->getUserDefinedType();
  elem->SetAttribute("UserDefinedType", uname.c_str());
  if (this->UserDefinedObjectTypes.find(uname) ==
      this->UserDefinedObjectTypes.end())
    {
    this->UserDefinedObjectTypes[uname] = 1;
    }

  if (dynamic_cast<pqCMBVOI *>(obj) != NULL)
    {
    this->processVOI(node, elem);
    }
  else if (dynamic_cast<pqCMBPoints *>(obj) != NULL)
    {
    this->processPoints(node, elem);
    }
  else if (dynamic_cast<pqCMBLine *>(obj) != NULL)
    {
    this->processLine(node, elem);
    }
  else if (dynamic_cast<pqCMBPlane *>(obj) != NULL)
    {
    this->processPlane(node, elem);
    }
  else if (dynamic_cast<pqCMBConicalRegion *>(obj) != NULL)
    {
    this->processCone(node, elem);
    }
  else if (dynamic_cast<pqCMBFacetedObject *>(obj) != NULL)
    {
    this->processFacetedObject(node, elem);
    }
  else if (dynamic_cast<pqCMBGlyphObject *>(obj) != NULL)
    {
    this->processGlyphObject(node, elem);
    }
  else if (dynamic_cast<pqCMBUniformGrid *>(obj) != NULL)
    {
    this->processUniformGrid(node, elem);
    }
  else if (dynamic_cast<pqCMBArc *>(obj) != NULL)
    {
    this->processContour(node, elem);
    }
  else if (dynamic_cast<pqCMBPolygon *>(obj) != NULL)
    {
    this->processPolygon(node, elem);
    }
  else if (dynamic_cast<pqCMBSolidMesh *>(obj) != NULL)
    {
    this->processSolidMesh(node, elem);
    }

  return elem;
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processVOI(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBVOI *obj =  dynamic_cast<pqCMBVOI *>(node->getDataObject());
  elem->SetAttribute("VOIType", "Bounds");

  double bounds[6];
  vtkXMLDataElement *be = vtkXMLDataElement::New();
  be->SetName("Bounds");
  obj->getDataBounds(bounds);
  be->SetVectorAttribute("Value", 6, bounds);
  elem->AddNestedElement(be);
  be->Delete();
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processLine(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBLine *obj = dynamic_cast<pqCMBLine *>(node->getDataObject());
  elem->SetAttribute("LineType", "StraightLine");

  double pos[3];
  vtkXMLDataElement *be1 = vtkXMLDataElement::New();
  be1->SetName("Point1WorldPosition");
  (static_cast<pqCMBLine*>(obj))->getPoint1Position(pos[0],pos[1], pos[2]);
  be1->SetVectorAttribute("Value", 3, pos);
  elem->AddNestedElement(be1);
  be1->Delete();
  vtkXMLDataElement *be2 = vtkXMLDataElement::New();
  be2->SetName("Point2WorldPosition");
  (static_cast<pqCMBLine*>(obj))->getPoint2Position(pos[0],pos[1], pos[2]);
  be2->SetVectorAttribute("Value", 3, pos);
  elem->AddNestedElement(be2);
  be2->Delete();
  this->processNonTransformProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processContour(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBArc *obj = dynamic_cast<pqCMBArc *>(node->getDataObject());
  if (!obj)
    {
    return;
    }
  pqOutputPort *port = obj->getSource()->getOutputPort(0);
  if ( !port )
    {
    return;
    }
  //add this contour to the collection of filters as input for the contour writer
  this->ContourInputs->push_back( port );

  //write out the xml info for this contour
  vtkSmartPointer<vtkXMLDataElement> normalElement =
    vtkSmartPointer<vtkXMLDataElement>::New();
  normalElement->SetName("PlaneProjectionNormal");
  normalElement->SetIntAttribute("Value",
    obj->getPlaneProjectionNormal());
  elem->AddNestedElement(normalElement);

  vtkSmartPointer<vtkXMLDataElement> positionElement =
    vtkSmartPointer<vtkXMLDataElement>::New();
  positionElement->SetName("PlanePositionOffset");
  positionElement->SetDoubleAttribute("Value",
    obj->getPlaneProjectionPosition());
  elem->AddNestedElement(positionElement);

  vtkSmartPointer<vtkXMLDataElement> closedElement =
    vtkSmartPointer<vtkXMLDataElement>::New();
  closedElement->SetName("ClosedLoop");
  closedElement->SetIntAttribute("Value",
    obj->getClosedLoop());
  elem->AddNestedElement(closedElement);

  this->processNonTransformProperties(node, elem);
  }


//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processPlane(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBPlane *obj = dynamic_cast<pqCMBPlane *>(node->getDataObject());
  double p1[3], p2[3];
  vtkXMLDataElement *be;
  obj->getPlaneInfo(p1, p2);
  be = vtkXMLDataElement::New();
  be->SetName("Point1");
  be->SetVectorAttribute("Value", 3, p1);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  be->SetName("Point2");
  be->SetVectorAttribute("Value", 3, p2);
  elem->AddNestedElement(be);
  be->Delete();
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processCone(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBConicalRegion *obj =
    dynamic_cast<pqCMBConicalRegion *>(node->getDataObject());
  double p1[3], dir[3], a;
  int b;
  vtkXMLDataElement *be;
  obj->getBaseCenter(p1);
  be = vtkXMLDataElement::New();
  be->SetName("BaseCenter");
  be->SetVectorAttribute("Value", 3, p1);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  obj->getDirection(dir);
  be->SetName("Direction");
  be->SetVectorAttribute("Value", 3, dir);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  a = obj->getHeight();
  be->SetName("Height");
  be->SetDoubleAttribute("Value", a);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  a = obj->getBaseRadius();
  be->SetName("BaseRadius");
  be->SetDoubleAttribute("Value", a);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  a = obj->getTopRadius();
  be->SetName("TopRadius");
  be->SetDoubleAttribute("Value", a);
  elem->AddNestedElement(be);
  be->Delete();
  be = vtkXMLDataElement::New();
  b = obj->getResolution();
  be->SetName("Resolution");
  be->SetIntAttribute("Value", b);
  elem->AddNestedElement(be);
  be->Delete();
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processFacetedObject(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBFacetedObject *obj =
    dynamic_cast<pqCMBFacetedObject*>(node->getDataObject());
  elem->SetAttribute("SurfaceType", obj->getSurfaceTypeAsString().c_str());
  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("FileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("FileEntry", (*iter).second);
    }
  this->processGeometricProperties(node, elem);
}
//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processSolidMesh(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBSolidMesh *obj =
    dynamic_cast<pqCMBSolidMesh*>(node->getDataObject());
  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("FileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("FileEntry", (*iter).second);
    }
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processUniformGrid(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBUniformGrid *obj =
    dynamic_cast<pqCMBUniformGrid*>(node->getDataObject());
  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("FileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("FileEntry", (*iter).second);
    }

  if (obj->isRawDEM())
    {
    vtkXMLDataElement *ue = vtkXMLDataElement::New();
    ue->SetName("RawDEMInfo");
    vtkIdType a[2], b[2];
    a[0] = (obj->getReadGroupOfFiles()) ? 1 : 0;
    ue->SetIntAttribute("ReadGroupOfFiles", a[0]);
    ue->SetIntAttribute("OnRatio", obj->getOnRatio());
    obj->getExtents(a, b);
    ue->SetVectorAttribute("RowExtents", 2, a);
    ue->SetVectorAttribute("ColumnExtents", 2, b);
    elem->AddNestedElement(ue);
    ue->Delete();
    }
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processPoints(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBPoints *obj =
    dynamic_cast<pqCMBPoints *>(node->getDataObject());
  if (obj->getReaderSource())
    {
    if (obj->getPieceId() == -1)
      {
      // old style, all pieces appended together.  Should only happen if read
      // in an old sg file
      vtkSMSourceProxy* sourceProxy = vtkSMSourceProxy::SafeDownCast(
        obj->getSource()->getProxy() );
      sourceProxy->UpdatePropertyInformation();
      int maxNumberOfPoints = pqSMAdaptor::getElementProperty(
        sourceProxy->GetProperty("GetMaxNumberOfPoints")).toInt();
      elem->SetIntAttribute("MaxNumberOfPoints", maxNumberOfPoints);
      }
    else
      {
      elem->SetIntAttribute("PieceId", obj->getPieceId());
      elem->SetIntAttribute("PieceOnRatio", obj->getPieceOnRatio());
      }
    elem->SetAttribute("DataPrecision", (obj->getDoubleDataPrecision() ?
                                         "double" : "float"));
    }

  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("FileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("FileEntry", (*iter).second);
    }
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processGlyphObject(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBGlyphObject *obj =
    dynamic_cast<pqCMBGlyphObject*>(node->getDataObject());
  elem->SetAttribute("SurfaceType", obj->getSurfaceTypeAsString().c_str());
  obj->writePointsFile();
  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getGlyphFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("GlyphFileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("GlyphFileEntry", (*iter).second);
    }

  iter = this->DirectoryMap.find(obj->getPointsFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("PointsFileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("PointsFileEntry", (*iter).second);
    }
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processPolygon(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBPolygon *obj =
    dynamic_cast<pqCMBPolygon*>(node->getDataObject());
  std::map<std::string, int>::iterator iter
    = this->DirectoryMap.find(obj->getFileName());
  if (iter == this->DirectoryMap.end())
    {
    elem->SetIntAttribute("PolygonFileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("PolygonFileEntry", (*iter).second);
    obj->writeToFile();
    }

  elem->SetDoubleAttribute("MinAngle", obj->getMinAngle());
  elem->SetDoubleAttribute("EdgeLength", obj->getEdgeLength());

  //get the list of arcs this node is mapped to.
  //these need to be saved so that we can restore the manager
  const std::set< pqCMBArc* >& arcs = obj->arcsUsedByPolygon();
  if ( arcs.size() > 0 )
    {
    vtkXMLDataElement *arcsDE = vtkXMLDataElement::New();
    arcsDE->SetName("Arcs");
    arcsDE->SetIntAttribute("Size",arcs.size());
    typedef std::set< pqCMBArc* >::const_iterator arc_it;
    for(arc_it i = arcs.begin(); i != arcs.end(); ++i)
      {
      vtkXMLDataElement *arcDE = vtkXMLDataElement::New();
      arcDE->SetName("Arc");
      arcDE->SetAttribute("Name",this->Tree->findNode(*i)->getName());
      arcsDE->AddNestedElement(arcDE);
      arcDE->Delete();
      }
    elem->AddNestedElement(arcsDE);
    arcsDE->Delete();
    }
  this->processGeometricProperties(node, elem);
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processNonTransformProperties(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  pqCMBSceneObjectBase *obj = node->getDataObject();
  vtkXMLDataElement *ue = vtkXMLDataElement::New();
  ue->SetName("Units");
  ue->SetAttribute("Value",
                   cmbSceneUnits::convertToString(obj->getUnits()).c_str());
  elem->AddNestedElement(ue);
  ue->Delete();

  vtkXMLDataElement *ce = vtkXMLDataElement::New();
  double color[4];
  ce->SetName("Color");
  obj->getColor(color);
  ce->SetVectorAttribute("Value", 4, color);
  // Is the color inherited?
  if (!node->hasExplicitColor())
    {
    vtkXMLDataElement *ie = vtkXMLDataElement::New();
    ie->SetName("Inherited");
    ce->AddNestedElement(ie);
    ie->Delete();
    }
  elem->AddNestedElement(ce);
  ce->Delete();

  if (!node->isVisible())
    {
    vtkXMLDataElement *ve = vtkXMLDataElement::New();
    ve->SetName("Invisible");
    elem->AddNestedElement(ve);
    ve->Delete();
    }

  if (node->isLocked())
    {
    vtkXMLDataElement *ve = vtkXMLDataElement::New();
    ve->SetName("Locked");
    elem->AddNestedElement(ve);
    ve->Delete();
    }

  if (obj->isSnapTarget())
    {
    vtkXMLDataElement *ve = vtkXMLDataElement::New();
    ve->SetName("SnapTarget");
    elem->AddNestedElement(ve);
    ve->Delete();
    }
}

//-----------------------------------------------------------------------------
void pqCMBSceneV2Writer::processGeometricProperties(pqCMBSceneNode *node, vtkXMLDataElement *elem)
{
  this->processNonTransformProperties(node, elem);

  pqCMBSceneObjectBase *obj = node->getDataObject();

  double data[3];
  vtkXMLDataElement *placement = vtkXMLDataElement::New();
  placement->SetName("Position");
  obj->getPosition(data);
  placement->SetVectorAttribute("Value", 3, data);
  elem->AddNestedElement(placement);
  placement->Delete();

  placement = vtkXMLDataElement::New();
  placement->SetName("Orientation");
  obj->getOrientation(data);
  placement->SetVectorAttribute("Value", 3, data);
  elem->AddNestedElement(placement);
  placement->Delete();

  placement = vtkXMLDataElement::New();
  placement->SetName("Scale");
  obj->getScale(data);
  placement->SetVectorAttribute("Value", 3, data);
  elem->AddNestedElement(placement);
  placement->Delete();

  placement = vtkXMLDataElement::New();
  placement->SetName("Origin");
  obj->getOrigin(data);
  placement->SetVectorAttribute("Value", 3, data);
  elem->AddNestedElement(placement);
  placement->Delete();

  vtkXMLDataElement *ce = this->addConstraints(obj);
  elem->AddNestedElement(ce);
  ce->Delete();

  pqCMBTexturedObject *tobj = dynamic_cast<pqCMBTexturedObject*>(obj);
  if (tobj != NULL)
    {
    if(tobj->hasTexture())
      {
      ce = this->addTextureInfo(tobj);
      elem->AddNestedElement(ce);
      ce->Delete();
      }
    // Save BathymetryInfo
    if(tobj->getBathymetrySource())
      {
      ce = this->addBathyMetryInfo(tobj);
      elem->AddNestedElement(ce);
      ce->Delete();
      }
    // Save Elevation
    if (tobj->showingElevation())
      {
      vtkXMLDataElement *ve = vtkXMLDataElement::New();
      ve->SetName("ShowElevation");
      elem->AddNestedElement(ve);
      ve->Delete();
      }
    }
}


//----------------------------------------------------------------------------
vtkXMLDataElement * pqCMBSceneV2Writer::addConstraints(pqCMBSceneObjectBase *obj)
{
  vtkXMLDataElement *elem = vtkXMLDataElement::New();
    elem->SetName("Constraints");

    if (obj->isXTransConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("XTransConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isYTransConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("YTransConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isZTransConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("ZTransConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isXRotationConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("XRotationConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isYRotationConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("YRotationConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isZRotationConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("ZRotationConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isIsotropicScalingConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("IsotropicScalingConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isXScalingConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("XScalingConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isYScalingConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("YScalingConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }

  if (obj->isZScalingConstrain())
    {
    vtkXMLDataElement *ce = vtkXMLDataElement::New();
    ce->SetName("ZScalingConstraint");
    elem->AddNestedElement(ce);
    ce->Delete();
    }
  return elem;
}
//-----------------------------------------------------------------------------
vtkXMLDataElement * pqCMBSceneV2Writer::addTextureInfo(pqCMBTexturedObject *obj)
{
  vtkXMLDataElement *elem = vtkXMLDataElement::New();
  elem->SetName("TextureInfo");

  std::map<std::string, int>::iterator iter
    = this->TextureDirectoryMap.find(
    obj->getTextureFileName().toStdString());
  if (iter == this->TextureDirectoryMap.end())
    {
    elem->SetIntAttribute("TextureFileEntry", -2);
    }
  else
    {
    elem->SetIntAttribute("TextureFileEntry", (*iter).second);
    }

  int n = obj->getNumberOfRegistrationPoints();
  int i, j;
  double data[12];
  for (i = 0, j= 0; i < n; i++, j+=4)
    {
    obj->getRegistrationPointPair(i, &(data[j]), &(data[j+2]));
    }

  elem->SetIntAttribute("NumberOfRegistrationPoints", n);
  elem->SetVectorAttribute("Points", 4*n, data);
  return elem;
}
//-----------------------------------------------------------------------------
vtkXMLDataElement* pqCMBSceneV2Writer::addBathyMetryInfo(
  pqCMBTexturedObject *obj)
{
  pqCMBSceneNode* bathynode = this->Tree->getSceneObjectNode(
    obj->getBathymetrySource());
  if(!bathynode)
    {
    return NULL;
    }
  vtkXMLDataElement *elem = vtkXMLDataElement::New();
  elem->SetName("BathymetryInfo");
  vtkXMLDataElement *bathyDE = vtkXMLDataElement::New();
  bathyDE->SetName("BathymetrySource");
  bathyDE->SetAttribute("Name",bathynode->getName());
  // NOTE: Other bathymetry parameters (low and high limits) are not saved currently.
  bathyDE->SetDoubleAttribute("ElevationRadius",obj->getBathymetryElevationRadious());
  elem->AddNestedElement(bathyDE);
  bathyDE->Delete();
  return elem;
}

//----------------------------------------------------------------------------
std::string pqCMBSceneV2Writer::getContourFileName(bool packageScene,
                                                    std::string absoluteFileName)
{
  //the scene file only uses relative paths, so we need to query the writer
  //to get the correct path for the contour file
  vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast(this->WriterSource->getProxy());

  //set if we are packaging the scene
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("PackageScene"), packageScene);
  proxy->UpdateProperty("PackageScene");

  //set the mode to be directory parsing
  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("Mode"), 1);
  proxy->UpdateProperty("Mode");

  //need to specify the contour file that this scene is linked too

  vtkStringList *absFiles = vtkStringList::New();
  absFiles->AddString( absoluteFileName.c_str() );

  vtkSMStringVectorProperty* dirProperty =
    vtkSMStringVectorProperty::SafeDownCast(proxy->GetProperty("AddObjectFileName"));
  dirProperty->SetElements( absFiles );

  proxy->UpdateProperty("AddObjectFileName");
  proxy->UpdatePipeline();

  //get the updated paths from the server
  proxy->UpdatePropertyInformation(dirProperty);
  vtkNew<vtkPVCMBSceneV2WriterInformation> info;

  proxy->GatherInformation(info.GetPointer());

  absFiles->Delete();
  std::string relativeFileName = info->GetObjectFileName(0);
  return relativeFileName;
}

//----------------------------------------------------------------------------
vtkXMLDataElement *pqCMBSceneV2Writer::generateContourFile( bool packageScene )
{
  //drop the .sg extension and add _contour.vtp
  std::string absFileName = this->FileName;
  size_t pos = absFileName.rfind(".");
  if ( pos != std::string::npos )
    {
    absFileName.resize(pos);
    }
  //now add on the contour tag
  absFileName += "_contour.vtp";

  std::string relativeFileName = this->getContourFileName(
    packageScene, absFileName);

  vtkXMLDataElement *objSection = vtkXMLDataElement::New();
  objSection->SetName("ContourDefinitions");
  objSection->SetAttribute("FileName", relativeFileName.c_str() );

  //add the inputs to the QMap
  QMap<QString, QList<pqOutputPort*> > namedInputs;
  namedInputs["Input"] = *this->ContourInputs;

  //now that we have collected all the contour info, write out the file
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();
  pqPipelineSource* writer = builder->createFilter("writers",
    "SceneGenV2ContourWriter", namedInputs, core->getActiveServer());

  vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast(writer->getProxy());

  pqSMAdaptor::setElementProperty(
    proxy->GetProperty("FileName"), absFileName.c_str());
  proxy->UpdateProperty("FileName");
  proxy->UpdatePipeline();

  //now that the file has been written, delete the writer
  builder->destroy(writer);

  return objSection;
}
