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

   Program: ConceptualModelBuilder
   Module:    pqCMBSceneV2Reader.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 "pqCMBSceneV2Reader.h"
#include "pqCMBSceneTree.h"
#include "pqCMBSceneNode.h"
#include "pqDistanceWidget.h"
#include "pqCMBArc.h"
#include "pqCMBConicalRegion.h"
#include "pqCMBFacetedObject.h"
#include "pqCMBGlyphObject.h"
#include "pqCMBLine.h"
#include "pqCMBPlane.h"
#include "pqCMBPoints.h"
#include "pqCMBPolygon.h"
#include "pqCMBUniformGrid.h"
#include "pqCMBVOI.h"
#include "pqCMBSolidMesh.h"
#include "qtCMBArcWidgetManager.h"
#include "vtkBoundingBox.h"
#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMSceneContourSourceProxy.h"
#include "vtkXMLDataElement.h"
#include "vtkXMLUtilities.h"
#include <vtksys/SystemTools.hxx>
#include <QFileInfo>
#include <QProgressDialog>
#include <QTreeWidget>
#include <sstream>

#include "pqObjectBuilder.h"
#include "pqPipelineSource.h"
#include "pqApplicationCore.h"
#include "pqSMAdaptor.h"

template <typename T>
void InitSceneGenCoutourNodesArray(
  vtkXMLDataElement* e, void* dstArray)
{
  const char *elem_cdata = e->GetCharacterData();
  std::stringstream pointData;
  pointData << elem_cdata;

  int dataLength = 0;
  int dataBufferSize = 64;

  T* dataBuffer = new T[dataBufferSize];
  T element;

  while(pointData >> element)
    {
    if(dataLength == dataBufferSize)
      {
      int newSize = dataBufferSize*2;
      T* newBuffer = new T[newSize];
      memcpy(newBuffer, dataBuffer, dataLength*sizeof(T));
      delete [] dataBuffer;
      dataBuffer = newBuffer;
      dataBufferSize = newSize;
      }
    dataBuffer[dataLength++] = element;
    }

  memcpy(dstArray, dataBuffer, dataLength*sizeof(T));
}

//----------------------------------------------------------------------------
pqCMBSceneV2Reader::pqCMBSceneV2Reader()
{
  this->Tree = NULL;
  this->Progress = NULL;
  this->UseBoundsConstraint = 0;
  for(int i=0; i<6; i++)
    {
    this->BoundsConstraint[i]=0.0;
    }
  this->FilterObjectByType = 0;
}

//----------------------------------------------------------------------------
pqCMBSceneV2Reader::~pqCMBSceneV2Reader()
{
  if (this->Progress)
    {
    delete this->Progress;
    }
}

//----------------------------------------------------------------------------
int pqCMBSceneV2Reader::process(vtkXMLDataElement *root)
{
  if (!this->Tree)
    {
    this->Status = "Scene Tree has not been set";
    return -1;
    }

  this->Tree->empty();
  this->Sources.clear();
  this->FileNames.clear();
  this->TextureFileNames.clear();
  while(!this->Contours.empty())
    {
    this->Contours.pop();
    }
  this->Status = "";

  vtkXMLDataElement *elem;
  if (strcmp(root->GetName(), "Scene"))
    {
    this->Status = "Root Element is not a Scene";
    return -1;
    }
  const char *cData;
  std::string sData;

  // Check Version
  cData = root->GetAttribute("Version");
  if ((!cData) || strcmp(cData, "2.0"))
    {
    this->Status = "This is not a 2.0 Version Scene Representation";
    return -1;
    }

  // Get Units
  elem = root->FindNestedElementWithName("Units");
  if (!elem)
    {
    this->Tree->setUnits(cmbSceneUnits::Unknown);
    }
  else
    {
    this->Tree->setUnits(cmbSceneUnits::convertFromString(
                           elem->GetAttribute("Value")));
    }

  // Lets get the number of things we need to process for the Progress Bar
  int num = 1; // Theres the Type Definitions and the Object File Directory to start with

  elem = root->FindNestedElementWithName("ObjectDefinitions");
  if (elem)
    {
    num += elem->GetNumberOfNestedElements();
    }

  this->Progress = new QProgressDialog("Reading V2 File", "Abort Load", 0, num,
                                       this->Tree->getWidget());
  this->Progress->setWindowTitle("Loading SceneGen");
  this->Progress->setWindowModality(Qt::WindowModal);
  this->Progress->setLabelText("Processing Type Definitions");
  this->Progress->setValue(0);
  elem = root->FindNestedElementWithName("TypeDefinitions");
  if (elem)
    {
    this->processTypes(elem);
    }
  if (this->Progress->wasCanceled())
    {
    this->abortSceneLoading();
    return -1;
    }
  this->Progress->setLabelText("Processing File Information");
  this->Progress->setValue(1);
  elem = root->FindNestedElementWithName("ObjectFileDirectory");
  if (elem)
    {
    this->processFiles(elem);
    }

  elem = root->FindNestedElementWithName("TextureFileDirectory");
  if (elem)
    {
    this->processTextureFiles(elem);
    }
  if (this->Progress->wasCanceled())
    {
    this->abortSceneLoading();
    return -1;
    }

  elem = root->FindNestedElementWithName("UserDefinedTypeDefinitions");
  if (elem)
    {
    this->processUserDefinedObjectTypes(elem);
    }
  if (this->Progress->wasCanceled())
    {
    this->abortSceneLoading();
    return -1;
    }

  elem = root->FindNestedElementWithName("ContourDefinitions");
  if ( elem )
    {
    this->processContourFile(elem);
    }
  if (this->Progress->wasCanceled())
    {
    this->abortSceneLoading();
    return -1;
    }

  elem = root->FindNestedElementWithName("ObjectDefinitions");
  if (elem)
    {
    this->processObjects(elem);
    }

  if (this->Status == "")
    {
    return 0;
    }

  return -1;
}
//----------------------------------------------------------------------------
int pqCMBSceneV2Reader::getUserDefinedObjectTypes(
  vtkXMLDataElement* root, QStringList& objTypes)
{
  if (strcmp(root->GetName(), "Scene"))
    {
    this->Status = "Root Element is not a Scene";
    return -1;
    }
  const char *cData = root->GetAttribute("Version");
  if (!cData || strcmp(cData, "2.0"))
    {
    this->Status = "The getUserDefinedObjectTypes() is only supported in version2.0 reader.";
    return -1;
    }

  int status = 0;
  vtkXMLDataElement *elem = root->FindNestedElementWithName("UserDefinedTypeDefinitions");
  objTypes.clear();
  if (elem)
    {
    for (int i = 0; i < elem->GetNumberOfNestedElements(); i++)
      {
      objTypes << elem->GetNestedElement(i)->GetAttribute("Name");
      }
    }
  else
    {
    this->Status = "Can't find UserDefinedTypeDefinitions element in XML.";
    status = -1;
    }
  return status;
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processTypes(vtkXMLDataElement *typeSection)
{
  pqCMBSceneNode *parent, *node;
  vtkXMLDataElement *te, *ce;
  const char *data;
  double color[4];
  int n = typeSection->GetNumberOfNestedElements();
  int i;
  for (i = 0; i < n; i++)
    {
    te = typeSection->GetNestedElement(i);
    data = te->GetAttribute("Parent");
    if (!data)
      {
      node = this->Tree->createRoot(te->GetAttribute("Name"));
      }
    else
      {
      parent = this->Tree->findNode(data);
      node = this->Tree->createNode(te->GetAttribute("Name"), parent, NULL, NULL);
      }
    ce = te->FindNestedElementWithName("Color");
    if (ce)
      {
      ce->GetVectorAttribute("Value", 4, color);
      node->setExplicitColor(color);
      }

    if (te->FindNestedElementWithName("Invisible"))
      {
      node->setVisibility(false);
      }
    if (te->FindNestedElementWithName("Locked"))
      {
      node->setIsLocked(true);
      }
    }
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processUserDefinedObjectTypes(vtkXMLDataElement *typesSection)
{
  vtkXMLDataElement *fe;
  int n = typesSection->GetNumberOfNestedElements();
  int i;
  for (i = 0; i < n; i++)
    {
    fe = typesSection->GetNestedElement(i);
    this->Tree->addUserDefinedType(fe->GetAttribute("Name"), false);
    }
  this->Tree->cleanUpUserDefinedTypes();
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processContourFile(vtkXMLDataElement *contourSection)
{
  std::string fullOrigPath =
    vtksys::SystemTools::CollapseFullPath( this->FileName.c_str());
  std::string dataPath = vtksys::SystemTools::GetFilenamePath(fullOrigPath);

  QStringList files;
  files << vtksys::SystemTools::CollapseFullPath(
    contourSection->GetAttribute("FileName"),dataPath.c_str()).c_str();

  //now that we have the contour file name, lets read it in and generate
  //all the server side contour objects
  pqObjectBuilder* builder = pqApplicationCore::instance()->getObjectBuilder();
  builder->blockSignals(true);
  pqPipelineSource *reader = NULL;
  reader = builder->createReader("sources", "XMLPolyDataReader", files,
          this->Tree->getCurrentServer());
  builder->blockSignals(false);

  if (reader)
    {
    pqPipelineSource* extractContour = builder->createFilter("filters",
      "CmbExtractContours", reader);
    vtkSMSourceProxy *proxy = vtkSMSourceProxy::SafeDownCast( extractContour->getProxy() );
    proxy->UpdatePipeline();
    proxy->UpdatePropertyInformation();
    int max =pqSMAdaptor::getElementProperty(
      proxy->GetProperty("NumberOfContoursInfo")).toInt();
    this->createContour(proxy);
    for ( int i = 1; i < max; ++i)
      {
      pqSMAdaptor::setElementProperty(proxy->GetProperty("ContourIndex"),i);
      proxy->UpdateProperty("ContourIndex");
      proxy->UpdatePipeline();
      this->createContour(proxy);
      }
    }
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::createContour(vtkSMSourceProxy *proxy)
{
  pqCMBArc* obj = new pqCMBArc(proxy);
  this->Contours.push(obj);
}

//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processFiles(vtkXMLDataElement *dirSection)
{
  std::string fullOrigPath =
    vtksys::SystemTools::CollapseFullPath( this->FileName.c_str());
  std::string dataPath = vtksys::SystemTools::GetFilenamePath( fullOrigPath );
  std::string fullPath;
  vtkXMLDataElement *fe;
  int n = dirSection->GetNumberOfNestedElements();
  int i;
  for (i = 0; i < n; i++)
    {
    fe = dirSection->GetNestedElement(i);
    fullPath =
      vtksys::SystemTools::CollapseFullPath(fe->GetAttribute("Name"),
                                            dataPath.c_str());
    this->FileNames.push_back(fullPath);
    this->Sources.push_back(NULL);
    }
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processTextureFiles(vtkXMLDataElement *dirSection)
{
  std::string fullOrigPath =
    vtksys::SystemTools::CollapseFullPath( this->FileName.c_str());
  std::string dataPath = vtksys::SystemTools::GetFilenamePath( fullOrigPath );
  std::string fullPath;
  vtkXMLDataElement *fe;
  int n = dirSection->GetNumberOfNestedElements();
  int i;
  for (i = 0; i < n; i++)
    {
    fe = dirSection->GetNestedElement(i);
    fullPath =
      vtksys::SystemTools::CollapseFullPath(fe->GetAttribute("Name"),
                                            dataPath.c_str());
    this->TextureFileNames.push_back(fullPath);
    }
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processObjects(vtkXMLDataElement *objSection)
{
  vtkXMLDataElement *oe;
  int baseCount = this->Progress->value() + 1;
  int n = objSection->GetNumberOfNestedElements();
  int i;

  vtkBoundingBox bbox;
  vtkXMLDataElement* pe=NULL;
  double position[3];
  bbox.SetBounds(this->BoundsConstraint);

  QString baseText("Creating Node:");
  for (i = 0; i < n; i++)
    {
    oe = objSection->GetNestedElement(i);
    this->Progress->setLabelText(baseText + oe->GetAttribute("Name"));
    this->Progress->setValue(baseCount+i);
    if (this->Progress->wasCanceled())
      {
      this->Tree->empty();
      this->Status = "Loading aborted by User";
      return;
      }

    // check if these objects should be processed.
    if(this->FilterObjectByType &&
      !this->FilterObjectTypes.contains(oe->GetAttribute("UserDefinedType")))
      {
      continue;
      }
    if(this->UseBoundsConstraint)
      {
      // We need the position
      pe = oe->FindNestedElementWithName("Position");
      if(!pe)
        {
        continue;
        }
      pe->GetVectorAttribute("Value", 3, position);
      if(!bbox.ContainsPoint(position))
        {
        continue;
        }
      }

    this->processObject(oe);
    }
}

//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processVOI(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  // Make sure its a bounds type VOI
  if (strcmp("Bounds", elem->GetAttribute("VOIType")))
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " is a VOI of an unsupported type: ";
    estring += elem->GetAttribute("VOIType");
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }
  double bounds[6];
  double position[3];

  // We need the position any way for VOI so lets grab it
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  e = elem->FindNestedElementWithName("Bounds");
  e->GetVectorAttribute("Value", 6, bounds);
  obj = new pqCMBVOI(position, bounds,
                        this->Tree->getCurrentServer(),
                        this->Tree->getCurrentView(),
                        false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processLine(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  // Make sure its a staight line type LineObject
  if (strcmp("StraightLine", elem->GetAttribute("LineType")))
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " is a line of an unsupported type: ";
    estring += elem->GetAttribute("LineType");
    this->appendStatus(estring);
    return NULL;
    }
  double point1[3], point2[3];
  e = elem->FindNestedElementWithName("Point1WorldPosition");
  e->GetVectorAttribute("Value", 3, point1);
  e = elem->FindNestedElementWithName("Point2WorldPosition");
  e->GetVectorAttribute("Value", 3, point2);

  obj = new pqCMBLine(point1, point2,
                         this->Tree->getCurrentServer(),
                         this->Tree->getCurrentView(),
                         false);
  this->Tree->setLineWidgetCallbacks(static_cast<pqCMBLine*>(obj));

  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processContour(vtkXMLDataElement * elem)
{
  vtkXMLDataElement *e;
  int positionNormal;
  double positionOffset;
  e = elem->FindNestedElementWithName("PlaneProjectionNormal");
  e->GetScalarAttribute("Value", positionNormal);
  e = elem->FindNestedElementWithName("PlanePositionOffset");
  e->GetScalarAttribute("Value", positionOffset);

  if ( this->Contours.empty() )
    {
    return NULL;
    }

  pqCMBArc *obj = this->Contours.front();
  obj->setPlaneProjectionNormal(positionNormal);
  obj->setPlaneProjectionPosition(positionOffset);

  this->Contours.pop();
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processCone(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double tr, br, h, p1[3], dir[3];
  int r;
  e = elem->FindNestedElementWithName("BaseCenter");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, p1);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("Direction");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, dir);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("Height");
  if(e)
    {
    e->GetScalarAttribute("Value", h);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("BaseRadius");
  if(e)
    {
    e->GetScalarAttribute("Value", br);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("TopRadius");
  if(e)
    {
    e->GetScalarAttribute("Value", tr);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("Resolution");
  if(e)
    {
    e->GetScalarAttribute("Value", r);
    }
  else
    {
    return NULL;
    }

  obj = new pqCMBConicalRegion(p1, br, h, tr, dir, r,
                                  this->Tree->getCurrentServer(),
                                  this->Tree->getCurrentView(),
                                  false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processPlane(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double position[3], p1[3], p2[3];

  e = elem->FindNestedElementWithName("Point1");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, p1);
    }
  else
    {
    return NULL;
    }

  e = elem->FindNestedElementWithName("Point2");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, p2);
    }
  else
    {
    return NULL;
    }

  obj = new pqCMBPlane(p1, p2,
                          this->Tree->getCurrentServer(),
                          this->Tree->getCurrentView(),
                          false);
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  obj->setPosition(position, false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processPoints(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double position[3];

  // if LIDAR data then (if not older version of sg file) will have
  // target/max number of points to load (to match state when saved to disk).
  // If older version of file, just default to 1,000,000 points.
  int lidarMaxNumberOfPoints = 1000000;
  elem->GetScalarAttribute("MaxNumberOfPoints", lidarMaxNumberOfPoints);

  // newset version support for LIDAR (pts and las) does pieces seperately
  // if pieceId is set, then must be the newest version
  int pieceId = -1;
  elem->GetScalarAttribute("PieceId", pieceId);

  bool doublePrecision = false;
  const char* precisionString = elem->GetAttribute("DataPrecision");
  if (precisionString && !strcmp(precisionString, "double"))
    {
    doublePrecision = true;
    }

 // See if we have already created a source for this object
  int index;
  elem->GetScalarAttribute("FileEntry", index);
  if (index < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a points file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  // if pieceId != -1, then LAS or LIDARPts file, which we handle differently
  // since may be multiple pieces in the file that will all use the same reader
  // to read
  if (pieceId != -1)
    {
    pqPipelineSource* source = 0;
    if (this->Sources[index])
      {
      source = dynamic_cast<pqCMBPoints*>(this->Sources[index])->getReaderSource();
      }
    else
      {
      QStringList files;
      files << this->FileNames[index].c_str();
      pqObjectBuilder* builder = pqApplicationCore::instance()->getObjectBuilder();

      QFileInfo finfo(this->FileNames[index].c_str());
      builder->blockSignals(true);
      if (finfo.completeSuffix().toLower() == "las")
        {
        source = builder->createReader("sources", "LASReader", files,
          this->Tree->getCurrentServer());
        }
      else // currently (for what we support) has to by LIDAR pts format
        {
        source = builder->createReader("sources", "LIDARReader", files,
          this->Tree->getCurrentServer());
        }
      builder->blockSignals(false);
      }
    int onRatio = 1;
    elem->GetScalarAttribute("PieceOnRatio", onRatio);
    pqCMBPoints *pobj =
      new pqCMBPoints(
        this->Tree->getCurrentServer(),
        this->Tree->getCurrentView(), source, pieceId, onRatio, doublePrecision);
    pobj->setFileName(this->FileNames[index].c_str());
    obj = pobj;
    this->Sources[index] = obj;
    }
  else if (this->Sources[index]) // Old format but we already have the source read in
    {
    obj = this->Sources[index]->duplicate(this->Tree->getCurrentServer(),
                                          this->Tree->getCurrentView(),
                                          false);
    }
  else // Old format that has not yet been read in
    {
    obj = new pqCMBPoints(this->FileNames[index].c_str(),
                             this->Tree->getCurrentServer(),
                             this->Tree->getCurrentView(),
                             lidarMaxNumberOfPoints,
                             false);
    this->Sources[index] = obj;
    }
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  obj->setPosition(position, false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processFacetedObject(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double position[3];

  // See if we have already created a source for this object
  int index;
  elem->GetScalarAttribute("FileEntry", index);
  if (index < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a faceted file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  pqCMBSceneObjectBase::enumSurfaceType otype =
    pqCMBSceneObjectBase::convertStringToSurfaceType(elem->GetAttribute("SurfaceType"));

  // Have we read in the file already?
  if (this->Sources[index])
    {
    obj = this->Sources[index]->duplicate(this->Tree->getCurrentServer(),
                                          this->Tree->getCurrentView(),
                                          false);
    }
  else
    {
    obj =
      new pqCMBFacetedObject(this->FileNames[index].c_str(),
                                this->Tree->getCurrentServer(),
                                this->Tree->getCurrentView(),
                                false);
    this->Sources[index] = obj;
    }

  pqCMBFacetedObject *fobj = dynamic_cast<pqCMBFacetedObject*>(obj);
  fobj->setSurfaceType(static_cast<pqCMBSceneObjectBase::enumSurfaceType>(otype));
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  obj->setPosition(position, false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processUniformGrid(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double position[3];

  // See if we have already created a source for this object
  int index;
  elem->GetScalarAttribute("FileEntry", index);
  if (index < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have an image file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  // Have we read in the file already?
  if (this->Sources[index])
    {
    obj = this->Sources[index]->duplicate(this->Tree->getCurrentServer(),
                                          this->Tree->getCurrentView(),
                                          false);
    }
  else
    {
    obj =
      new pqCMBUniformGrid(this->FileNames[index].c_str(),
                                this->Tree->getCurrentServer(),
                                this->Tree->getCurrentView(),
                                false);
    this->Sources[index] = obj;
    }

  pqCMBUniformGrid *fobj = dynamic_cast<pqCMBUniformGrid*>(obj);
  // See if this is a Raw DEM File
  e = elem->FindNestedElementWithName("RawDEMInfo");
  if(e)
    {
    vtkIdType a[2], b[2];
    int c;
    elem->GetScalarAttribute("ReadGroupOfFiles", c);
    fobj->setReadGroupOfFiles((c==1));
    elem->GetScalarAttribute("OnRatio", c);
    fobj->setOnRatio(c);
    e->GetVectorAttribute("RowExtents", 2, a);
    e->GetVectorAttribute("ColumnExtents", 2, b);
    fobj->setExtents(a, b);
    }
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  obj->setPosition(position, false);
  return obj;
}
//----------------------------------------------------------------------------

pqCMBSceneObjectBase *pqCMBSceneV2Reader::processUnknownObject(vtkXMLDataElement * elem)
{
  std::string estring = elem->GetAttribute("Name");
  estring += " is of unknown type and is not supported by this reader";
  this->appendStatus(estring);
  return NULL; // We don't support this!
}

//----------------------------------------------------------------------------
pqCMBSceneObjectBase *pqCMBSceneV2Reader::processGlyphObject(vtkXMLDataElement * elem)
{
  pqCMBGlyphObject *obj;
  vtkXMLDataElement *e;
  double position[3];

  // See if we have already created a source for this object
  int gindex, pindex;
  elem->GetScalarAttribute("GlyphFileEntry", gindex);
  if (gindex < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a glyph file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  elem->GetScalarAttribute("PointsFileEntry", pindex);
  if (pindex < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a points file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  pqCMBSceneObjectBase::enumSurfaceType otype =
    pqCMBSceneObjectBase::convertStringToSurfaceType(elem->GetAttribute("SurfaceType"));

  // Have we read in the glyph file already?
  // NOTE: that if we have not read in the file for a faceted object that also
  // uses the same source geometry as the glyph we do not cache the read in source
  // The reason for this is that we would need to keep track of these faceted scene
  // objects so we can later delete them.  This implementation is not 100% optimal but
  // is easier to maintain and the cost is almost 0
    obj =
      new pqCMBGlyphObject(this->FileNames[gindex].c_str(),
                              this->FileNames[pindex].c_str(),
                              this->Tree->getCurrentServer(),
                              this->Tree->getCurrentView(),
                              false);
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
   position[0] = position[1] = position[2] = 0.0;
   }

  obj->setPosition(position, false);
  obj->setSurfaceType(static_cast<pqCMBSceneObjectBase::enumSurfaceType>(otype));
  return obj;
}

//----------------------------------------------------------------------------
pqCMBSceneObjectBase *pqCMBSceneV2Reader::processPolygons(vtkXMLDataElement * elem)
{
  pqCMBPolygon *obj = NULL;
  vtkXMLDataElement *e;

  // See if we have already created a source for this object
  int pindex;
  elem->GetScalarAttribute("PolygonFileEntry", pindex);
  if (pindex < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a polygon file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  // We need the min angle
  double minAngle=0; //default to disable min angle
  elem->GetScalarAttribute("MinAngle",minAngle);

  // We need the edge length
  double edgeLength=0; //default to disabled
  elem->GetScalarAttribute("EdgeLength",edgeLength);

  e = elem->FindNestedElementWithName("Arcs");
  std::vector<pqCMBSceneNode*> inputNodes;
  pqCMBSceneNode *temp = NULL;
  if ( e )
    {
    int size = 0;
    const char* arcName;
    e->GetScalarAttribute("Size", size);
    for ( int i=0; i < size; ++i)
      {
      arcName = e->GetNestedElement(i)->GetAttribute("Name");
      if (!arcName)
        {
        continue;
        }
      //look up this name in the scene tree
      temp = this->Tree->findNode(arcName);
      if ( temp )
        {
        inputNodes.push_back(temp);
        }
      }
    //create the polygon filter client item
    obj = new pqCMBPolygon(minAngle,edgeLength,inputNodes);
    }

  return obj;
}

//----------------------------------------------------------------------------

void pqCMBSceneV2Reader::processObject(vtkXMLDataElement * elem)
{

  pqCMBSceneObjectBase::enumObjectType otype =
    pqCMBSceneObjectBase::convertStringToType(elem->GetAttribute("ObjectType"));

  pqCMBSceneNode *parent = this->Tree->findNode(elem->GetAttribute("NodeType"));
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e, *colorElement;
  double data3[3];

  if (otype == pqCMBSceneObjectBase::VOI)
    {
    obj = this->processVOI(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Line)
    {
    obj = this->processLine(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Arc)
    {
    obj = this->processContour(elem);
    }
  else if (otype == pqCMBSceneObjectBase::GroundPlane)
    {
    obj = this->processPlane(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Points)
    {
    obj = this->processPoints(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Faceted)
    {
    obj = this->processFacetedObject(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Glyph)
    {
    obj = this->processGlyphObject(elem);
    }
  else if (otype == pqCMBSceneObjectBase::Polygon)
    {
    obj = this->processPolygons(elem);
    }
  else if (otype == pqCMBSceneObjectBase::GeneralCone)
    {
    obj = this->processCone(elem);
    }
  else if (otype == pqCMBSceneObjectBase::UniformGrid)
    {
    obj = this->processUniformGrid(elem);
    }
  else if (otype == pqCMBSceneObjectBase::SolidMesh)
    {
    obj = this->processSolidMesh(elem);
    }
  else
    {
    obj = this->processUnknownObject(elem);
    }
  if (obj == NULL)
    {
    return;
    }

  e = elem->FindNestedElementWithName("Units");
  if(e)
    {
    obj->setUnits(cmbSceneUnits::convertFromString(e->GetAttribute("Value")));
    }

  e = elem->FindNestedElementWithName("Orientation");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, data3);
    obj->setOrientation(data3, false);
    }
  e = elem->FindNestedElementWithName("Scale");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, data3);
    obj->setScale(data3, false);
    }
  e = elem->FindNestedElementWithName("Origin");
  if (e)
    {
    e->GetVectorAttribute("Value", 3, data3);
    obj->setOrigin(data3, false);
    }

  e = elem->FindNestedElementWithName("Constraints");
  if (e)
    {
    this->processConstraints(e, obj);
    }

  e = elem->FindNestedElementWithName("TextureInfo");
  if (e)
    {
    int status = this->processTextureInfo(e, obj);
    if (status == -1)
      {
      std::string estring = elem->GetAttribute("Name");
      estring += " does not have a texture file associated with it";
      this->appendStatus(estring);
      }
    else if (status == -2)
      {
      std::string estring = elem->GetAttribute("Name");
      estring += " can not have a texture associated with it";
      this->appendStatus(estring);
      }
    }

  e = elem->FindNestedElementWithName("BathymetryInfo");
  if (e)
    {
    int status = this->processBathymetryInfo(e, obj);
    if (status == -1)
      {
      std::string estring = elem->GetAttribute("Name");
      estring += " can not have a bathymetry source associated with it";
      this->appendStatus(estring);
      }
    else if (status == -2)
      {
      std::string estring = elem->GetAttribute("Name");
      estring += " does not have a valid bathymetry source associated with it";
      this->appendStatus(estring);
      }
    }

  e = elem->FindNestedElementWithName("ShowElevation");
  if (e)
    {
    pqCMBTexturedObject *tobj = dynamic_cast<pqCMBTexturedObject *>(obj);
    if (tobj == NULL)
      {
      std::string estring = elem->GetAttribute("Name");
      estring += " can not have elevation associated with it";
      this->appendStatus(estring);
      }
    else
      {
      tobj->showElevation(true);
      }
    }

  pqCMBSceneNode *node =
    this->Tree->createNode(elem->GetAttribute("Name"), parent, obj, NULL);

  double color[4];
  colorElement = elem->FindNestedElementWithName("Color");
  if(colorElement &&
    colorElement->FindNestedElementWithName("Inherited") == NULL)
    {
    // Explicit Color
    colorElement->GetVectorAttribute("Value", 4, color);
    node->setExplicitColor(color);
    }

  if (elem->FindNestedElementWithName("Invisible"))
    {
    node->setVisibility(false);
    }
  if (elem->FindNestedElementWithName("Locked"))
    {
    node->setIsLocked(true);
    }
  std::string userType = elem->GetAttribute("UserDefinedType");
  obj->setUserDefinedType(userType.c_str());
  obj->updateRepresentation();
  if (elem->FindNestedElementWithName("SnapTarget"))
    {
    this->Tree->setSnapTarget(node);
    }
}

//----------------------------------------------------------------------------
int pqCMBSceneV2Reader::processTextureInfo(vtkXMLDataElement *elem,
                                          pqCMBSceneObjectBase *obj)
{
  int index;
  int npoints;
  double points[12];

  elem->GetScalarAttribute("TextureFileEntry", index);
  if (index < 0)
    {
    return -1;
    }

  pqCMBTexturedObject *tobj = dynamic_cast<pqCMBTexturedObject *>(obj);
  if (tobj == NULL)
    {
    return -2;
    }

  elem->GetScalarAttribute("NumberOfRegistrationPoints", npoints);
  elem->GetVectorAttribute("Points", npoints*4, points);

  tobj->setTextureMap(this->TextureFileNames[index].c_str(), npoints, points);
  this->Tree->addTextureFileName(this->TextureFileNames[index].c_str());
  return 0;
}

//----------------------------------------------------------------------------
int pqCMBSceneV2Reader::processBathymetryInfo(
  vtkXMLDataElement *elem, pqCMBSceneObjectBase *obj)
{
  pqCMBTexturedObject *tobj = dynamic_cast<pqCMBTexturedObject *>(obj);
  if (tobj == NULL)
    {
    return -1;
    }
  vtkXMLDataElement *bathyDE = elem->FindNestedElementWithName("BathymetrySource");
  if (!bathyDE)
    {
    return -2;
    }
  std::string sourceObjName = bathyDE->GetAttribute("Name");
  pqCMBSceneNode* node = this->Tree->getSceneNodeByName(sourceObjName.c_str());
  if (!node)
    {
    return -2;
    }
  double radius;
  // NOTE: Other bathymetry parameters (low and high limits) are not saved currently.
  int numRead = bathyDE->GetScalarAttribute("ElevationRadius", radius);
  radius = numRead>0 ? radius : 1.0;
  tobj->applyBathymetry(node->getDataObject(), radius);
  return 0;
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::processConstraints(vtkXMLDataElement *constraintSection,
                                          pqCMBSceneObjectBase *obj)
{
  obj->clearConstraints();
  vtkXMLDataElement *ce;
  int n = constraintSection->GetNumberOfNestedElements();
  int i;
  std::string name;
  for (i = 0; i < n; i++)
    {
    ce = constraintSection->GetNestedElement(i);
    name = ce->GetName();

    if (name == "XTransConstraint")
      {
      obj->setXTransConstraint(true);
      }
    else if (name == "YTransConstraint")
      {
      obj->setYTransConstraint(true);
      }
    else if (name == "ZTransConstraint")
      {
      obj->setZTransConstraint(true);
      }
    else if (name == "XRotationConstraint")
      {
      obj->setXRotationConstraint(true);
      }
    else if (name == "YRotationConstraint")
      {
      obj->setYRotationConstraint(true);
      }
    else if (name == "ZRotationConstraint")
      {
      obj->setZRotationConstraint(true);
      }
    else if (name == "IsotropicScalingConstraint")
      {
      obj->setIsotropicScalingConstraint(true);
      }
    else if (name == "XScalingConstraint")
      {
      obj->setXScalingConstraint(true);
      }
    else if (name == "YScalingConstraint")
      {
      obj->setYScalingConstraint(true);
      }
    else if (name == "ZScalingConstraint")
      {
      obj->setZScalingConstraint(true);
      }
    else
      {
      std::string estring = name;
      estring += " is an unsupported constraint";
      this->appendStatus(estring);
      }
    }
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::appendStatus(const std::string &newStatus)
{
  if (this->Status != "")
    {
    this->Status += "\n";
    }
  this->Status += newStatus;
}
//----------------------------------------------------------------------------
pqCMBSceneObjectBase *pqCMBSceneV2Reader::processSolidMesh(vtkXMLDataElement * elem)
{
  pqCMBSceneObjectBase *obj;
  vtkXMLDataElement *e;
  double position[3];

  // See if we have already created a source for this object
  int index;
  elem->GetScalarAttribute("FileEntry", index);
  if (index < 0)
    {
    std::string estring = elem->GetAttribute("Name");
    estring += " does not have a faceted file associated with it";
    this->appendStatus(estring);
    return NULL; // We don't support this!
    }

  // Have we read in the file already?
  if (this->Sources[index])
    {
    obj = this->Sources[index]->duplicate(this->Tree->getCurrentServer(),
      this->Tree->getCurrentView(),
      false);
    }
  else
    {
    obj =
      new pqCMBSolidMesh(this->FileNames[index].c_str(),
      this->Tree->getCurrentServer(),
      this->Tree->getCurrentView(),
      false);
    this->Sources[index] = obj;
    }

  pqCMBSolidMesh *fobj = dynamic_cast<pqCMBSolidMesh*>(obj);
  // We need the position
  e = elem->FindNestedElementWithName("Position");
  if(e)
    {
    e->GetVectorAttribute("Value", 3, position);
    }
  else
    {
    position[0] = position[1] = position[2] = 0.0;
    }

  obj->setPosition(position, false);
  return obj;
}
//----------------------------------------------------------------------------
void pqCMBSceneV2Reader::abortSceneLoading()
{
  this->Tree->empty();
  this->Status = "Loading aborted by User";
}
//----------------------------------------------------------------------------
