text: /* ========================================================================= *
   *                                                                           *
   *                               OpenMesh                                    *
   *           Copyright (c) 2001-2015, RWTH-Aachen University                 *
   *           Department of Computer Graphics and Multimedia                  *
   *                          All rights reserved.                             *
   *                            www.openmesh.org                               *
   *                                                                           *
   *---------------------------------------------------------------------------*
   * This file is part of OpenMesh.                                            *
   *---------------------------------------------------------------------------*
   *                                                                           *
   * Redistribution and use in source and binary forms, with or without        *
   * modification, are permitted provided that the following conditions        *
   * are met:                                                                  *
   *                                                                           *
   * 1. Redistributions of source code must retain the above copyright notice, *
   *    this list of conditions and the following disclaimer.                  *
   *                                                                           *
   * 2. Redistributions in binary form must reproduce the above copyright      *
   *    notice, this list of conditions and the following disclaimer in the    *
   *    documentation and/or other materials provided with the distribution.   *
   *                                                                           *
   * 3. Neither the name of the copyright holder nor the names of its          *
   *    contributors may be used to endorse or promote products derived from   *
   *    this software without specific prior written permission.               *
   *                                                                           *
   * 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 COPYRIGHT HOLDER *
   * 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.              *
   *                                                                           *
   * ========================================================================= */
  
  /*===========================================================================*\
   *                                                                           *
   *   $Revision$                                                         *
   *   $Date$                   *
   *                                                                           *
   \*===========================================================================*/
  
  #define LINE_LEN 4096
  
  //== INCLUDES =================================================================
  
  // OpenMesh
  #include <OpenMesh/Core/System/config.h>
  #include <OpenMesh/Core/System/omstream.hh>
  #include <OpenMesh/Core/IO/reader/PLYReader.hh>
  #include <OpenMesh/Core/IO/IOManager.hh>
  #include <OpenMesh/Core/Utils/color_cast.hh>
  
  //STL
  #include <fstream>
  #include <iostream>
  #include <memory>
  
  #ifndef WIN32
  #endif
  
  //=== NAMESPACES ==============================================================
  
  
  namespace OpenMesh {
  namespace IO {
  
  //=============================================================================
  
  //=== INSTANCIATE =============================================================
  
  
  _PLYReader_ __PLYReaderInstance;
  _PLYReader_& PLYReader() {
      return __PLYReaderInstance;
  }
  
  //=== IMPLEMENTATION ==========================================================
  
  
  _PLYReader_::_PLYReader_() {
      IOManager().register_module(this);
  
      // Store sizes in byte of each property type
      scalar_size_[ValueTypeINT8] = 1;
      scalar_size_[ValueTypeUINT8] = 1;
      scalar_size_[ValueTypeINT16] = 2;
      scalar_size_[ValueTypeUINT16] = 2;
      scalar_size_[ValueTypeINT32] = 4;
      scalar_size_[ValueTypeUINT32] = 4;
      scalar_size_[ValueTypeFLOAT32] = 4;
      scalar_size_[ValueTypeFLOAT64] = 8;
  
      scalar_size_[ValueTypeCHAR] = 1;
      scalar_size_[ValueTypeUCHAR] = 1;
      scalar_size_[ValueTypeSHORT] = 2;
      scalar_size_[ValueTypeUSHORT] = 2;
      scalar_size_[ValueTypeINT] = 4;
      scalar_size_[ValueTypeUINT] = 4;
      scalar_size_[ValueTypeFLOAT] = 4;
      scalar_size_[ValueTypeDOUBLE] = 8;
  }
  
  //-----------------------------------------------------------------------------
  
  
  bool _PLYReader_::read(const std::string& _filename, BaseImporter& _bi, Options& _opt) {
  
      std::fstream in(_filename.c_str(), (std::ios_base::binary | std::ios_base::in) );
  
      if (!in.is_open() || !in.good()) {
          omerr() << "[PLYReader] : cannot not open file " << _filename << std::endl;
          return false;
      }
  
      bool result = read(in, _bi, _opt);
  
      in.close();
      return result;
  }
  
  //-----------------------------------------------------------------------------
  
  
  bool _PLYReader_::read(std::istream& _in, BaseImporter& _bi, Options& _opt) {
  
      if (!_in.good()) {
          omerr() << "[PLYReader] : cannot not use stream" << std::endl;
          return false;
      }
  
      // filter relevant options for reading
      bool swap = _opt.check(Options::Swap);
  
      userOptions_ = _opt;
  
      // build options to be returned
      _opt.clear();
  
      if (options_.vertex_has_normal() && userOptions_.vertex_has_normal()) {
          _opt += Options::VertexNormal;
      }
      if (options_.vertex_has_texcoord() && userOptions_.vertex_has_texcoord()) {
          _opt += Options::VertexTexCoord;
      }
      if (options_.vertex_has_color() && userOptions_.vertex_has_color()) {
          _opt += Options::VertexColor;
      }
      if (options_.face_has_color() && userOptions_.face_has_color()) {
          _opt += Options::FaceColor;
      }
      if (options_.is_binary()) {
          _opt += Options::Binary;
      }
      if (options_.color_is_float()) {
          _opt += Options::ColorFloat;
      }
      if (options_.check(Options::Custom) && userOptions_.check(Options::Custom)) {
          _opt += Options::Custom;
      }
  
      //    //force user-choice for the alpha value when reading binary
      //    if ( options_.is_binary() && userOptions_.color_has_alpha() )
      //      options_ += Options::ColorAlpha;
  
      return (options_.is_binary() ? read_binary(_in, _bi, swap, _opt) : read_ascii(_in, _bi, _opt));
  
  }
  
  template<typename T, typename Handle>
  struct Handle2Prop;
  
  template<typename T>
  struct Handle2Prop<T,VertexHandle>
  {
    typedef OpenMesh::VPropHandleT<T> PropT;
  };
  
  template<typename T>
  struct Handle2Prop<T,FaceHandle>
  {
    typedef OpenMesh::FPropHandleT<T> PropT;
  };
  
  //read and assign custom properties with the given type. Also creates property, if not exist
  template<bool binary, typename T, typename Handle>
  void _PLYReader_::readCreateCustomProperty(std::istream& _in, BaseImporter& _bi, Handle _h, const std::string& _propName, const _PLYReader_::ValueType _valueType, const _PLYReader_::ValueType _listType) const
  {
    if (_listType == Unsupported) //no list type defined -> property is not a list
    {
      //get/add property
      typename Handle2Prop<T,Handle>::PropT prop;
      if (!_bi.kernel()->get_property_handle(prop,_propName))
      {
        _bi.kernel()->add_property(prop,_propName);
        _bi.kernel()->property(prop).set_persistent(true);
      }
  
      //read and assign
      T in;
      read(_valueType, _in, in, OpenMesh::GenProg::Bool2Type<binary>());
      _bi.kernel()->property(prop,_h) = in;
    }
    else
    {
      //get/add property
      typename Handle2Prop<std::vector<T>,Handle>::PropT prop;
      if (!_bi.kernel()->get_property_handle(prop,_propName))
      {
        _bi.kernel()->add_property(prop,_propName);
        _bi.kernel()->property(prop).set_persistent(true);
      }
  
      //init vector
      int numberOfValues;
      read(_listType, _in, numberOfValues, OpenMesh::GenProg::Bool2Type<binary>());
      std::vector<T> vec;
      vec.reserve(numberOfValues);
      //read and assign
      for (int i = 0; i < numberOfValues; ++i)
      {
        T in;
        read(_valueType, _in, in, OpenMesh::GenProg::Bool2Type<binary>());
        vec.push_back(in);
      }
      _bi.kernel()->property(prop,_h) = vec;
    }
  }
  
  template<bool binary, typename Handle>
  void _PLYReader_::readCustomProperty(std::istream& _in, BaseImporter& _bi, Handle _h, const std::string& _propName, const _PLYReader_::ValueType _valueType, const _PLYReader_::ValueType _listIndexType) const
  {
    switch (_valueType)
    {
    case ValueTypeINT8:
    case ValueTypeCHAR:
        readCreateCustomProperty<binary,signed char>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeUINT8:
    case ValueTypeUCHAR:
        readCreateCustomProperty<binary,unsigned char>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeINT16:
    case ValueTypeSHORT:
        readCreateCustomProperty<binary,short>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeUINT16:
    case ValueTypeUSHORT:
        readCreateCustomProperty<binary,unsigned short>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeINT32:
    case ValueTypeINT:
        readCreateCustomProperty<binary,int>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeUINT32:
    case ValueTypeUINT:
        readCreateCustomProperty<binary,unsigned int>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeFLOAT32:
    case ValueTypeFLOAT:
        readCreateCustomProperty<binary,float>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    case ValueTypeFLOAT64:
    case ValueTypeDOUBLE:
        readCreateCustomProperty<binary,double>(_in,_bi,_h,_propName,_valueType,_listIndexType);
        break;
    default:
        std::cerr << "unsupported type" << std::endl;
        break;
    }
  }
  
  
  //-----------------------------------------------------------------------------
  
  bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options& _opt) const {
  
      // Reparse the header
      if (!can_u_read(_in)) {
          omerr() << "[PLYReader] : Unable to parse header\n";
          return false;
      }
  
      unsigned int i, j, k, l, idx;
      unsigned int nV;
      OpenMesh::Vec3f v, n;
      std::string trash;
      OpenMesh::Vec2f t;
      OpenMesh::Vec4i c;
      float tmp;
      BaseImporter::VHandles vhandles;
      VertexHandle vh;
  
      _bi.reserve(vertexCount_, 3* vertexCount_ , faceCount_);
  
      if (vertexDimension_ != 3) {
          omerr() << "[PLYReader] : Only vertex dimension 3 is supported." << std::endl;
          return false;
      }
  
      const bool err_enabled = omerr().is_enabled();
      size_t complex_faces = 0;
      if (err_enabled)
        omerr().disable();
  
      // read vertices:
      for (i = 0; i < vertexCount_ && !_in.eof(); ++i) {
          vh = _bi.add_vertex();
  
          v[0] = 0.0;
          v[1] = 0.0;
          v[2] = 0.0;
  
          n[0] = 0.0;
          n[1] = 0.0;
          n[2] = 0.0;
  
          t[0] = 0.0;
          t[1] = 0.0;
  
          c[0] = 0;
          c[1] = 0;
          c[2] = 0;
          c[3] = 255;
  
          for (size_t propertyIndex = 0; propertyIndex < vertexProperties_.size(); ++propertyIndex) {
              switch (vertexProperties_[propertyIndex].property) {
              case XCOORD:
                  _in >> v[0];
                  break;
              case YCOORD:
                  _in >> v[1];
                  break;
              case ZCOORD:
                  _in >> v[2];
                  break;
              case XNORM:
                  _in >> n[0];
                  break;
              case YNORM:
                  _in >> n[1];
                  break;
              case ZNORM:
                  _in >> n[2];
                  break;
              case TEXX:
                  _in >> t[0];
                  break;
              case TEXY:
                  _in >> t[1];
                  break;
              case COLORRED:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      _in >> tmp;
                      c[0] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      _in >> c[0];
                  break;
              case COLORGREEN:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      _in >> tmp;
                      c[1] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      _in >> c[1];
                  break;
              case COLORBLUE:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      _in >> tmp;
                      c[2] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      _in >> c[2];
                  break;
              case COLORALPHA:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      _in >> tmp;
                      c[3] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      _in >> c[3];
                  break;
              case CUSTOM_PROP:
                  if (_opt.check(Options::Custom))
                    readCustomProperty<false>(_in, _bi, vh, vertexProperties_[propertyIndex].name, vertexProperties_[propertyIndex].value, vertexProperties_[propertyIndex].listIndexType);
                  else
                    _in >> trash;
                  break;
              default:
                  _in >> trash;
                  break;
              }
          }
  
          _bi.set_point(vh, v);
          if (_opt.vertex_has_normal())
            _bi.set_normal(vh, n);
          if (_opt.vertex_has_texcoord())
            _bi.set_texcoord(vh, t);
          if (_opt.vertex_has_color())
            _bi.set_color(vh, Vec4uc(c));
      }
  
      // faces
      for (i = 0; i < faceCount_; ++i) {
        FaceHandle fh;
        for (size_t propertyIndex = 0; propertyIndex < faceProperties_.size(); ++propertyIndex) {
          PropertyInfo prop = faceProperties_[propertyIndex];
          switch (prop.property) {
  
          case VERTEX_INDICES:
            // nV = number of Vertices for current face
            _in >> nV;
  
            if (nV == 3) {
              vhandles.resize(3);
              _in >> j;
              _in >> k;
              _in >> l;
  
              vhandles[0] = VertexHandle(j);
              vhandles[1] = VertexHandle(k);
              vhandles[2] = VertexHandle(l);
            } else {
              vhandles.clear();
              for (j = 0; j < nV; ++j) {
                _in >> idx;
                vhandles.push_back(VertexHandle(idx));
              }
            }
  
            fh = _bi.add_face(vhandles);
            if (!fh.is_valid())
              ++complex_faces;
            break;
  
          case CUSTOM_PROP:
            if (_opt.check(Options::Custom) && fh.is_valid())
              readCustomProperty<false>(_in, _bi, fh, prop.name, prop.value, prop.listIndexType);
            else
              _in >> trash;
            break;
  
          default:
            _in >> trash;
            break;
          }
        }
  
      }
  
      if (err_enabled) 
        omerr().enable();
  
      if (complex_faces)
        omerr() << complex_faces << "The reader encountered invalid faces, that could not be added.\n";
  
      // File was successfully parsed.
      return true;
  }
  
  //-----------------------------------------------------------------------------
  
  bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap*/, const Options& _opt) const {
  
      // Reparse the header
      if (!can_u_read(_in)) {
          omerr() << "[PLYReader] : Unable to parse header\n";
          return false;
      }
  
      OpenMesh::Vec3f        v, n;  // Vertex
      OpenMesh::Vec2f        t;  // TexCoords
      BaseImporter::VHandles vhandles;
      VertexHandle           vh;
      OpenMesh::Vec4i        c;  // Color
      float                  tmp;
  
      _bi.reserve(vertexCount_, 3* vertexCount_ , faceCount_);
  
      const bool err_enabled = omerr().is_enabled();
      size_t complex_faces = 0;
      if (err_enabled)
        omerr().disable();
  
      // read vertices:
      for (unsigned int i = 0; i < vertexCount_ && !_in.eof(); ++i) {
          vh = _bi.add_vertex();
  
          v[0] = 0.0;
          v[1] = 0.0;
          v[2] = 0.0;
  
          n[0] = 0.0;
          n[1] = 0.0;
          n[2] = 0.0;
  
          t[0] = 0.0;
          t[1] = 0.0;
  
          c[0] = 0;
          c[1] = 0;
          c[2] = 0;
          c[3] = 255;
  
          for (size_t propertyIndex = 0; propertyIndex < vertexProperties_.size(); ++propertyIndex) {
              switch (vertexProperties_[propertyIndex].property) {
              case XCOORD:
                  readValue(vertexProperties_[propertyIndex].value, _in, v[0]);
                  break;
              case YCOORD:
                  readValue(vertexProperties_[propertyIndex].value, _in, v[1]);
                  break;
              case ZCOORD:
                  readValue(vertexProperties_[propertyIndex].value, _in, v[2]);
                  break;
              case XNORM:
                  readValue(vertexProperties_[propertyIndex].value, _in, n[0]);
                  break;
              case YNORM:
                  readValue(vertexProperties_[propertyIndex].value, _in, n[1]);
                  break;
              case ZNORM:
                  readValue(vertexProperties_[propertyIndex].value, _in, n[2]);
                  break;
              case TEXX:
                  readValue(vertexProperties_[propertyIndex].value, _in, t[0]);
                  break;
              case TEXY:
                  readValue(vertexProperties_[propertyIndex].value, _in, t[1]);
                  break;
              case COLORRED:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      readValue(vertexProperties_[propertyIndex].value, _in, tmp);
  
                      c[0] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      readInteger(vertexProperties_[propertyIndex].value, _in, c[0]);
  
                  break;
              case COLORGREEN:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      readValue(vertexProperties_[propertyIndex].value, _in, tmp);
                      c[1] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      readInteger(vertexProperties_[propertyIndex].value, _in, c[1]);
  
                  break;
              case COLORBLUE:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      readValue(vertexProperties_[propertyIndex].value, _in, tmp);
                      c[2] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      readInteger(vertexProperties_[propertyIndex].value, _in, c[2]);
  
                  break;
              case COLORALPHA:
                  if (vertexProperties_[propertyIndex].value == ValueTypeFLOAT32 ||
                      vertexProperties_[propertyIndex].value == ValueTypeFLOAT) {
                      readValue(vertexProperties_[propertyIndex].value, _in, tmp);
                      c[3] = static_cast<OpenMesh::Vec4i::value_type> (tmp * 255.0f);
                  } else
                      readInteger(vertexProperties_[propertyIndex].value, _in, c[3]);
  
                  break;
              case CUSTOM_PROP:
                if (_opt.check(Options::Custom))
                  readCustomProperty<true>(_in, _bi, vh, vertexProperties_[propertyIndex].name, vertexProperties_[propertyIndex].value, vertexProperties_[propertyIndex].listIndexType);
                else
                  consume_input(_in, scalar_size_[vertexProperties_[propertyIndex].value]);
                break;
              default:
                  // Read unsupported property
                  consume_input(_in, scalar_size_[vertexProperties_[propertyIndex].value]);
                  break;
              }
  
          }
  
          _bi.set_point(vh,v);
          if (_opt.vertex_has_normal())
            _bi.set_normal(vh, n);
          if (_opt.vertex_has_texcoord())
            _bi.set_texcoord(vh, t);
          if (_opt.vertex_has_color())
            _bi.set_color(vh, Vec4uc(c));
      }
  
      for (unsigned i = 0; i < faceCount_; ++i) {
        FaceHandle fh;
        for (size_t propertyIndex = 0; propertyIndex < faceProperties_.size(); ++propertyIndex)
        {
          PropertyInfo prop = faceProperties_[propertyIndex];
          switch (prop.property) {
  
          case VERTEX_INDICES:
            // nV = number of Vertices for current face
            unsigned int nV;
            readInteger(prop.listIndexType, _in, nV);
  
            if (nV == 3) {
              vhandles.resize(3);
              unsigned int j,k,l;
              readInteger(prop.value, _in, j);
              readInteger(prop.value, _in, k);
              readInteger(prop.value, _in, l);
  
              vhandles[0] = VertexHandle(j);
              vhandles[1] = VertexHandle(k);
              vhandles[2] = VertexHandle(l);
            } else {
              vhandles.clear();
              for (unsigned j = 0; j < nV; ++j) {
                unsigned int idx;
                readInteger(prop.value, _in, idx);
                vhandles.push_back(VertexHandle(idx));
              }
            }
  
            fh = _bi.add_face(vhandles);
            if (!fh.is_valid())
              ++complex_faces;
            break;
  
          case CUSTOM_PROP:
            if (_opt.check(Options::Custom) && fh.is_valid())
              readCustomProperty<true>(_in, _bi, fh, prop.name, prop.value, prop.listIndexType);
            else
              consume_input(_in, scalar_size_[faceProperties_[propertyIndex].value]);
            break;
  
          default:
            consume_input(_in, scalar_size_[faceProperties_[propertyIndex].value]);
            break;
          }
        }
      }
  
      if (err_enabled) 
        omerr().enable();
  
     if (complex_faces)
        omerr() << complex_faces << "The reader encountered invalid faces, that could not be added.\n";
  
  
      return true;
  }
  
  
  //-----------------------------------------------------------------------------
  
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, float& _value) const {
  
      switch (_type) {
      case ValueTypeFLOAT32:
      case ValueTypeFLOAT:
          float32_t tmp;
          restore(_in, tmp, options_.check(Options::MSB));
          _value = tmp;
          break;
      default:
          _value = 0.0;
          std::cerr << "unsupported conversion type to float: " << _type << std::endl;
          break;
      }
  }
  
  
  //-----------------------------------------------------------------------------
  
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, double& _value) const {
  
      switch (_type) {
  
          case ValueTypeFLOAT64:
  
          case ValueTypeDOUBLE:
  
              float64_t tmp;
              restore(_in, tmp, options_.check(Options::MSB));
              _value = tmp;
  
              break;
  
      default:
  
          _value = 0.0;
          std::cerr << "unsupported conversion type to double: " << _type << std::endl;
  
          break;
      }
  }
  
  
  //-----------------------------------------------------------------------------
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, unsigned char& _value) const{
    unsigned int tmp;
    readValue(_type,_in,tmp);
    _value = tmp;
  }
  
  //-----------------------------------------------------------------------------
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, unsigned short& _value) const{
    unsigned int tmp;
    readValue(_type,_in,tmp);
    _value = tmp;
  }
  
  //-----------------------------------------------------------------------------
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, signed char& _value) const{
    int tmp;
    readValue(_type,_in,tmp);
    _value = tmp;
  }
  
  //-----------------------------------------------------------------------------
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, short& _value) const{
    int tmp;
    readValue(_type,_in,tmp);
    _value = tmp;
  }
  
  
  //-----------------------------------------------------------------------------
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, unsigned int& _value) const {
  
      uint32_t tmp_uint32_t;
      uint16_t tmp_uint16_t;
      uint8_t tmp_uchar;
  
      switch (_type) {
  
          case ValueTypeUINT:
  
          case ValueTypeUINT32:
  
              restore(_in, tmp_uint32_t, options_.check(Options::MSB));
              _value = tmp_uint32_t;
  
          break;
  
          case ValueTypeUSHORT:
  
          case ValueTypeUINT16:
  
              restore(_in, tmp_uint16_t, options_.check(Options::MSB));
              _value = tmp_uint16_t;
  
              break;
  
          case ValueTypeUCHAR:
  
          case ValueTypeUINT8:
  
              restore(_in, tmp_uchar, options_.check(Options::MSB));
              _value = tmp_uchar;
  
              break;
  
          default:
  
              _value = 0;
              std::cerr << "unsupported conversion type to unsigned int: " << _type << std::endl;
  
              break;
      }
  }
  
  
  //-----------------------------------------------------------------------------
  
  
  void _PLYReader_::readValue(ValueType _type, std::istream& _in, int& _value) const {
  
      int32_t tmp_int32_t;
      int16_t tmp_int16_t;
      int8_t tmp_char;
  
      switch (_type) {
  
          case ValueTypeINT:
  
          case ValueTypeINT32:
  
              restore(_in, tmp_int32_t, options_.check(Options::MSB));
              _value = tmp_int32_t;
  
              break;
  
          case ValueTypeSHORT:
  
          case ValueTypeINT16:
  
              restore(_in, tmp_int16_t, options_.check(Options::MSB));
              _value = tmp_int16_t;
  
              break;
  
          case ValueTypeCHAR:
  
          case ValueTypeINT8:
  
              restore(_in, tmp_char, options_.check(Options::MSB));
              _value = tmp_char;
  
              break;
  
          default:
  
              _value = 0;
              std::cerr << "unsupported conversion type to int: " << _type << std::endl;
  
              break;
      }
  }
  
  
  //-----------------------------------------------------------------------------
  
  
  void _PLYReader_::readInteger(ValueType _type, std::istream& _in, int& _value) const {
  
      int32_t tmp_int32_t;
      uint32_t tmp_uint32_t;
      int8_t tmp_char;
      uint8_t tmp_uchar;
  
      switch (_type) {
  
          case ValueTypeINT:
  
          case ValueTypeINT32:
  
              restore(_in, tmp_int32_t, options_.check(Options::MSB));
              _value = tmp_int32_t;
  
              break;
  
          case ValueTypeUINT:
  
          case ValueTypeUINT32:
  
              restore(_in, tmp_uint32_t, options_.check(Options::MSB));
              _value = tmp_uint32_t;
  
              break;
  
          case ValueTypeCHAR:
  
          case ValueTypeINT8:
  
              restore(_in, tmp_char, options_.check(Options::MSB));
              _value = tmp_char;
  
              break;
  
          case ValueTypeUCHAR:
  
          case ValueTypeUINT8:
  
              restore(_in, tmp_uchar, options_.check(Options::MSB));
              _value = tmp_uchar;
  
              break;
  
          default:
  
              _value = 0;
              std::cerr << "unsupported conversion type to int: " << _type << std::endl;
  
              break;
      }
  }
  
  
  //-----------------------------------------------------------------------------
  
  
  void _PLYReader_::readInteger(ValueType _type, std::istream& _in, unsigned int& _value) const {
  
      int32_t tmp_int32_t;
      uint32_t tmp_uint32_t;
      int8_t tmp_char;
      uint8_t tmp_uchar;
  
      switch (_type) {
  
          case ValueTypeUINT:
  
          case ValueTypeUINT32:
  
              restore(_in, tmp_uint32_t, options_.check(Options::MSB));
              _value = tmp_uint32_t;
  
              break;
  
          case ValueTypeINT:
  
          case ValueTypeINT32:
  
              restore(_in, tmp_int32_t, options_.check(Options::MSB));
              _value = tmp_int32_t;
  
              break;
  
          case ValueTypeUCHAR:
  
          case ValueTypeUINT8:
  
              restore(_in, tmp_uchar, options_.check(Options::MSB));
              _value = tmp_uchar;
  
              break;
  
          case ValueTypeCHAR:
  
          case ValueTypeINT8:
  
              restore(_in, tmp_char, options_.check(Options::MSB));
              _value = tmp_char;
  
              break;
  
          default:
  
              _value = 0;
              std::cerr << "unsupported conversion type to unsigned int: " << _type << std::endl;
  
              break;
      }
  }
  
  
  //------------------------------------------------------------------------------
  
  
  bool _PLYReader_::can_u_read(const std::string& _filename) const {
  
      // !!! Assuming BaseReader::can_u_parse( std::string& )
      // does not call BaseReader::read_magic()!!!
  
      if (BaseReader::can_u_read(_filename)) {
          std::ifstream ifs(_filename.c_str());
          if (ifs.is_open() && can_u_read(ifs)) {
              ifs.close();
              return true;
          }
      }
      return false;
  }
  
  
  
  //-----------------------------------------------------------------------------
  
  std::string get_property_name(std::string _string1, std::string _string2) {
  
      if (_string1 == "float32" || _string1 == "float64" || _string1 == "float" || _string1 == "double" ||
          _string1 == "int8" || _string1 == "uint8" || _string1 == "char" || _string1 == "uchar" ||
          _string1 == "int32" || _string1 == "uint32" || _string1 == "int" || _string1 == "uint" ||
          _string1 == "int16" || _string1 == "uint16" || _string1 == "short" || _string1 == "ushort")
          return _string2;
  
      if (_string2 == "float32" || _string2 == "float64" || _string2 == "float" || _string2 == "double" ||
          _string2 == "int8" || _string2 == "uint8" || _string2 == "char" || _string2 == "uchar" ||
          _string2 == "int32" || _string2 == "uint32" || _string2 == "int" || _string2 == "uint" ||
          _string2 == "int16" || _string2 == "uint16" || _string2 == "short" || _string2 == "ushort")
          return _string1;
  
  
      std::cerr << "Unsupported entry type" << std::endl;
      return "Unsupported";
  }
  
  //-----------------------------------------------------------------------------
  
  _PLYReader_::ValueType get_property_type(std::string _string1, std::string _string2) {
  
      if (_string1 == "float32" || _string2 == "float32")
  
          return _PLYReader_::ValueTypeFLOAT32;
  
      else if (_string1 == "float64" || _string2 == "float64")
  
          return _PLYReader_::ValueTypeFLOAT64;
  
      else if (_string1 == "float" || _string2 == "float")
  
          return _PLYReader_::ValueTypeFLOAT;
  
      else if (_string1 == "double" || _string2 == "double")
  
          return _PLYReader_::ValueTypeDOUBLE;
  
      else if (_string1 == "int8" || _string2 == "int8")
  
          return _PLYReader_::ValueTypeINT8;
  
      else if (_string1 == "uint8" || _string2 == "uint8")
  
          return _PLYReader_::ValueTypeUINT8;
  
      else if (_string1 == "char" || _string2 == "char")
  
          return _PLYReader_::ValueTypeCHAR;
  
      else if (_string1 == "uchar" || _string2 == "uchar")
  
          return _PLYReader_::ValueTypeUCHAR;
  
      else if (_string1 == "int32" || _string2 == "int32")
  
          return _PLYReader_::ValueTypeINT32;
  
      else if (_string1 == "uint32" || _string2 == "uint32")
  
          return _PLYReader_::ValueTypeUINT32;
  
      else if (_string1 == "int" || _string2 == "int")
  
          return _PLYReader_::ValueTypeINT;
  
      else if (_string1 == "uint" || _string2 == "uint")
  
          return _PLYReader_::ValueTypeUINT;
  
      else if (_string1 == "int16" || _string2 == "int16")
  
          return _PLYReader_::ValueTypeINT16;
  
      else if (_string1 == "uint16" || _string2 == "uint16")
  
          return _PLYReader_::ValueTypeUINT16;
  
      else if (_string1 == "short" || _string2 == "short")
  
          return _PLYReader_::ValueTypeSHORT;
  
      else if (_string1 == "ushort" || _string2 == "ushort")
  
          return _PLYReader_::ValueTypeUSHORT;
  
      return _PLYReader_::Unsupported;
  }
  
  
  //-----------------------------------------------------------------------------
  
  bool _PLYReader_::can_u_read(std::istream& _is) const {
  
      // Clear per file options
      options_.cleanup();
  
      // clear property maps, will be recreated
      vertexProperties_.clear();
      faceProperties_.clear();
  
      // read 1st line
      std::string line;
      std::getline(_is, line);
      trim(line);
  
      // Handle '\r\n' newlines 
      const size_t s = line.size();
      if( s > 0 && line[s - 1] == '\r') line.resize(s - 1); 
  
      //Check if this file is really a ply format
      if (line != "PLY" && line != "ply")
          return false;
  
      vertexCount_ = 0;
      faceCount_ = 0;
      vertexDimension_ = 0;
  
      std::string keyword;
      std::string fileType;
      std::string elementName = "";
      std::string propertyName;
      std::string listIndexType;
      std::string listEntryType;
      float version;
  
      _is >> keyword;
      _is >> fileType;
      _is >> version;
  
      if (_is.bad()) {
          omerr() << "Defect PLY header detected" << std::endl;
          return false;
      }
  
      if (fileType == "ascii") {
          options_ -= Options::Binary;
      } else if (fileType == "binary_little_endian") {
          options_ += Options::Binary;
          options_ += Options::LSB;
          //if (Endian::local() == Endian::MSB)
  
          //  options_ += Options::Swap;
      } else if (fileType == "binary_big_endian") {
          options_ += Options::Binary;
          options_ += Options::MSB;
          //if (Endian::local() == Endian::LSB)
  
          //  options_ += Options::Swap;
      } else {
          omerr() << "Unsupported PLY format: " << fileType << std::endl;
          return false;
      }
  
      std::streamoff streamPos = _is.tellg();
      _is >> keyword;
      while (keyword != "end_header") {
  
          if (keyword == "comment") {
              std::getline(_is, line);
          } else if (keyword == "element") {
              _is >> elementName;
              if (elementName == "vertex") {
                  _is >> vertexCount_;
              } else if (elementName == "face") {
                  _is >> faceCount_;
              } else {
                  omerr() << "PLY header unsupported element type: " << elementName << std::endl;
              }
          } else if (keyword == "property") {
              std::string tmp1;
              std::string tmp2;
  
              // Read first keyword, as it might be a list
              _is >> tmp1;
  
              if (tmp1 == "list") {
                _is >> listIndexType;
                _is >> listEntryType;
                _is >> propertyName;
  
                ValueType indexType = Unsupported;
                ValueType entryType = Unsupported;
  
                if (listIndexType == "uint8") {
                  indexType = ValueTypeUINT8;
                } else if (listIndexType == "uchar") {
                  indexType = ValueTypeUCHAR;
                } else if (listIndexType == "int") {
                  indexType = ValueTypeINT;
                } else {
                  omerr() << "Unsupported Index type for property list: " << listIndexType << std::endl;
                  continue;
                }
  
                entryType = get_property_type(listEntryType, listEntryType);
  
                if (entryType == Unsupported) {
                  omerr() << "Unsupported Entry type for property list: " << listEntryType << std::endl;
                }
  
                  PropertyInfo property(CUSTOM_PROP, entryType, propertyName);
                  property.listIndexType = indexType;
  
                  // just 2 elements supported by now
                  if (elementName == "vertex")
                  {
                    vertexProperties_.push_back(property);
                  }
                  else if (elementName == "face")
                  {
                    // special case for vertex indices
                    if (propertyName == "vertex_index" || propertyName == "vertex_indices")
                    {
                      property.property = VERTEX_INDICES;
                      if (!faceProperties_.empty())
                      {
                        omerr() << "Custom face Properties defined, before 'vertex_indices' property was defined. They will be skipped" << std::endl;
                        faceProperties_.clear();
                      }
                    }
                    faceProperties_.push_back(property);
  
                  }
                  else
                    omerr() << "property " << propertyName << " belongs to unsupported element " << elementName << std::endl;
  
              } else {
                // as this is not a list property, read second value of property
                _is >> tmp2;
  
  
                // Extract name and type of property
                // As the order seems to be different in some files, autodetect it.
                ValueType valueType = get_property_type(tmp1, tmp2);
                propertyName = get_property_name(tmp1, tmp2);
  
                PropertyInfo entry;
  
                //special treatment for some vertex properties.
                if (elementName == "vertex") {
                  if (propertyName == "x") {
                    entry = PropertyInfo(XCOORD, valueType);
                    vertexDimension_++;
                  } else if (propertyName == "y") {
                    entry = PropertyInfo(YCOORD, valueType);
                    vertexDimension_++;
                  } else if (propertyName == "z") {
                    entry = PropertyInfo(ZCOORD, valueType);
                    vertexDimension_++;
                  } else if (propertyName == "nx") {
                    entry = PropertyInfo(XNORM, valueType);
                    options_ += Options::VertexNormal;
                  } else if (propertyName == "ny") {
                    entry = PropertyInfo(YNORM, valueType);
                    options_ += Options::VertexNormal;
                  } else if (propertyName == "nz") {
                    entry = PropertyInfo(ZNORM, valueType);
                    options_ += Options::VertexNormal;
                  } else if (propertyName == "u" || propertyName == "s") {
                    entry = PropertyInfo(TEXX, valueType);
                    options_ += Options::VertexTexCoord;
                  } else if (propertyName == "v" || propertyName == "t") {
                    entry = PropertyInfo(TEXY, valueType);
                    options_ += Options::VertexTexCoord;
                  } else if (propertyName == "red") {
                    entry = PropertyInfo(COLORRED, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "green") {
                    entry = PropertyInfo(COLORGREEN, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "blue") {
                    entry = PropertyInfo(COLORBLUE, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "diffuse_red") {
                    entry = PropertyInfo(COLORRED, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "diffuse_green") {
                    entry = PropertyInfo(COLORGREEN, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "diffuse_blue") {
                    entry = PropertyInfo(COLORBLUE, valueType);
                    options_ += Options::VertexColor;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  } else if (propertyName == "alpha") {
                    entry = PropertyInfo(COLORALPHA, valueType);
                    options_ += Options::VertexColor;
                    options_ += Options::ColorAlpha;
                    if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                      options_ += Options::ColorFloat;
                  }
                }
  
                //not a special property, load as custom
                if (entry.value == Unsupported){
                  Property prop =  CUSTOM_PROP;
                  options_ += Options::Custom;
                  entry  = PropertyInfo(prop, valueType, propertyName);
                }
  
                if (entry.property != UNSUPPORTED)
                {
                  if (elementName == "vertex")
                    vertexProperties_.push_back(entry);
                  else if (elementName == "face")
                    faceProperties_.push_back(entry);
                  else
                    omerr() << "Properties not supported in element " << elementName << std::endl;
                }
  
              }
  
          } else {
              omlog() << "Unsupported keyword : " << keyword << std::endl;
          }
  
          streamPos = _is.tellg();
          _is >> keyword;
          if (_is.bad()) {
              omerr() << "Error while reading PLY file header" << std::endl;
              return false;
          }
      }
  
      // As the binary data is directy after the end_header keyword
      // and the stream removes too many bytes, seek back to the right position
      if (options_.is_binary()) {
          _is.seekg(streamPos);
  
          char c1 = 0;
          char c2 = 0;
          _is.get(c1);
          _is.get(c2);
  
          if (c1 == 0x0D && c2 == 0x0A) {
              _is.seekg(streamPos + 14);
          }
          else {
              _is.seekg(streamPos + 12);
          }
      }
  
      return true;
  }
  
  //=============================================================================
  } // namespace IO
  } // namespace OpenMesh
  //=============================================================================
  