/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004-2016                                           \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#ifndef __VCG_POLYGON_COMPONENT
#define __VCG_POLYGON_COMPONENT

#include <cassert>
#include <vector>
#include <string>

namespace vcg {
namespace face {

/*-------------------------- PolInfo -------------------------------------------*/

template <class T> class PolyInfo: public T {
protected:
  inline void __SetVN(const int & n) {
    assert((_ns==-1) || (_ns==n) || (n==-1));
    _ns = n;
  }
public:
  PolyInfo(){ _ns = -1; }
  static bool HasPolyInfo()   { return true; }
  inline const int &  VN() const { return _ns;}
  inline int Prev(const int i){ return (i+(VN()-1))%VN();}
  inline int Next(const int i){ return (i+1)%VN();}
  inline void Alloc(const int ns){
    T::Alloc(ns);
    __SetVN(ns);
  }
  inline void Dealloc(){
    T::Dealloc();
    __SetVN(-1);
  }

private:
  int _ns;
};

template <class T> class PFVAdj: public T {
public:
  typedef typename  T::VertexType::CoordType CoordType;
  typedef typename  T::VertexType::ScalarType ScalarType;
  typedef typename  T::VertexType VertexType;

  PFVAdj(){ _vpoly = nullptr; }
  /* Note: the destructor will not be called in general because there are no virtual destructors.
   * Instead, the job of deallocating the memory will be done by the face allocator.
   * This destructor is only done for those who istance a face alone (outside a mesh)
   */
//  ~PFVAdj(){ __Dealloc(); }

  inline       typename T::VertexType * &  V( const int j )       { assert(j>=0 && j<this->VN()); return _vpoly[j]; }
  inline const typename T::VertexType *    V( const int j ) const { assert(j>=0 && j<this->VN()); return _vpoly[j]; }
  inline const typename T::VertexType *   cV( const int j ) const { assert(j>=0 && j<this->VN()); return _vpoly[j]; }


  /** Return the pointer to the ((j+1)%3)-th vertex of the face.
        @param j Index of the face vertex.
     */
  inline       VertexType * &  V0( const int j )       { return V(j);}
  inline       VertexType * &  V1( const int j )       { return V((j+1)%this->VN());}
  inline       VertexType * &  V2( const int j )       { return V((j+2)%this->VN());}
  inline const VertexType *    V0( const int j ) const { return V(j);}
  inline const VertexType *    V1( const int j ) const { return V((j+1)%this->VN());}
  inline const VertexType *    V2( const int j ) const { return V((j+2)%this->VN());}
  inline const VertexType *   cV0( const int j ) const { return cV(j);}
  inline const VertexType *   cV1( const int j ) const { return cV((j+1)%this->VN());}
  inline const VertexType *   cV2( const int j ) const { return cV((j+2)%this->VN());}

  inline       CoordType  &P( const int j )       { assert(j>=0 && j<this->VN()); return _vpoly[j]->P(); }
  inline       CoordType   P( const int j ) const { assert(j>=0 && j<this->VN()); return _vpoly[j]->cP(); }
  inline const CoordType &cP( const int j ) const { assert(j>=0 && j<this->VN()); return _vpoly[j]->cP(); }

  inline       CoordType  &P0( const int j )       { return V(j)->P();}
  inline       CoordType  &P1( const int j )       { return V((j+1)%this->VN())->P();}
  inline       CoordType  &P2( const int j )       { return V((j+2)%this->VN())->P();}
  inline       CoordType   P0( const int j ) const { return cV(j)->P();}
  inline       CoordType   P1( const int j ) const { return cV((j+1)%this->VN())->P();}
  inline       CoordType   P2( const int j ) const { return cV((j+2)%this->VN())->P();}
  inline const CoordType &cP0( const int j ) const { return cV(j)->P();}
  inline const CoordType &cP1( const int j ) const { return cV((j+1)%this->VN())->P();}
  inline const CoordType &cP2( const int j ) const { return cV((j+2)%this->VN())->P();}

  template <class LeftF>
  void ImportData(const LeftF & leftF){  T::ImportData(leftF);}
  inline void Alloc(const int & ns) {
    __Dealloc();
    _vpoly = new   typename T::VertexType*[ns];
    for(int i = 0; i < ns; ++i) _vpoly[i] = 0;
    T::Alloc(ns);
  }
  inline void Dealloc() {
    __Dealloc();
    T::Dealloc();
  }

  static bool HasVertexRef() { return true; }
  static bool HasFVAdjacency()   { return true; }
  static void Name(std::vector<std::string> & name){name.push_back(std::string("PFVAdj"));T::Name(name);}

private:
  inline void __Dealloc(){
    delete [] _vpoly;
    _vpoly = NULL;
  }

  typename T::VertexPointer *_vpoly;
};

template <class T> class PVFAdj: public T {
public:

  PVFAdj(){ _vfiP = NULL; _vfiP = NULL; }
  /* Note: the destructor will not be called in general because there are no virtual destructors.
   * Instead, the job of deallocating the memory will be done by the face allocator.
   * This destructor is only done for those who istance a face alone (outside a mesh)
   */
//  ~PVFAdj(){ __Dealloc(); }
  typedef typename T::VertexType VertexType;
  typedef typename T::FaceType FaceType;
  typename T::FacePointer       &VFp(const int j)        { assert(j>=0 && j<this->VN());  return _vfpP[j]; }
  typename T::FacePointer const  VFp(const int j) const  { assert(j>=0 && j<this->VN());  return _vfpP[j]; }
  typename T::FacePointer const cVFp(const int j) const  { assert(j>=0 && j<this->VN());  return _vfpP[j]; }
  char & VFi(const int j)       { return _vfiP[j]; }
  char   VFi(const int j) const { return _vfiP[j]; }
  char  cVFi(const int j) const { return _vfiP[j]; }
  template <class LeftF>
  void ImportData(const LeftF & leftF){T::ImportData(leftF);}
  inline void Alloc(const int & ns) {
    _vfpP = new  FaceType*[ns];
    _vfiP = new  char[ns];
    for(int i = 0; i < ns; ++i) {_vfpP[i] = 0;_vfiP[i] = -1;}
    T::Alloc(ns);
  }
  unsigned int SizeNeigh(){ return this->VN();}
  inline void Dealloc() {
    __Dealloc();
    T::Dealloc();
  }

  static bool HasVFAdjacency()      {   return true; }
  static void Name(std::vector<std::string> & name){name.push_back(std::string("PVFAdj"));T::Name(name);}

private:
  inline void __Dealloc(){
    delete [] _vfpP; _vfpP = NULL;
    delete [] _vfiP; _vfiP = NULL;
  }

  typename T::FacePointer *_vfpP ;
  char *_vfiP ;
};

/*----------------------------- FFADJ ------------------------------*/

template <class T> class PFFAdj: public T {
public:
  typedef typename T::FaceType FaceType;
  PFFAdj(){ _ffpP = NULL; _ffiP = NULL; }
  /* Note: the destructor will not be called in general because there are no virtual destructors.
   * Instead, the job of deallocating the memory will be done by the face allocator.
   * This destructor is only done for those who istance a face alone (outside a mesh)
   */
//  ~PFFAdj(){ __Dealloc(); }
  typename T::FacePointer        &FFp(const int j)       { assert(j>=0 && j<this->VN());  return _ffpP[j]; }
  typename T::FacePointer         FFp(const int j) const { assert(j>=0 && j<this->VN());  return _ffpP[j]; }
  const typename T::FacePointer  cFFp(const int j) const { assert(j>=0 && j<this->VN());  return _ffpP[j]; }
  char  &FFi(const int j)       { return _ffiP[j]; }
  char  cFFi(const int j) const { return _ffiP[j]; }

  template <class LeftF>
  void ImportData(const LeftF & leftF){T::ImportData(leftF);}
  inline void Alloc(const int & ns) {
    _ffpP = new  FaceType*[ns];
    _ffiP = new  char[ns];
    for(int i = 0; i < ns; ++i) {_ffpP[i] = 0;_ffiP[i] = 0;}
    T::Alloc(ns);
  }
  inline void Dealloc() {
    __Dealloc();
    T::Dealloc();
  }

  static bool HasFFAdjacency()      {   return true; }
  static void Name(std::vector<std::string> & name){name.push_back(std::string("PFFAdj"));T::Name(name);}

private:
  inline void __Dealloc(){
    delete [] _ffpP; _ffpP = NULL;
    delete [] _ffiP; _ffiP = NULL;
  }

  typename T::FacePointer *_ffpP ;
  char *_ffiP ;
};

/*----------------------------- PFEADJ ------------------------------*/

template <class T> class PFEAdj: public T {
public:
  typedef typename T::EdgeType EdgeType;
  PFEAdj(){ _fepP = NULL; }
  /* Note: the destructor will not be called in general because there are no virtual destructors.
   * Instead, the job of deallocating the memory will be done by the face allocator.
   * This destructor is only done for those who istance a face alone (outside a mesh)
   */
//  ~PFEAdj(){ __Dealloc(); }
  typename T::EdgePointer        &FEp(const int j)       { assert(j>=0 && j<this->VN());  return _fepP[j]; }
  typename T::EdgePointer const   FEp(const int j) const { assert(j>=0 && j<this->VN());  return _fepP[j]; }
  const typename T::EdgePointer  cFEp(const int j) const { assert(j>=0 && j<this->VN());  return _fepP[j]; }

  template <class LeftF>
  void ImportData(const LeftF & leftF){T::ImportData(leftF);}
  inline void Alloc(const int & ns) {
    _fepP = new  EdgeType *[ns];
    for(int i = 0; i < ns; ++i) {_fepP[i] = 0;}
    T::Alloc(ns);
  }
  inline void Dealloc() {
    T::Dealloc();
  }

  static bool HasFEAdjacency()      {   return true; }
  static void Name(std::vector<std::string> & name){name.push_back(std::string("PFEAdj"));T::Name(name);}

private:
  inline void __Dealloc(){
    delete [] _fepP; _fepP = NULL;
  }

  typename T::EdgePointer *_fepP ;
};


/*----------------------------- PFHADJ ------------------------------*/

template <class T> class PFHAdj: public T {
public:
  typedef typename T::HEdgeType HEdgeType;
  typedef typename T::HEdgePointer HEdgePointer;

  PFHAdj(){ _fhP = NULL;  }
  typename T::HEdgePointer       &FHp()        {  return _fhP; }
  const typename T::HEdgePointer cFHp() const  {  return _fhP; }

  template <class LeftF>
  void ImportData(const LeftF & leftF){T::ImportData(leftF);}
  inline void Alloc(const int & ns) {T::Alloc(ns);}
  inline void Dealloc() {	 T::Dealloc();}

  static bool HasFHAdjacency()      {   return true; }
  static void Name(std::vector<std::string> & name){name.push_back(std::string("PFHAdj"));T::Name(name);}

private:
  typename T::HEdgePointer  _fhP ;
};

} // end namespace face
} // end namespace vcg
#endif
