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

Copyright (c) 1998-2012 Kitware Inc. 28 Corporate Drive,
Clifton Park, NY, 12065, USA.

All rights reserved. No part of this software may be reproduced,
distributed,
or modified, in any form or by any means, without permission in writing from
Kitware Inc.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
=========================================================================*/
#include "vtkKMLReader.h"

// KML includes.
#include <kml/dom.h>

#include <kml/engine/kml_file.h>
#include <kml/engine/style_resolver.h>

// C++ includes.
#include <ios>
#include <iostream>
#include <string>
#include <sstream>

// C includes.
#include <stdio.h>

// VTK includes.
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#include <vtkIntArray.h>
#include <vtkLine.h>
#include "vtkNew.h"
#include <vtkObjectFactory.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkPolyDataReader.h>
#include <vtkPolygon.h>
#include <vtkPolyLine.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkVertex.h>
#include <vtkXMLPolyDataWriter.h>

#include <vtksys/SystemTools.hxx>

// VTKExtensions includes.
#include <vtkCMBTriangleMesher.h>
#include <vtkCMBPrepareForTriangleMesher.h>

// Import types.
using kmldom::AbstractViewPtr;
using kmldom::CoordinatesPtr;
using kmldom::DocumentPtr;
using kmldom::ElementPtr;
using kmldom::FeaturePtr;
using kmldom::FolderPtr;
using kmldom::GeometryPtr;
using kmldom::IconStylePtr;
using kmldom::InnerBoundaryIsPtr;
using kmldom::KmlPtr;
using kmldom::LinearRingPtr;
using kmldom::LineStringPtr;
using kmldom::LineStylePtr;
using kmldom::LookAtPtr;
using kmldom::MultiGeometryPtr;
using kmldom::OuterBoundaryIsPtr;
using kmldom::PlacemarkPtr;
using kmldom::PointPtr;
using kmldom::PolygonPtr;
using kmldom::PolyStylePtr;
using kmldom::StylePtr;
using kmldom::StyleSelectorPtr;

using kmlengine::KmlFilePtr;

// @NOTE: Not supporting KMZ.
//using kmlengine::KmzFilePtr;

vtkStandardNewMacro(vtkKMLReader);

//-----------------------------------------------------------------------------
// Implementation class.
class vtkKMLReaderInternal
{
public:
  vtkKMLReaderInternal();
 ~vtkKMLReaderInternal();


  FeaturePtr  GetRootFeature(ElementPtr elem);
  DocumentPtr GetDocument   (FeaturePtr feature);

  void ConvertHexToRGB      (std::string hexStr,
                             int& r, int& g, int& b);

  vtkPolyData*
       HandleFile           (const char* fileName);

  // Currently supported KML tags.
  void HandleDocument       (DocumentPtr documentPtr);
  void HandleFeature        (FeaturePtr featurePtr);
  void HandleFolder         (FolderPtr folderPtr);
  void HandleGeometry       (GeometryPtr geometryPtr, StylePtr stylePtr);
  void HandleLineString     (LineStringPtr linePtr, LineStylePtr lineStylePtr);
  void HandleLookAt         (LookAtPtr lookAtPtr);
  void HandleMultiGeometry  (MultiGeometryPtr multiGeometryPtr, StylePtr stylePtr);
  void HandlePlacemark      (PlacemarkPtr placemarkPtr);
  void HandlePoint          (PointPtr pointPtr, IconStylePtr iconStylePtr);
  void HandlePolygon        (PolygonPtr polygonPtr, PolyStylePtr polyStylePtr);
  void HandleStyle          (StylePtr stylePtr);
  void HandleStyleSelector  (StyleSelectorPtr styleSelectorPtr);
  void HandleStyleUrl       ();

  void Triangulate          (vtkPolyData* polyData);

  // Global variables.
  ElementPtr kmlFile;

  std::map<std::string, StylePtr>             styles;
  typedef std::map<std::string, StylePtr>::const_iterator
                                              StylesItr;

  vtkSmartPointer<vtkCellArray> polys;
  vtkSmartPointer<vtkCellArray> lines;
  vtkSmartPointer<vtkCellArray> vertices;
  vtkSmartPointer<vtkPoints>    points;
  vtkSmartPointer<vtkPolyData>  polyData;
  vtkSmartPointer<vtkUnsignedCharArray>
                                colors;

  vtkSmartPointer<vtkIntArray>  polyDataIds;
  vtkSmartPointer<vtkIntArray>  polygonIds;

  struct KMLInternalPt
    {
    double x,y,z;
    friend bool operator < (const KMLInternalPt& l,const KMLInternalPt& r)
      {
      //Makes sure that points can't be equal unless all 3 components
      //are the same. Ordering doesn't really matter
      return l.x != r.x ? (l.x < r.x) : l.y != r.y ? (l.y < r.y) : l.z < r.z;
      }
    KMLInternalPt(double _x, double _y, double _z):x(_x),y(_y),z(_z){};
    };
};

//-----------------------------------------------------------------------------
vtkKMLReaderInternal::vtkKMLReaderInternal()
{
 points  = vtkSmartPointer<vtkPoints>::New();
 polys   = vtkSmartPointer<vtkCellArray>::New();
 lines   = vtkSmartPointer<vtkCellArray>::New();
 vertices= vtkSmartPointer<vtkCellArray>::New();
 colors  = vtkSmartPointer<vtkUnsignedCharArray>::New();
 colors->SetNumberOfComponents(3);

 polyDataIds = vtkSmartPointer<vtkIntArray>::New();
 polyDataIds->SetName("PolyDataIds");
 polyDataIds->SetNumberOfComponents(1);
 polyDataIds->Initialize();

 polygonIds = vtkSmartPointer<vtkIntArray>::New();
 polygonIds->SetName("PolygonIds");
 polygonIds->SetNumberOfComponents(1);
 polygonIds->Initialize();

 polyData =vtkSmartPointer<vtkPolyData>::New();
 polyData->SetPoints(points);
 polyData->SetPolys(polys);
 polyData->SetLines(lines);
 polyData->SetVerts(vertices);
}

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

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::ConvertHexToRGB(std::string hexStr,
                                           int& r, int& g, int& b)
{
  std::istringstream ss (hexStr);
  int ihex;
  ss >> std::hex >> ihex;
  r = (ihex >> 16)  & 0xFF;
  g = (ihex >> 8)   & 0xFF;
  b = (ihex)        & 0xFF;
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleFeature(FeaturePtr featurePtr)
{
  if(!featurePtr)
    {
    return;
    }

  if(kmldom::AsPlacemark(featurePtr))
    {
    this->HandlePlacemark(kmldom::AsPlacemark(featurePtr));
    }
  if(kmldom::AsDocument(featurePtr))
    {
    this->HandleDocument(kmldom::AsDocument(featurePtr));
    }
  if(kmldom::AsFolder(featurePtr))
    {
    this->HandleFolder(kmldom::AsFolder(featurePtr));
    }
}

//-----------------------------------------------------------------------------
vtkPolyData* vtkKMLReaderInternal::HandleFile(const char* fileName)
{
  if(!fileName)
    {
    return 0;
    }

  std::string ext = vtksys::SystemTools::GetFilenameExtension(fileName);

  // @NOTE: Not supporting KMZ as of now.
  if(ext.compare(".kmz") == 0)
     {
//     std::vector<string> kmls;
//     KmzFilePtr kmzFile = kmlengine::KmzFile::OpenFromFile(fileName);
//     kmzFile->List(&kmls);

//     for(size_t i=0; i < kmls.size(); ++i)
//       {
//       std::string output;
//       kmzFile->ReadKml(&output);
//       std::cout << output << std::endl;
//       kmlFile = kmldom::ParseKml(output);
//       if(!kmlFile)
//         {
//         std::cerr << "Error parsing KML. " << std::endl;
//         std::exit(0);
//         }

//       FeaturePtr feature = GetRootFeature(kmlFile);
//       this->HandleFeature(feature);
//       }
     }
  else
    {
    // Parse the file.
    FILE* filePtr;
    long  length;
    char* buffer;

    filePtr = fopen(fileName,"rb");
    fseek(filePtr,0,SEEK_END);        //go to end

    length = ftell(filePtr);          //get position at end (length)
    fseek(filePtr,0,SEEK_SET);        //go to beg.

    buffer=static_cast<char *>(malloc(length));                      //malloc buffer
    /*size_t bytes_read =*/ fread(buffer,length,1,filePtr); //read into buffer

    fclose(filePtr);

    buffer[length-1] = '\0'; //Make sure the last character is a null terminator

    std::string xmlStr (buffer);
    std::string errors;

    this->kmlFile = kmldom::Parse(xmlStr, &errors);
    if(!this->kmlFile)
       {
       std::cerr << "Error parsing KML. " << std::endl;
       std::cerr << "Error message: " << errors << std::endl;
       return 0;
       }

     FeaturePtr feature = this->GetRootFeature(this->kmlFile);
     this->HandleFeature(feature);
     }

  // Now insert cell data.
  for(int i=0; i < vertices->GetNumberOfCells();++i)
    {
    polyDataIds->InsertNextValue(-1);
    }
  for(int i=0; i < lines->GetNumberOfCells();++i)
    {
    polyDataIds->InsertNextValue(-1);
    }
  for(int i=0; i < polygonIds->GetNumberOfTuples();++i)
    {
    polyDataIds->InsertNextValue(polygonIds->GetValue(i));
    }

  polyData->GetCellData()->AddArray(polyDataIds);

  return polyData;
}


//-----------------------------------------------------------------------------
FeaturePtr vtkKMLReaderInternal::GetRootFeature(ElementPtr elem)
{
  if(!elem)
    {
    return 0;
    }

  KmlPtr kml = kmldom::AsKml(elem);
  if(kml)
    {
    return kml->get_feature();
    }
  else
    {
    return kmldom::AsFeature(elem);
    }
}

//-----------------------------------------------------------------------------
DocumentPtr vtkKMLReaderInternal::GetDocument(FeaturePtr feature)
{
  if(!feature)
    {
    return 0;
    }

  return kmldom::AsDocument(feature);
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandlePlacemark(PlacemarkPtr placemarkPtr)
{
  if(!placemarkPtr)
    {
    return;
    }

  // This the case when style is referenced.
  StylePtr stylePtr (0);
  if(placemarkPtr->has_styleurl())
    {
    std::string styleURL = placemarkPtr->get_styleurl();
    StylesItr itr = styles.find(styleURL);
    if(itr != styles.end())
      {
      stylePtr = itr->second;
      if(stylePtr)
        {
        // Do nothing.
        }
      }
    else
      {
        // Do nothing.
      }

    // @TODO: Handle style maps here?
    }

  // This is the case when style is within the placemark.
  if(placemarkPtr->has_styleselector())
    {
    PolyStylePtr polyStyle = kmldom::AsStyle(placemarkPtr->get_styleselector())->get_polystyle();
    if(polyStyle)
      {
      // Do nothing.
      }
    }

  if(placemarkPtr->has_geometry())
    {
    this->HandleGeometry(placemarkPtr->get_geometry(), stylePtr);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleDocument(DocumentPtr documentPtr)
{
  if(!documentPtr)
    {
    return;
    }

  // Documents can have more than one style selector.
  size_t numberOfStyles = documentPtr->get_styleselector_array_size();
  for(size_t i=0; i < numberOfStyles; ++i)
    {
    this->HandleStyleSelector(documentPtr->get_styleselector_array_at(i));
    }

  size_t numberOfFeatures = documentPtr->get_feature_array_size();
  for(size_t i=0; i < numberOfFeatures; ++i)
    {
    FeaturePtr feature = documentPtr->get_feature_array_at(i);
    this->HandleFeature(feature);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleFolder(FolderPtr folderPtr)
{
  if(!folderPtr)
    {
    return;
    }

  if(folderPtr->has_styleselector())
    {
    this->HandleStyleSelector(folderPtr->get_styleselector());
    }

  for(size_t i=0; i < folderPtr->get_feature_array_size(); ++i)
    {
    FeaturePtr feature = folderPtr->get_feature_array_at(i);
    this->HandleFeature(feature);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleGeometry(GeometryPtr geometryPtr, StylePtr stylePtr)
{
  if(MultiGeometryPtr multiGeometryPtr = kmldom::AsMultiGeometry(geometryPtr))
    {
    this->HandleMultiGeometry(multiGeometryPtr, stylePtr);
    }

  if(PolygonPtr poly = kmldom::AsPolygon(geometryPtr))
    {
    if(stylePtr)
      {
      this->HandlePolygon(poly, stylePtr->get_polystyle());
      }
    else
      {
      this->HandlePolygon(poly, 0);
      }
    }

  if(LineStringPtr line = kmldom::AsLineString(geometryPtr))
    {
    if(stylePtr)
      {
      this->HandleLineString(line, stylePtr->get_linestyle());
      }
    else
      {
      this->HandleLineString(line, 0);
      }
    }
  if(PointPtr pnt = kmldom::AsPoint(geometryPtr))
    {
    if(stylePtr)
      {
      this->HandlePoint(pnt, stylePtr->get_iconstyle());
      }
    else
      {
      this->HandlePoint(pnt, 0);
      }
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleMultiGeometry(MultiGeometryPtr multiGeometryPtr, StylePtr stylePtr)
{
  if(!multiGeometryPtr)
    {
    return;
    }

  size_t sizeOfGeometryArray = multiGeometryPtr->get_geometry_array_size();
  for(size_t i=0; i < sizeOfGeometryArray; ++i)
    {
    GeometryPtr geom = multiGeometryPtr->get_geometry_array_at(i);
    this->HandleGeometry(geom, stylePtr);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleLookAt(LookAtPtr /*lookAtPtr*/)
{
  // Not implemented yet.
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleStyleUrl()
{
  // Not implemented yet.
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleStyle(StylePtr stylePtr)
{
  if(!stylePtr)
    {
    return ;
    }

  std::string id = stylePtr->get_id();
  std::string url("#");
  url.append(id);

  styles.insert(std::pair<std::string, StylePtr>(url, stylePtr));
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleStyleSelector(StyleSelectorPtr styleSelectorPtr)
{
  if(!styleSelectorPtr)
    {
    return;
    }

  if( kmldom::StylePtr stylePtr = kmldom::AsStyle(styleSelectorPtr) )
    {
    this->HandleStyle(stylePtr);
    }
  if( kmldom::StyleMapPtr styleMapPtr = kmldom::AsStyleMap(styleSelectorPtr) )
    {
    // Not implemented yet.
    // @TODO: Implement this if required.
    }
}

//-----------------------------------------------------------------------------
// @TODO: What to do with the IconStyle?
void vtkKMLReaderInternal::HandlePoint(PointPtr pointPtr, IconStylePtr /*iconStylePtr*/)
{
  if(!pointPtr)
    {
    return;
    }

  vtkSmartPointer<vtkVertex> vtx (vtkSmartPointer<vtkVertex>::New());
  if(pointPtr->has_coordinates())
    {
    CoordinatesPtr coordinatesPtr = pointPtr->get_coordinates();
    size_t size = coordinatesPtr->get_coordinates_array_size();
    for(size_t i=0; i < size; ++i)
      {
      kmlbase::Vec3 vec = coordinatesPtr->get_coordinates_array_at(i);
      points->InsertNextPoint(vec.get_longitude(), vec.get_latitude(), vec.get_altitude());
      vtx->GetPointIds()->InsertNextId(points->GetNumberOfPoints() - 1);
      }

    vertices->InsertNextCell(vtx);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandleLineString(LineStringPtr linePtr, LineStylePtr lineStylePtr)
{
  if(!linePtr)
    {
    return;
    }

  bool hasColor(false);

  if(linePtr->has_coordinates())
    {
    vtkSmartPointer<vtkPolyLine>    polyLine (vtkSmartPointer<vtkPolyLine>::New());

    if(lineStylePtr)
      {
      // Handle color property.
      if(lineStylePtr->has_color())
        {
        kmlbase::Color32 color = lineStylePtr->get_color();
        unsigned char vtkColor[3] = {static_cast<unsigned char>(color.get_red()),
                                     static_cast<unsigned char>(color.get_green()),
                                     static_cast<unsigned char>(color.get_blue())
                                     };
        colors->InsertNextTupleValue(vtkColor);
        }
      }

    if(!hasColor)
      {
      unsigned char vtkColor[3] = {static_cast<unsigned char>(255),
                                   static_cast<unsigned char>(255),
                                   static_cast<unsigned char>(255)};
      colors->InsertNextTupleValue(vtkColor);
      }

    CoordinatesPtr coordinatesPtr = linePtr->get_coordinates();

    size_t coordinatesArraySize = coordinatesPtr->get_coordinates_array_size();

    int numberOfPoints = points->GetNumberOfPoints();

    for(size_t i=0; i < coordinatesArraySize; ++i)
      {
      kmlbase::Vec3 vec = coordinatesPtr->get_coordinates_array_at(i);
      points->InsertNextPoint(vec.get_longitude(),
                              vec.get_latitude(),
                              vec.get_altitude());

      polyLine->GetPointIds()->InsertNextId(numberOfPoints + i);
      }

    lines->InsertNextCell(polyLine);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::HandlePolygon(PolygonPtr polygonPtr, PolyStylePtr polyStylePtr)
{
  if(!polygonPtr)
    {
    return;
    }

  vtkSmartPointer<vtkPolyData> tempPoly;
  vtkSmartPointer<vtkCellArray>tempCells;
  vtkSmartPointer<vtkPoints>   tempPoints;

  vtkCMBPrepareForTriangleMesher* mapInterface = vtkCMBPrepareForTriangleMesher::New();

  std::map<KMLInternalPt, vtkIdType> pt2Id; //prevent duplicate points

  // @NOTE: Currently we support fill and color only.
  vtkSmartPointer<vtkCell>  cell(0);
  bool                      renderAsPolyLine(false);
  bool                      hasColor(false);
  bool                      triangulate(false);

  if(polyStylePtr)
    {
    // Handle fill property.
    if(polyStylePtr->has_fill())
      {
      if(!polyStylePtr->get_fill())
        {
        renderAsPolyLine = true;
        cell = vtkSmartPointer<vtkPolyLine>::New();
        }
      }

    // Handle color property.
    if(polyStylePtr->has_color())
      {
      kmlbase::Color32 color = polyStylePtr->get_color();
      unsigned char vtkColor[3] = {static_cast<unsigned char>(color.get_red()),
                                   static_cast<unsigned char>(color.get_green()),
                                   static_cast<unsigned char>(color.get_blue())};
      colors->InsertNextTupleValue(vtkColor);
      hasColor = true;
      }
    }

    if(!hasColor)
      {
      unsigned char vtkColor[3] = {static_cast<unsigned char>(255),
                                   static_cast<unsigned char>(255),
                                   static_cast<unsigned char>(255)};
      colors->InsertNextTupleValue(vtkColor);
      }

  if(!renderAsPolyLine)
    {
    cell = vtkSmartPointer<vtkPolygon>::New();
    }

  size_t sizeOfInnerBoundaryArray = polygonPtr->get_innerboundaryis_array_size();
  if(sizeOfInnerBoundaryArray > 0)
    {
    triangulate = true;
    }

  if(polygonPtr->has_outerboundaryis())
    {
    OuterBoundaryIsPtr outerBoundaryPtr = polygonPtr->get_outerboundaryis();
    if(outerBoundaryPtr->has_linearring())
      {
      LinearRingPtr linearRingPtr = outerBoundaryPtr->get_linearring();
      if(linearRingPtr->has_coordinates())
        {
        CoordinatesPtr coordinatesPtr = linearRingPtr->get_coordinates();

        size_t size = coordinatesPtr->get_coordinates_array_size();

        // If number of points in coordinate array is more than 3 than we could
        // be dealing with concave polygons or points not in the same plane. Though
        // it is overkill to triagulate 4 - small number of points but for time being
        // we are doing this.
        // @TODO: Optimize this.
        if(size > 3)
          {
          triangulate = true;
          }

        if(!triangulate)
          {
          int numberOfPoints = points->GetNumberOfPoints();

          for(size_t i=0; i < size; ++i)
            {
            kmlbase::Vec3 vec = coordinatesPtr->get_coordinates_array_at(i);
            points->InsertNextPoint((vec.get_longitude()),
                                    (vec.get_latitude()),
                                    (vec.get_altitude()));

            cell->GetPointIds()->InsertNextId(numberOfPoints + i);
            }

          if(!renderAsPolyLine)
            {
            polys->InsertNextCell(cell);
            }
          else
            {
            lines->InsertNextCell(cell);
            }
          } // triangulate.
        else
          {
          tempPoly    = vtkSmartPointer<vtkPolyData>::New();
          tempCells   = vtkSmartPointer<vtkCellArray>::New();
          tempPoints  = vtkSmartPointer<vtkPoints>::New();

          tempPoints->Initialize();
          tempPoints->SetDataTypeToDouble();

          tempPoly->SetPoints(tempPoints);
          tempPoly->SetLines(tempCells);

          mapInterface->SetPolyData(tempPoly);
          //TODO it would be nice to know how many arcs / loops
          //are going to be added. Not sure if possible or not
          mapInterface->InitializeNewMapInfo();

          vtkIdType ptIds[2];
          int count = 0;

          for(size_t i=0; i < size; ++i)
            {
            kmlbase::Vec3 vec = coordinatesPtr->get_coordinates_array_at(i);
            //Use pt2Id to see if this point is a duplicate
            KMLInternalPt ptToInsert(vec.get_longitude(),
                                     vec.get_latitude(),
                                     vec.get_altitude());
            std::map<KMLInternalPt, vtkIdType>::iterator pt2IdIter = pt2Id.find(ptToInsert);
            if(pt2IdIter != pt2Id.end())
              {//Pt has been seen
              ptIds[count] = (*pt2IdIter).second;
              }
            else
              { //Pt hasn't been seen
              ptIds[count] = tempPoints->InsertNextPoint(ptToInsert.x,
                                                         ptToInsert.y,
                                                         ptToInsert.z);
              pt2Id[ptToInsert] = ptIds[count];
              }

            if(i != 0)
              {
              tempCells->InsertNextCell(2, ptIds);
              ptIds[0] = ptIds[count];
              count = 0;
              }
            ++count;
            }

          //Assuming VTK_LINES, so 3 times numCells i
          vtkIdType loopId = mapInterface->AddLoop(1,-1);
          mapInterface->AddArc(0, (tempCells->GetNumberOfCells())*3,loopId,loopId,-1,0,0);
          }
        } // has_coordinates()
      } // has_linearring()
    } // has_outerboundaryis()

  for(size_t i=0; i < sizeOfInnerBoundaryArray; ++i)
    {
    InnerBoundaryIsPtr innerBoundaryPtr = polygonPtr->get_innerboundaryis_array_at(i);
    if(innerBoundaryPtr &&  innerBoundaryPtr->has_linearring())
      {
      LinearRingPtr linearRingPtr = innerBoundaryPtr->get_linearring();
      if(linearRingPtr->has_coordinates())
        {
        CoordinatesPtr coordinatesPtr = linearRingPtr->get_coordinates();

        size_t size = coordinatesPtr->get_coordinates_array_size();

        vtkIdType ptIds[2];
        int count = 0;

        vtkIdType firstCellPos = (tempCells->GetNumberOfCells())*3;
        for(size_t j=0; j < size; ++j)
          {
          kmlbase::Vec3 vec = coordinatesPtr->get_coordinates_array_at(j);
          //Use pt2Id to see if this point is a duplicate
          KMLInternalPt ptToInsert(vec.get_longitude(),
                                   vec.get_latitude(),
                                   vec.get_altitude());

          std::map<KMLInternalPt, vtkIdType>::iterator pt2IdIter = pt2Id.find(ptToInsert);
          if(pt2IdIter != pt2Id.end())
            {//Pt has been seen
            ptIds[count] = (*pt2IdIter).second;
            }
          else
            {//Pt hasn't been seen
            ptIds[count] = tempPoints->InsertNextPoint(ptToInsert.x,
                                                       ptToInsert.y,
                                                       ptToInsert.z);
            pt2Id[ptToInsert] = ptIds[count];
            }
          if(j != 0)
            {
            tempCells->InsertNextCell(2, ptIds);

            ptIds[0] = ptIds[count];
            count = 0;
            }
          ++count;
          }
          vtkIdType loopId = mapInterface->AddLoop(-1,1);
          mapInterface->AddArc(firstCellPos,(tempCells->GetNumberOfCells())*3,loopId,-1,loopId,1+i,1+i);

          } // has_coordinates()
        } // has_linearring()
      } // has_innerboundaryis()

  mapInterface->FinalizeNewMapInfo();
  mapInterface->Delete();
  if(triangulate)
    {
    this->Triangulate(tempPoly);
    }
}

//-----------------------------------------------------------------------------
void vtkKMLReaderInternal::Triangulate(vtkPolyData* polyDataIn)
{
  vtkNew<vtkCMBTriangleMesher> cmbMapMesher;
  cmbMapMesher->SetMaxAreaMode(vtkCMBTriangleMesher::NoMaxArea);
  cmbMapMesher->SetInputData(polyDataIn);
  cmbMapMesher->Update();
  vtkPolyData* output = cmbMapMesher->GetOutput();

  //when meshing fails you can't just expect to get a null data, you
  //can get an empty mesh instead.
  if(output &&
     output->GetNumberOfPoints() > 0 && output->GetNumberOfCells() > 0)
    {
    vtkPoints* outPnts = output->GetPoints();
    std::vector<int> exists(outPnts->GetNumberOfPoints(), -1);

    int lastPolygonId (0);
    if(polygonIds->GetNumberOfTuples() > 0)
      {
      //If there already were polygons start where they left off
      lastPolygonId = polygonIds->GetValue(polygonIds->GetNumberOfTuples() - 1);
      }

    int numberOfCells = output->GetNumberOfCells();
    for(int i=0; i < numberOfCells; ++i)
      {
      vtkSmartPointer<vtkIdList> list (vtkSmartPointer<vtkIdList>::New());
      output->GetCellPoints(i, list);
      int numberOfIds = list->GetNumberOfIds();
      //If the number of points is greater than 2 it is a triangle
      if(numberOfIds > 2)
        {
        vtkSmartPointer<vtkPolygon> polygon (vtkSmartPointer<vtkPolygon>::New());
        for(int j=0; j < numberOfIds; ++j)
          {
          int id = -1;
          if(exists[list->GetId(j)] == -1)
            {
            double pt[3];
            polyDataIn->GetPoints()->GetPoint(list->GetId(j),pt);
            vtkIdType ptId = points->InsertNextPoint(pt);
            exists[list->GetId(j)] = ptId;
            id = ptId;
            }
          else
            {
            id = exists[list->GetId(j)];
            }

          polygon->GetPointIds()->InsertNextId(id);
          }
        polys->InsertNextCell(polygon);

        // We want to insert the same value for all the cells.
        polygonIds->InsertNextValue(lastPolygonId + 1);
        }
     }
   }
  else
    {
    std::cerr << "Failed to tesselate the polygon." << std::endl;
    }
}


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

  (this->FileName ?
    (os << indent << "FileName: " << this->FileName) :
    (os << indent << "FileName: NULL"));
}

//-----------------------------------------------------------------------------
vtkKMLReader::vtkKMLReader()
{
  this->Implementation = new vtkKMLReaderInternal();

  this->FileName = 0;

  // This is a source.
  this->SetNumberOfInputPorts(0);
}

//-----------------------------------------------------------------------------
vtkKMLReader::~vtkKMLReader()
{
  this->SetFileName(0);

  delete this->Implementation;
}

//-----------------------------------------------------------------------------
int vtkKMLReader::RequestData(vtkInformation* vtkNotUsed(request),
                              vtkInformationVector** vtkNotUsed(inputVector),
                              vtkInformationVector* outputVector)
{
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  if(!outInfo)
    {
    vtkErrorMacro("Invalid output information.\n");
    return 1;
    }

  vtkDataObject* outDataObj = outInfo->Get(vtkDataObject::DATA_OBJECT());
  if(!outDataObj)
    {
    vtkErrorMacro("Invalid output data object.\n");
    return 1;
    }

  vtkPolyData* outPolyData  = vtkPolyData::SafeDownCast(outDataObj);
  if(!outPolyData)
    {
    vtkErrorMacro("Invalid polydata.\n");
    return 1;
    }

  if(!this->FileName)
    {
    vtkErrorMacro("Requires input filename.\n");
    return 1;
    }

  vtkPolyData* out =
    this->Implementation->HandleFile(this->FileName);
  if(!out)
    {
    vtkErrorMacro("Failed to create valid output.\n");
    return 1;
    }

  outPolyData->ShallowCopy(out);

  return 1;
}
