/****************************************************************************
* 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.                                                         *
*                                                                           *
****************************************************************************/
/****************************************************************************
  $Log: not supported by cvs2svn $
  Revision 1.19  2006/10/15 07:31:21  cignoni
  typenames and qualifiers for gcc compliance

  Revision 1.18  2006/10/09 20:09:40  cignoni
  Changed some access to VertexFaceIterator to reflect the shorter new operators.

  Revision 1.17  2005/10/12 10:44:01  cignoni
  Now creation of new edge use Ordered() constructor to comply the fact that the basic collapse is simmetric.

  Revision 1.16  2005/01/19 10:35:28  cignoni
  Better management of symmetric/asymmetric edge collapses

  Revision 1.15  2004/12/10 01:03:53  cignoni
  better comments and removed logging

  Revision 1.14  2004/11/23 10:34:23  cignoni
  passed parameters by reference in many funcs and gcc cleaning

  Revision 1.13  2004/10/25 16:28:32  ganovelli
  pos to edge

  Revision 1.12  2004/09/15 11:16:02  ganovelli
  changed P() to cP()

  Revision 1.11  2004/09/09 13:23:01  ponchio
  Header  guards typo

  Revision 1.10  2004/09/09 13:01:12  ponchio
  Linux compatible path in #include

  Revision 1.9  2004/09/08 15:13:29  ganovelli
  changes for gc

  Revision 1.8  2004/09/08 14:33:31  ganovelli
  *** empty log message ***


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

#ifndef __VCG_DECIMATION_TRICOLLAPSE
#define __VCG_DECIMATION_TRICOLLAPSE

#include<vcg/complex/trimesh/edge_collapse.h>
#include<vcg/simplex/face/pos.h>
#include<vcg/complex/local_optimization.h>


namespace vcg{
namespace tri{

/** \addtogroup trimesh */
/*@{*/
/// This Class is specialization of LocalModification for the edge collapse.
/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine.
/// Note that it has knowledge of the heap of the class LocalOptimization because
/// it is responsible of updating it after a collapse has been performed; 
/// This is the base class of all the specialized collapse classes like for example Quadric Edge Collapse. 
/// Each derived class 

template<class TriMeshType, class MYTYPE>
class TriEdgeCollapse: public LocalOptimization<TriMeshType>::LocModType , public EdgeCollapse<TriMeshType> 
{
public:
 /// static data to gather statistical information about the reasons of collapse failures
  class FailStat { 
  public:
	static int &Volume()           {static int vol=0; return vol;}
	static int &LinkConditionFace(){static int lkf=0; return lkf;}
	static int &LinkConditionEdge(){static int lke=0; return lke;}
	static int &LinkConditionVert(){static int lkv=0; return lkv;}
	static int &OutOfDate()        {static int ofd=0; return ofd;}
	static int &Border()           {static int bor=0; return bor;}
  static void Init() 
  {
   Volume()           =0;
   LinkConditionFace()=0;
   LinkConditionEdge()=0;
   LinkConditionVert()=0;
   OutOfDate()        =0;
   Border()           =0;
  }
};
protected:
  typedef	typename TriMeshType::FaceType FaceType;
  typedef	typename TriMeshType::FaceType::VertexType VertexType;
	typedef	typename VertexType::EdgeType EdgeType;
  typedef	typename FaceType::VertexType::CoordType CoordType;
  typedef	typename TriMeshType::VertexType::ScalarType ScalarType;
  typedef typename LocalOptimization<TriMeshType>::HeapElem HeapElem;
	typedef typename LocalOptimization<TriMeshType>::HeapType HeapType;

  TriMeshType *mt;
	///the pair to collapse 
	EdgeType pos;

	///mark for up_dating
	static int& GlobalMark(){ static int im=0; return im;}

	///mark for up_dating
	int localMark;
	
	/// priority in the heap
	ScalarType _priority;

	public:
	/// Default Constructor
	inline	TriEdgeCollapse()
			{}
	///Constructor with postype
	 inline TriEdgeCollapse(const EdgeType &p, int mark)
			{    
				localMark = mark;
				pos=p;
				_priority = ComputePriority();
			}

		~TriEdgeCollapse()
			{}

private:


public:


  inline ScalarType ComputePriority()
  { 
		_priority = Distance(pos.V(0)->cP(),pos.V(1)->cP()); 
    return _priority;
  }

  virtual const char *Info(TriMeshType &m) {
		mt = &m;
    static char buf[60];
      sprintf(buf,"%i -> %i %g\n", pos.V(0)-&m.vert[0], pos.V(1)-&m.vert[0],-_priority);
   return buf;
  }
 
  inline void Execute(TriMeshType &m)
  {	
    CoordType MidPoint=(pos.V(0)->P()+pos.V(1)->P())/2.0;
    /* int FaceDel = */ DoCollapse(m, pos, MidPoint);
  }
  
  static bool IsSymmetric() { return true;}
  
  inline  void UpdateHeap(HeapType & h_ret)
  {
		GlobalMark()++; int nn=0;
		VertexType *v[2];
		v[0]= pos.V(0);v[1]=pos.V(1);	
		v[1]->IMark() = GlobalMark();

		// First loop around the remaining vertex to unmark visited flags
    vcg::face::VFIterator<FaceType> vfi(v[1]->VFp(),v[1]->VFi());	
		while (!vfi.End()){
			vfi.V1()->ClearV();
			vfi.V2()->ClearV();
			++vfi;
		}

    // Second Loop 
		vfi.F() = v[1]->VFp();
		vfi.I() = v[1]->VFi();	
    while (!vfi.End())
    {
			assert(!vfi.F()->IsD());
      for (int j=0;j<3;j++)
			{
				if( !(vfi.V1()->IsV()) && (vfi.V1()->IsRW()))
				{
				vfi.F()->V1(vfi.I())->SetV();
        h_ret.push_back(HeapElem(new MYTYPE(EdgeType::OrderedEdge( vfi.V(),vfi.V1() ),GlobalMark())));
				std::push_heap(h_ret.begin(),h_ret.end());
				if(this->IsSymmetric()){				
					h_ret.push_back(HeapElem(new MYTYPE(EdgeType::OrderedEdge( vfi.V1(),vfi.V()),GlobalMark())));
					std::push_heap(h_ret.begin(),h_ret.end());
				}
      }
				if(  !(vfi.V2()->IsV()) && (vfi.V2()->IsRW()))
				{
					vfi.V2()->SetV();
				h_ret.push_back(HeapElem(new MYTYPE(EdgeType::OrderedEdge(vfi.F()->V(vfi.I()),vfi.F()->V2(vfi.I())),GlobalMark())));
				std::push_heap(h_ret.begin(),h_ret.end());
				//if(false){				
				//	h_ret.push_back(HeapElem(new MYTYPE(EdgeType (vfi.F()->V1(vfi.I()),vfi.F()->V(vfi.I())),GlobalMark())));
				//	std::push_heap(h_ret.begin(),h_ret.end());
				//	}
      }


			}
      ++vfi;nn++;
    }
//		printf("ADDED %d\n",nn);
  }

  ModifierType IsOfType(){ return TriEdgeCollapseOp;}

  inline bool IsFeasible(){
		return LinkConditions(pos);
	}

  inline bool IsUpToDate(){
   // if(pos.V(1)->IsD()) {
			//	++FailStat::OutOfDate();
			//	return false;
			//}
			//
   // if(pos.V(1)->IsD()) {
			//	++FailStat::OutOfDate();
			//	return false;
			//}

		  VertexType *v0=pos.V(0);
			VertexType *v1=pos.V(1);
			
			//if(! (( (!v0->IsD()) && (!v1->IsD())) &&
			//				 localMark>=v0->IMark() &&
			//				 localMark>=v1->IMark()))
			if( v0->IsD() || v1->IsD() ||
				 localMark < v0->IMark()  ||
		  	 localMark < v1->IMark()   )
			{
				++FailStat::OutOfDate();
				return false;
			}
				return true;
	}

	virtual ScalarType Priority() const {
	return _priority;
  }

	static void Init(TriMeshType&m,HeapType&h_ret){
		h_ret.clear();
		typename TriMeshType::FaceIterator fi;
		for(fi = m.face.begin(); fi != m.face.end();++fi)
		if(!(*fi).IsD()){
		   for (int j=0;j<3;j++)
      {
        EdgeType p=EdgeType::OrderedEdge((*fi).V(j),(*fi).V((j+1)%3));
        h_ret.push_back(HeapElem(new MYTYPE(p, IMark(m))));
        //printf("Inserting in heap coll %3i ->%3i %f\n",p.V()-&m.vert[0],p.VFlip()-&m.vert[0],h_ret.back().locModPtr->Priority());
      }
		}
	}

};
}//end namespace tri
}//end namespace vcg

#endif