/****************************************************************************
 * VCGLib                                                            o o     *
 * Visual and Computer Graphics Library                            o     o   *
 *                                                                _   O  _   *
 * Copyright(C) 2006                                                \/)\/    *
 * 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.                                                         *
 *                                                                           *
 ****************************************************************************/
/****************************************************************************
  History

$Log: not supported by cvs2svn $
Revision 1.11  2006/06/08 13:55:16  cignoni
Added ColorPreserving Cellbase template.

Revision 1.10  2006/05/26 10:18:11  cignoni
Re-adapted to ms compilers

Revision 1.9  2006/05/25 09:37:14  cignoni
Many changes for the different interpretation of hash_set between gcc and .net. Probably to be completed.

Revision 1.8  2006/05/24 16:42:22  m_di_benedetto
Corrected bbox inflation amount in case of _cellsize != 0

Revision 1.7  2006/05/24 15:16:01  cignoni
better comment to the init parameters

Revision 1.6  2006/05/24 08:54:04  cignoni
Added missing std:: to swap

Revision 1.5  2006/05/21 06:40:31  cignoni
Added DoubleFace management

Revision 1.4  2006/05/19 20:49:03  m_di_benedetto
Added check for empty generated mesh (prevent call to mesh allocator with zero vertices or faces).

Revision 1.3  2006/05/18 22:20:53  m_di_benedetto
added check for deleted faces and modified/added std namespace qualifier.

Revision 1.2  2006/05/18 13:59:20  cignoni
Some minor optimizations

Revision 1.1  2006/05/16 21:56:06  cignoni
First working Version

****************************************************************************/

#ifndef __VCGLIB_CLUSTERING
#define __VCGLIB_CLUSTERING

#include<vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/clean.h>
#include<vcg/space/triangle3.h>
#include<vcg/complex/trimesh/update/topology.h>
#include<vcg/space/index/grid_util.h>

#include <iostream>
#include <math.h>
#include <limits>

// some stuff for portable hashes...
#ifdef WIN32
 #ifndef __MINGW32__
  #include <hash_map>
  #include <hash_set>
  #define STDEXT stdext
 #else
  #include <ext/hash_map>
  #include <ext/hash_set>
  #define STDEXT __gnu_cxx
 #endif
#else
 #include <ext/hash_map>
 #include <ext/hash_set>
 #define STDEXT __gnu_cxx
#endif



namespace vcg{
namespace tri{
#define HASH_P0 73856093
#define HASH_P1 19349663
#define HASH_P2 83492791

class HashedPoint3i : public Point3i
{
public:

  const size_t Hash() const 
  {
    return (V(0)*HASH_P0 ^ V(1)*HASH_P1 ^ V(2)*HASH_P2);
  }

  operator size_t () const
  {return Hash();}
};

// needed for gcc compilation
#ifndef _MSC_VER
}} namespace STDEXT {
  template <> struct hash<vcg::tri::HashedPoint3i>{
  inline	size_t	operator ()(const vcg::tri::HashedPoint3i &p) const {return size_t(p);}
};
} namespace vcg{ namespace tri{
#endif

//
template<class MeshType>
class  AverageCell
{
  typedef typename MeshType::CoordType CoordType;
  typedef typename MeshType::FaceType  FaceType;
  public:
    inline void Add(MeshType &m, FaceType &f, int i)
    {
      p+=f.cV(i)->cP();
      // we prefer to use the un-normalized face normal so small faces facing away are dropped out 
      // and the resulting average is weighed with the size of the faces falling here.
      n+=f.cN();      
      cnt++;
    }
    AverageCell(): p(0,0,0), n(0,0,0),cnt(0){}
   CoordType p;
   CoordType n;
   int cnt;
   int id;
   CoordType Pos() const 
  {
    return p/cnt;
  }
  Color4b Col() const {return Color4b::White;}

};


template<class MeshType>
class  AverageColorCell
{
  typedef typename MeshType::CoordType CoordType;
  typedef typename MeshType::FaceType  FaceType;
  public:
    inline void Add(MeshType &m, FaceType &f, int i)
    {
      p+=f.cV(i)->cP();
      c+=CoordType(f.cV(i)->C()[0],f.cV(i)->C()[1],f.cV(i)->C()[2]);

      // we prefer to use the un-normalized face normal so small faces facing away are dropped out 
      // and the resulting average is weighed with the size of the faces falling here.
      n+=f.cN();      
      cnt++;
    }
    AverageColorCell(): p(0,0,0), n(0,0,0), c(0,0,0),cnt(0){}
   CoordType p;
   CoordType n;
   CoordType c;
   int cnt;
   int id;
  Color4b Col() const 
  {
    return Color4b(c[0]/cnt,c[1]/cnt,c[2]/cnt,255);
  }

   CoordType Pos() const 
  {
    return p/cnt;
  }
};


/*
  Metodo di clustering
*/
template<class MeshType, class CellType, bool Selected=true>
class Clustering
{	
 public:
  typedef typename MeshType::ScalarType  ScalarType;
  typedef typename MeshType::CoordType CoordType;
  typedef typename MeshType::VertexType  VertexType;
  typedef typename MeshType::FaceType  FaceType;
  typedef typename MeshType::VertexPointer  VertexPointer;
  typedef typename MeshType::VertexIterator VertexIterator;
  typedef typename MeshType::FaceIterator   FaceIterator;

  // DuplicateFace == bool means that during the clustering doublesided surface (like a thin shell) that would be clustered to a single surface
  // will be merged into two identical but opposite faces. 
  // So in practice:
  // DuplicateFace=true a model with looks ok if you enable backface culling
  // DuplicateFace=false a model with looks ok if you enable doublesided lighting and disable backfaceculling

  bool DuplicateFaceParam;

  class SimpleTri
  {
  public: 
    CellType *v[3];
    const int ii(int i) const {return *((int *)(&(v[i])));}
    bool operator < ( const SimpleTri &p) const {
      return	(v[2]!=p.v[2])?(v[2]<p.v[2]):
				(v[1]!=p.v[1])?(v[1]<p.v[1]):
				(v[0]<p.v[0]);
	  }

    // Sort the vertex of the face maintaining the original face orientation (it only ensure that v0 is the minimum)
    void sortOrient()
    { 
      if(v[1] < v[0] && v[1] < v[2] ) { std::swap(v[0],v[1]); std::swap(v[1],v[2]); return; } // v1 was the minimum
      if(v[2] < v[0] && v[2] < v[1] ) { std::swap(v[0],v[2]); std::swap(v[1],v[2]); return; } // v2 was the minimum
      return; // v0 was the minimum;
    }
    void sort()
    {
      if(v[0] > v[1] ) std::swap(v[0],v[1]); // now v0 < v1
      if(v[0] > v[2] ) std::swap(v[0],v[2]); // now v0 is the minimum
      if(v[1] > v[2] ) std::swap(v[1],v[2]); // sorted!
    }
    // Hashing Function;
    operator size_t () const
    {
      return (ii(0)*HASH_P0 ^ ii(1)*HASH_P1 ^ ii(2)*HASH_P2);
    }
  };


  // The init function Take two parameters
  // _size is the approximate total number of cells composing the grid surrounding the objects (usually a large number) 
  //       eg _size==1.000.000 means a 100x100x100 grid
  // _cellsize is the absolute lenght of the edge of the grid cell.
  //       eg _cellsize==2.0 means that all the vertexes in a 2.0x2.0x2.0 cell are clustered togheter

  // Notes:
  // _size is used only if the cell edge IS zero. 
  // _cellsize gives you an absolute measure of the maximum error introduced
  //           during the simplification (e.g. half of the cell edge lenght)


  void Init(Box3<ScalarType> _mbb, int _size, ScalarType _cellsize=0)
  {
    Grid.bbox=_mbb;
	///inflate the bb calculated
      ScalarType infl = (_cellsize == (ScalarType)0) ? (Grid.bbox.Diag() / _size) : (_cellsize);
	  Grid.bbox.min-=CoordType(infl,infl,infl);
	  Grid.bbox.max+=CoordType(infl,infl,infl);
    Grid.dim  = Grid.bbox.max - Grid.bbox.min;
    if(		_cellsize==0)
        BestDim( _size, Grid.dim, Grid.siz );
    else
      Grid.siz = Point3i::Construct(Grid.dim / _cellsize);

				// find voxel size
		Grid.voxel[0] = Grid.dim[0]/Grid.siz[0];
		Grid.voxel[1] = Grid.dim[1]/Grid.siz[1];
		Grid.voxel[2] = Grid.dim[2]/Grid.siz[2];
  }
  
  
  BasicGrid<ScalarType> Grid;

#ifdef _MSC_VER
  STDEXT::hash_set<SimpleTri> TriSet;
  typedef typename STDEXT::hash_set<SimpleTri>::iterator TriHashSetIterator;
#else
  struct SimpleTriHashFunc{
	inline	size_t	operator ()(const SimpleTri &p) const {return size_t(p);}
  };
  STDEXT::hash_set<SimpleTri,SimpleTriHashFunc> TriSet;
  typedef typename STDEXT::hash_set<SimpleTri,SimpleTriHashFunc>::iterator TriHashSetIterator;
#endif

  STDEXT::hash_map<HashedPoint3i,CellType> GridCell;
  
  void Add(MeshType &m)
  {
    FaceIterator fi;
    for(fi=m.face.begin();fi!=m.face.end();++fi)
    {
		if ((*fi).IsD())
		{
			continue;
		}
      HashedPoint3i pi;
      SimpleTri st;
      for(int i=0;i<3;++i)
      {
        Grid.PToIP((*fi).cV(i)->cP(), pi );
        st.v[i]=&(GridCell[pi]);
        st.v[i]->Add(m,*(fi),i);
      }        
      if( (st.v[0]!=st.v[1]) && (st.v[0]!=st.v[2]) && (st.v[1]!=st.v[2]) )
      { // if we allow the duplication of faces we sort the vertex only partially (to maintain the original face orientation)
        if(DuplicateFaceParam) st.sortOrient();  
                          else st.sort();
        TriSet.insert(st);
      }
    //  printf("Inserted %8i triangles, clustered to %8i tri and %i cells\n",distance(m.face.begin(),fi),TriSet.size(),GridCell.size());
    }
  }
  
  void Extract(MeshType &m)
  {
    m.Clear();

	if (TriSet.empty() || GridCell.empty())
	{
		return;
	}

    Allocator<MeshType>::AddVertices(m,GridCell.size());
    typename STDEXT::hash_map<HashedPoint3i,CellType>::iterator gi;
    int i=0;
    for(gi=GridCell.begin();gi!=GridCell.end();++gi)
    {
      m.vert[i].P()=(*gi).second.Pos();
      if(m.vert[i].HasColor())
        m.vert[i].C()=(*gi).second.Col();
      (*gi).second.id=i;
      ++i;
    }
    Allocator<MeshType>::AddFaces(m,TriSet.size());
    TriHashSetIterator ti;
    i=0;
    for(ti=TriSet.begin();ti!=TriSet.end();++ti)
    {
      m.face[i].V(0)=&(m.vert[(*ti).v[0]->id]);
      m.face[i].V(1)=&(m.vert[(*ti).v[1]->id]);
      m.face[i].V(2)=&(m.vert[(*ti).v[2]->id]);
      // if we are merging faces even when opposite we choose 
      // the best orientation according to the averaged normal
      if(!DuplicateFaceParam) 
      {
		  CoordType N=vcg::Normal(m.face[i]);
      int badOrient=0;
      if( N*(*ti).v[0]->n <0) ++badOrient;
      if( N*(*ti).v[1]->n <0) ++badOrient;
      if( N*(*ti).v[2]->n <0) ++badOrient;
      if(badOrient>2)
          std::swap(m.face[i].V(0),m.face[i].V(1));
      }
      i++;
    }
    
  }
}; //end class clustering 
 } // namespace tri
} // namespace vcg

#endif