/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* 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.13  2004/11/23 10:34:45  cignoni
    passed parameters by reference in many funcs and gcc cleaning


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

#ifndef __VCG_TETRA_TRI_COLLAPSE
#define __VCG_TETRA_TRI_COLLAPSE


#include<vcg/simplex/face/pos.h>
#include<vcg/simplex/face/topology.h>
#include<map>

namespace vcg{
namespace tri{	

/** \addtogroup trimesh */
/*@{*/
/** This a static utility class for the edge collapse. 
    It provides a common set of useful function for actually making an edge collapse over a trimesh. 
    See also the corresponding class in the local optimization framework called TriEdgeCollapse
**/

template <class TRI_MESH_TYPE> 
class EdgeCollapse
{
	public:
  /// The tetrahedral mesh type
  typedef	 TRI_MESH_TYPE TriMeshType;
  /// The tetrahedron type
  typedef	typename TriMeshType::FaceType FaceType;
	/// The vertex type
	typedef	typename FaceType::VertexType VertexType;
  /// The vertex iterator type
  typedef	typename TriMeshType::VertexIterator VertexIterator;
  /// The tetra iterator type
  typedef	typename TriMeshType::FaceIterator FaceIterator;
  /// The coordinate type
	typedef	typename FaceType::VertexType::CoordType CoordType;
  /// The scalar type
  typedef	typename TriMeshType::VertexType::ScalarType ScalarType;
  ///the container of tetrahedron type
  typedef typename TriMeshType::FaceContainer FaceContainer;
  ///the container of vertex type
  typedef typename TriMeshType::VertContainer VertContainer;
  ///half edge type
	typedef typename TriMeshType::FaceType::EdgeType EdgeType;
	/// vector of pos
	typedef typename std::vector<EdgeType> EdgeVec;
	///of VFIterator
	typedef typename vcg::face::VFIterator<FaceType>  VFI;
	/// vector of VFIterator
	typedef typename std::vector<vcg::face::VFIterator<FaceType> > VFIVec;



	/// Default Constructor
	EdgeCollapse()
		{
		};

  ~EdgeCollapse()
		{
		};


  struct Edge{
			VertexType* v0,v1;
      Edge(	const VertexType*& a,const VertexType*& b){
						assert(a!=b);
						if(a<b) 
							{v0=a;v1=b;}
						else
						{v1=a;v0=b;}
      }
			
			const bool operator <(const Edge & e) const {
				return (v0==e.v0)?(v1<e.v1):(v0<e.v0);
			}

			const bool operator ==(const Edge & e) const {
			return (v0==e.v0)&&(v1==e.v1);
			}

};

	static VFIVec & AV0(){static VFIVec av0; return av0;}
	static VFIVec & AV1(){static VFIVec av1; return av1;}
	static VFIVec & AV01(){static VFIVec av01; return av01;}


	void FindSets(EdgeType &p)
	{
		VertexType * v0 = p.V(0);
		VertexType * v1 = p.V(1);

		AV0().clear();  // Facce incidenti in v0 
		AV1().clear();  // Facce incidenti in v1 
		AV01().clear(); // Facce incidenti in v0 e v1
		
		VFI x;

		for( x.f = v0->VFp(), x.z = v0->VFi(); x.f!=0; ++x)
		{
			int zv1 = -1;

			for(int j=0;j<3;++j) 
				if( x.f->V(j)==&*v1 )	{
					zv1 = j;
					break;
				}
			if(zv1==-1) 	AV0().push_back( x ); // la faccia x.f non ha il vertice v1 => e' incidente solo in v0
			else    			AV01().push_back( x );
		}

		for( x.f = v1->VFp(), x.z = v1->VFi(); x.f!=0; ++x )
		{
			int zv0 = -1;

			for(int j=0;j<3;++j)
				if( x.f->V(j)==&*v0 )	{
					zv0 = j;
					break;
				}
			if(zv0==-1)	AV1().push_back( x ); // la faccia x.f non ha il vertice v1 => e' incidente solo in v0
		}
}

	bool LinkConditions(EdgeType  pos){
		
		const int ADJ_1 = TriMeshType::VertexType::NewBitFlag();
		const int ADJ_E = TriMeshType::VertexType::NewBitFlag();
		//enum {ADJ_1= MeshType::VertexType::USER0,
		//	    ADJ_E= MeshType::VertexType::USER0<<1} ;
		// const int ALLADJ = ADJ_1|ADJ_E;
		const int NOTALLADJ = ~(ADJ_1 | ADJ_E | TriMeshType::VertexType::VISITED);
		const int NOTALLADJ1 = ~(ADJ_E | TriMeshType::VertexType::VISITED);

		//EdgePosB<MeshType::face_type::face_base> x;
		typename vcg::face::VFIterator<FaceType> x;
		// Clear visited and adj flag for all vertices adj to v0;
		for(x.f = pos.V(0)->VFp(), x.z = pos.V(0)->VFi(); x.f!=0; ++x )	 	{
			x.f->V1(x.z)->Flags() &= NOTALLADJ;
			x.f->V2(x.z)->Flags() &= NOTALLADJ;
		}
		// Clear visited flag for all vertices adj to v1 and set them adj1 to v1;
		for(x.f = pos.V(1)->VFp(), x.z = pos.V(1)->VFi(); x.f!=0; ++x )	 	{
			x.f->V1(x.z)->Flags() &= NOTALLADJ1;
			x.f->V2(x.z)->Flags() &= NOTALLADJ1;
		}
		// Mark vertices adj to v1 as  ADJ_1 and  adj1 to v1;
		for(x.f = pos.V(1)->VFp(), x.z = pos.V(1)->VFi(); x.f!=0; ++x )	 	{
			if(x.f->V1(x.z)==pos.V(0)) x.f->V2(x.z)->Flags() |= ADJ_E | ADJ_1;
			else x.f->V2(x.z)->Flags() |= ADJ_1;
			if(x.f->V2(x.z)==pos.V(0)) x.f->V1(x.z)->Flags() |= ADJ_E | ADJ_1;
			else x.f->V1(x.z)->Flags() |= ADJ_1;
		}
			
		// compute the number of:
		int adj01=0;  // vertices adjacents to both v0 and v1 
		int adje=0;   // vertices adjacents to an egde (usually 2)
		for(x.f = pos.V(0)->VFp(), x.z = pos.V(0)->VFi(); x.f!=0; ++x )	 	{
			if(!x.f->V1(x.z)->IsV()) {
				x.f->V1(x.z)->SetV();
				if(x.f->V1(x.z)->Flags()&ADJ_1) ++adj01;
				if(x.f->V1(x.z)->Flags()&ADJ_E) ++adje;
			}
			if(!x.f->V2(x.z)->IsV()) {
				x.f->V2(x.z)->SetV();
				if(x.f->V2(x.z)->Flags()&ADJ_1) ++adj01;
				if(x.f->V2(x.z)->Flags()&ADJ_E) ++adje;
			}
		}

		//bool val=TopoCheck2();
		//if(val != (adj01==adje)) 		printf("Wrong topo %i %i\n",adj01,adje);
		TriMeshType::VertexType::DeleteBitFlag(ADJ_E);
		TriMeshType::VertexType::DeleteBitFlag(ADJ_1);
		
    return (adj01==adje);
	}



	int DoCollapse(EdgeType & c, const Point3<ScalarType> &p)
	{
		FindSets(c);
		typename VFIVec::iterator i;
		int n_face_del =0 ;
		
		//set Face Face topology
		if (TriMeshType::HasFFTopology())
		{
			//int e0=c.z;
			//int e1=c.f->FFi(c.z);	//opposite edge

			//FaceType *f0=c.f;
			//FaceType *f1=f0->FFp(c.z);
			//
			////take right indexes
			//FaceType *f00=f0->FFp((e0+1)%3);
			//FaceType *f01=f0->FFp((e0+2)%3);
			//int If00=f0->FFi((e0+1)%3);
			//int If01=f0->FFi((e0+2)%3);
			//
			////then attach faces
			//f00->FFp(If00)=f01;
			//f00->FFi(If00)=If01;
			//f01->FFp(If01)=f00;
			//f01->FFi(If01)=If00;

			////and the ones of face f1

			//f00=f1->FFp((e1+1)%3);
			//f01=f1->FFp((e1+2)%3);
			//If00=f1->FFi((e1+1)%3);
			//If01=f1->FFi((e1+2)%3);
			//
			////and attach faces
			//f00->FFp(If00)=f01;
			//f00->FFi(If00)=If01;
			//f01->FFp(If01)=f00;
			//f01->FFi(If01)=If00;
		}

		for(i=AV01().begin();i!=AV01().end();++i)
		{	
			FaceType  & f = *((*i).f);
			assert(f.V((*i).z) == c.V(0));
			vcg::face::VFDetach(f,((*i).z+1)%3);
			vcg::face::VFDetach(f,((*i).z+2)%3);
			f.SetD();
			n_face_del++;
		}

		//set Vertex Face topology
		for(i=AV0().begin();i!=AV0().end();++i)
		{
			(*i).f->V((*i).z) = c.V(1);									 // In tutte le facce incidenti in v0, si sostituisce v0 con v1
			(*i).f->VFp((*i).z) = (*i).f->V((*i).z)->VFp(); // e appendo la lista di facce incidenti in v1 a questa faccia
			(*i).f->VFi((*i).z) = (*i).f->V((*i).z)->VFi();
			(*i).f->V((*i).z)->VFp() = (*i).f;
			(*i).f->V((*i).z)->VFi() = (*i).z;
		}
		
		c.V(0)->SetD();
		c.V(1)->P()=p;
		return n_face_del;
	}

};

}
}
#endif