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

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkPLYReader.cxx,v $
  Language:  C++
  Date:      $Date: 2003/02/11 13:42:57 $
  Version:   $Revision: 1.15 $

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen 
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPLYReader.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkPointData.h"
#include "vtkFloatArray.h"
#include "vtkObjectFactory.h"
#include "vtkPLY.h"
#include "vtkPolyData.h"

#include <ctype.h>
#include <stddef.h>

vtkCxxRevisionMacro(vtkPLYReader, "$Revision: 1.15 $");
vtkStandardNewMacro(vtkPLYReader);

#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif


// Construct object with merging set to true.
vtkPLYReader::vtkPLYReader()
{
  this->FileName = NULL;
}

vtkPLYReader::~vtkPLYReader()
{
  if (this->FileName)
    {
    delete [] this->FileName;
    }
}

typedef struct _plyVertex {
  float x[3];             // the usual 3-space position of a vertex
  unsigned char red;
  unsigned char green;
  unsigned char blue;
} plyVertex;

typedef struct _plyFace {
  unsigned char intensity; // optional face attributes
  unsigned char red;
  unsigned char green;
  unsigned char blue;
  unsigned char nverts;   // number of vertex indices in list
  int *verts;             // vertex index list
} plyFace;

void vtkPLYReader::Execute()
{
  PlyProperty vertProps[] = {
    {"x", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x[0])), 
     0, 0, 0, 0},
    {"y", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x[1])), 
     0, 0, 0, 0},
    {"z", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x[2])), 
     0, 0, 0, 0},
    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,red)), 0, 0, 0, 0},
    {"green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,green)), 0, 0, 0, 0},
    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,blue)), 0, 0, 0, 0},
  };
  PlyProperty faceProps[] = {
    {"vertex_indices", PLY_INT, PLY_INT, 
     static_cast<int>(offsetof(plyFace,verts)),
     1, PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,nverts))},
    {"intensity", PLY_UCHAR, PLY_UCHAR, 
     static_cast<int>(offsetof(plyFace,intensity)), 0, 0, 0, 0},
    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,red)), 0, 0, 0, 0},
    {"green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,green)), 0, 0, 0, 0},
    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,blue)), 0, 0, 0, 0},
  };

  int i, j, k;
  int numPts=0, numPolys=0;
  vtkPolyData *output = (vtkPolyData *)this->GetOutput();

  if (!this->FileName)
    {
    vtkErrorMacro(<<"A File Name must be specified.");
    return;
    }

  // open a PLY file for reading
  PlyFile *ply;
  int nelems, fileType, numElems, nprops;
  char **elist, *elemName;
  float version;
  
  if ( !(ply = vtkPLY::ply_open_for_reading(this->FileName, &nelems, &elist, 
                                            &fileType, &version)) )
    {
    vtkErrorMacro(<<"Could not open PLY file");
    return;
    }

  // Check to make sure that we can read geometry
  PlyElement *elem;
  int index;
  if ( (elem = vtkPLY::find_element (ply, "vertex")) == NULL ||
       vtkPLY::find_property (elem, "x", &index) == NULL ||
       vtkPLY::find_property (elem, "y", &index) == NULL ||
       vtkPLY::find_property (elem, "z", &index) == NULL ||
       (elem = vtkPLY::find_element (ply, "face")) == NULL ||
       vtkPLY::find_property (elem, "vertex_indices", &index) == NULL )
    {
    vtkErrorMacro(<<"Cannot read geometry");
    vtkPLY::ply_close (ply);
    }

  // Check for optional attribute data. We can handle intensity; and the
  // triplet red, green, blue.
  unsigned char intensityAvailable=false, RGBCellsAvailable=false, RGBPointsAvailable=false;
  vtkUnsignedCharArray *intensity=NULL;
  vtkUnsignedCharArray *RGBCells=NULL;
  vtkUnsignedCharArray *RGBPoints=NULL;
  if ( (elem = vtkPLY::find_element (ply, "face")) != NULL &&
       vtkPLY::find_property (elem, "intensity", &index) != NULL )
    {
    intensity = vtkUnsignedCharArray::New();
    intensity->SetName("intensity");
    intensityAvailable = true;
    output->GetCellData()->AddArray(intensity);
    output->GetCellData()->SetActiveScalars("intensity");
    intensity->Delete();
    }

  if ( (elem = vtkPLY::find_element (ply, "face")) != NULL &&
       vtkPLY::find_property (elem, "red", &index) != NULL &&
       vtkPLY::find_property (elem, "green", &index) != NULL &&
       vtkPLY::find_property (elem, "blue", &index) != NULL )
    {
    RGBCells = vtkUnsignedCharArray::New();
    RGBCells->SetName("RGB");
    RGBCellsAvailable = true;
    output->GetCellData()->AddArray(RGBCells);
    output->GetCellData()->SetActiveScalars("RGB");
    RGBCells->Delete();
    }

  if ( (elem = vtkPLY::find_element (ply, "vertex")) != NULL &&
       vtkPLY::find_property (elem, "red", &index) != NULL &&
       vtkPLY::find_property (elem, "green", &index) != NULL &&
       vtkPLY::find_property (elem, "blue", &index) != NULL )
    {
    RGBPoints = vtkUnsignedCharArray::New();
    RGBPointsAvailable = true;
    RGBPoints->SetName("RGB");
    output->GetPointData()->SetScalars(RGBPoints);
    RGBPoints->Delete();
    }

  // Okay, now we can grab the data
  for (i = 0; i < nelems; i++) 
    {
    //get the description of the first element */
    elemName = elist[i];
    vtkPLY::ply_get_element_description (ply, elemName, &numElems, &nprops);

    // if we're on vertex elements, read them in
    if ( elemName && !strcmp ("vertex", elemName) ) 
      {
      // Create a list of points
      numPts = numElems;
      vtkPoints *pts = vtkPoints::New();
      pts->SetDataTypeToFloat();
      pts->SetNumberOfPoints(numPts);
      
      // Setup to read the PLY elements
      vtkPLY::ply_get_property (ply, elemName, &vertProps[0]);
      vtkPLY::ply_get_property (ply, elemName, &vertProps[1]);
      vtkPLY::ply_get_property (ply, elemName, &vertProps[2]);

      if ( RGBPointsAvailable )
        {
        vtkPLY::ply_get_property (ply, elemName, &vertProps[3]);
        vtkPLY::ply_get_property (ply, elemName, &vertProps[4]);
        vtkPLY::ply_get_property (ply, elemName, &vertProps[5]);
        RGBPoints->SetNumberOfComponents(3);
        RGBPoints->SetNumberOfTuples(numPts);
        }
      plyVertex vertex;
      for (j=0; j < numPts; j++) 
        {
        vtkPLY::ply_get_element (ply, (void *) &vertex);
        pts->SetPoint (j, vertex.x);
        if ( RGBPointsAvailable )
          {
          RGBPoints->SetTuple3(j,vertex.red,vertex.green,vertex.blue);
          }
        }
      output->SetPoints(pts);
      pts->Delete();
      }//if vertex

    else if ( elemName && !strcmp ("face", elemName) ) 
      {
      // Create a polygonal array
      numPolys = numElems;
      vtkCellArray *polys = vtkCellArray::New();
      polys->Allocate(polys->EstimateSize(numPolys,3),numPolys/2);
      plyFace face;
      int verts[256];
      vtkIdType vtkVerts[256];

      // Get the face properties
      vtkPLY::ply_get_property (ply, elemName, &faceProps[0]);
      if ( intensityAvailable )
        {
        vtkPLY::ply_get_property (ply, elemName, &faceProps[1]);
        RGBCells->SetNumberOfComponents(1);
        RGBCells->SetNumberOfTuples(numPolys);
        }
      if ( RGBCellsAvailable )
        {
        vtkPLY::ply_get_property (ply, elemName, &faceProps[2]);
        vtkPLY::ply_get_property (ply, elemName, &faceProps[3]);
        vtkPLY::ply_get_property (ply, elemName, &faceProps[4]);
        RGBCells->SetNumberOfComponents(3);
        RGBCells->SetNumberOfTuples(numPolys);
        }
      
      // grab all the face elements
      for (j=0; j < numPolys; j++) 
        {
        //grab and element from the file
        face.verts = verts;
        vtkPLY::ply_get_element (ply, (void *) &face);
        for (k=0; k<face.nverts; k++)
          {
          vtkVerts[k] = face.verts[k];
          }
        polys->InsertNextCell(face.nverts,vtkVerts);
        if ( intensityAvailable )
          {
          intensity->SetValue(j,face.intensity);
          }
        if ( RGBCellsAvailable )
          {
          RGBCells->SetValue(3*j,face.red);
          RGBCells->SetValue(3*j+1,face.green);
          RGBCells->SetValue(3*j+2,face.blue);
          }
        }
      output->SetPolys(polys);
      polys->Delete();
      }//if face

    free(elist[i]); //allocated by ply_open_for_reading
    
    }//for all elements of the PLY file
  free(elist); //allocated by ply_open_for_reading
  
  vtkDebugMacro( <<"Read: " << numPts << " points, " 
                 << numPolys << " polygons");

  // close the PLY file 
  vtkPLY::ply_close (ply);

}

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

  os << indent << "File Name: " 
     << (this->FileName ? this->FileName : "(none)") << "\n";

}
