//=============================================================================
//                                                                            
//                               OpenMesh                                     
//      Copyright (C) 2001-2005 by Computer Graphics Group, RWTH Aachen       
//                           www.openmesh.org                                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//                                License                                     
//                                                                            
//   This library is free software; you can redistribute it and/or modify it 
//   under the terms of the GNU Library General Public License as published  
//   by the Free Software Foundation, version 2.                             
//                                                                             
//   This library 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         
//   Library General Public License for more details.                          
//                                                                            
//   You should have received a copy of the GNU Library General Public         
//   License along with this library; if not, write to the Free Software       
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 
//                                                                            
//-----------------------------------------------------------------------------
//                                                                            
//   $Revision: 1.2 $
//   $Date: 2005-12-21 13:58:54 $
//                                                                            
//=============================================================================


//=============================================================================
//
//  CLASS RuleInterfaceT
//
//=============================================================================

#ifndef OPENMESH_SUBDIVIDER_ADAPTIVE_RULEINTERFACET_HH
#define OPENMESH_SUBDIVIDER_ADAPTIVE_RULEINTERFACET_HH


//== INCLUDES =================================================================

#include <string>
#include <OpenMesh/Tools/Subdivider/Adaptive/Composite/CompositeTraits.hh>

//== NAMESPACE ================================================================

namespace OpenMesh   { // BEGIN_NS_OPENMESH
namespace Subdivider { // BEGIN_NS_SUBDIVIDER
namespace Adaptive   { // BEGIN_NS_ADAPTIVE


//== FORWARDS =================================================================

template <typename M> class CompositeT;
template <typename M> class RuleInterfaceT;

//== CLASS DEFINITION =========================================================


// ----------------------------------------------------------------------------

/** Handle template for adaptive composite subdividion rules
 *  \internal
 * 
 *  Use typed handle of a rule, e.g. Tvv3<MyMesh>::Handle.
 */
template < typename R > 
struct RuleHandleT : public BaseHandle
{
  explicit RuleHandleT(int _idx=-1) : BaseHandle(_idx) {}
  typedef R Rule;

  operator bool() const { return is_valid(); }

};

/** Defines the method type() (RuleInterfaceT::type()) and the
 *  typedefs Self and Handle.
 */
#define COMPOSITE_RULE( classname, mesh_type ) \
  protected:\
    friend class CompositeT<mesh_type>; \
  public: \
    const char *type() const { return #classname; } \
    typedef classname<mesh_type>     Self;          \
    typedef RuleHandleT< Self >      Handle


// ----------------------------------------------------------------------------
/** Base class for adaptive composite subdivision rules
 *  \see class CompositeT
 */
template <typename M> class RuleInterfaceT
{
public:

  typedef M                   Mesh;
  typedef RuleInterfaceT<M>   Self;
  typedef RuleHandleT< Self > Rule;

  typedef typename M::Scalar  scalar_t;

protected:

  /// Default constructor
  RuleInterfaceT(Mesh& _mesh) : mesh_(_mesh) {};

public:

  /// Destructor
  virtual ~RuleInterfaceT() {};


  /// Returns the name of the rule.
  /// Use define COMPOSITE_RULE to overload this function in a derived class.
  virtual const char *type() const = 0;

public:

  /// \name Raise item
  //@{
  /// Raise item to target state \c _target_state.
  virtual void raise(typename M::FaceHandle& _fh, state_t _target_state) 
  {
    if (mesh_.deref(_fh).state() < _target_state) {
      update(_fh, _target_state);
      mesh_.deref(_fh).inc_state();
    }
  }

  virtual void raise(typename M::EdgeHandle& _eh, state_t _target_state) 
  {
    if (mesh_.deref(_eh).state() < _target_state) {
      update(_eh, _target_state);
      mesh_.deref(_eh).inc_state();
    }
  }

  virtual void raise(typename M::VertexHandle& _vh, state_t _target_state) 
  {
    if (mesh_.deref(_vh).state() < _target_state) {
      update(_vh, _target_state);
      mesh_.deref(_vh).inc_state();
    }
  }
  //@}

  void update(typename M::FaceHandle& _fh, state_t _target_state)
  {
    typename M::FaceHandle opp_fh;

    while (mesh_.deref(_fh).state() < _target_state - 1) {
      prev_rule()->raise(_fh, _target_state - 1);
    }

    // Don't use unflipped / unfinal faces!!!
    if (subdiv_type() == 3) {

      if (mesh_.face_handle(mesh_.opposite_halfedge_handle(mesh_.halfedge_handle(_fh))).is_valid()) {

	while (!mesh_.deref(_fh).final()) {

	  opp_fh = mesh_.face_handle(mesh_.opposite_halfedge_handle(mesh_.halfedge_handle(_fh)));
      
	  assert (mesh_.deref(_fh).state() >= 
		  mesh_.deref(opp_fh).state());

	  // different states: raise other face
	  if (mesh_.deref(_fh).state() > mesh_.deref(opp_fh).state()){

	    // raise opposite face
	    prev_rule()->raise(opp_fh, _target_state - 1);
	  }

	  else {

	    // equal states

	    // flip edge
	    // 	    typename M::EdgeHandle eh(mesh_.edge_handle(mesh_.halfedge_handle(_fh)));

	    // 	    if (mesh_.is_flip_ok(eh)) {

	    // 	      std::cout << "Flipping Edge...\n";

	    // 	      mesh_.flip(eh);

	    // 	      mesh_.deref(_fh).set_final();
	    // 	      mesh_.deref(opp_fh).set_final();
	    // 	    }

	    // 	    else {

	    // 	      std::cout << "Flip not okay.\n";
	    // 	    }
	  }
	}
      }

      else {

	// 	mesh_.deref(_fh).set_final();
      }

      //     std::cout << "Raising Face   to Level " 
      // 	      << _target_state
      // 	      << " with "
      // 	      << type()
      // 	      << ".\n";

    }
    
    assert( subdiv_type() != 4       || 
            mesh_.deref(_fh).final() || 
            _target_state%n_rules() == (subdiv_rule()->number() + 1)%n_rules() );

    typename M::FaceEdgeIter   fe_it;
    typename M::FaceVertexIter fv_it;
    typename M::EdgeHandle     eh;
    typename M::VertexHandle   vh;

    std::vector<typename M::FaceHandle> face_vector;
    face_vector.clear();

    if (_target_state > 1) {

      for (fe_it = mesh_.fe_iter(_fh); fe_it; ++fe_it) {

	eh = fe_it.handle();
	prev_rule()->raise(eh, _target_state - 1);
      }

      for (fv_it = mesh_.fv_iter(_fh); fv_it; ++fv_it) {

	vh = fv_it.handle();
	prev_rule()->raise(vh, _target_state - 1);
      }
    }
  }
  

  void update(typename M::EdgeHandle& _eh, state_t _target_state) 
  {
    state_t state(mesh_.deref(_eh).state());

    // raise edge to correct state
    if (state + 1 < _target_state && _target_state > 0) {

      prev_rule()->raise(_eh, _target_state - 1);
    }

    typename M::VertexHandle vh;
    typename M::FaceHandle   fh;

    if (_target_state > 1)
    {
      vh = mesh_.to_vertex_handle(mesh_.halfedge_handle(_eh, 0));
      prev_rule()->raise(vh, _target_state - 1);

      vh = mesh_.to_vertex_handle(mesh_.halfedge_handle(_eh, 1));
      prev_rule()->raise(vh, _target_state - 1);

      fh = mesh_.face_handle(mesh_.halfedge_handle(_eh, 0));
      if (fh.is_valid())
	prev_rule()->raise(fh, _target_state - 1);

      fh = mesh_.face_handle(mesh_.halfedge_handle(_eh, 1));
      if (fh.is_valid())
	prev_rule()->raise(fh, _target_state - 1);
    }
  }


  void update(typename M::VertexHandle& _vh, state_t _target_state) {

    state_t state(mesh_.deref(_vh).state());

    // raise vertex to correct state
    if (state + 1 < _target_state)
    {
      prev_rule()->raise(_vh, _target_state - 1);
    }

    std::vector<typename M::HalfedgeHandle> halfedge_vector;
    halfedge_vector.clear();

    typename M::VertexOHalfedgeIter voh_it;
    typename M::EdgeHandle eh;
    typename M::FaceHandle fh;

    if (_target_state > 1)
    {

      for (voh_it = mesh_.voh_iter(_vh); voh_it; ++voh_it) {
	halfedge_vector.push_back(voh_it.handle());
      }

      while ( !halfedge_vector.empty() ) {
	eh = mesh_.edge_handle(halfedge_vector.back());
	halfedge_vector.pop_back();

	prev_rule()->raise(eh, _target_state - 1);
      }

      for (voh_it = mesh_.voh_iter(_vh); voh_it; ++voh_it) {
	halfedge_vector.push_back(voh_it.handle());
      }

      while ( !halfedge_vector.empty() ) {
	fh = mesh_.face_handle(halfedge_vector.back());
	halfedge_vector.pop_back();

	if (fh.is_valid())
	  prev_rule()->raise(fh, _target_state - 1);
      }
    }
  }

public:


  /// Type of split operation, if it is a topological operator
  int  subdiv_type() const { return subdiv_type_; }


  /// Position in rule sequence
  int  number() const      { return number_;      }

  /// \name Parameterization of rule
  //@{

  /// Set coefficient - ignored by non-parameterized rules.
  virtual void set_coeff( scalar_t _coeff ) { coeff_ = _coeff; }

  /// Get coefficient - ignored by non-parameterized rules.
  scalar_t coeff() const { return coeff_; }

  //@}

protected:

  void  set_prev_rule(Self*& _p) { prev_rule_ = _p; }
  Self* prev_rule() { return prev_rule_; }

  void  set_subdiv_rule(Self*& _n) { subdiv_rule_ = _n; }
  Self* subdiv_rule() { return subdiv_rule_; }

  void set_number(int _n) { number_ = _n; }

  void set_n_rules(int _n) { n_rules_ = _n; }
  int  n_rules() { return n_rules_; }

  void set_subdiv_type(int _n) 
  { assert(_n == 3 || _n == 4); subdiv_type_ = _n; }

  friend class CompositeT<M>;

protected:

  Mesh& mesh_;

private:

  Self* prev_rule_;
  Self* subdiv_rule_;
  
  int   subdiv_type_;
  int   number_;
  int   n_rules_;

  scalar_t coeff_;

private: // Noncopyable

  RuleInterfaceT(const RuleInterfaceT&);
  RuleInterfaceT& operator=(const RuleInterfaceT&);

};

//=============================================================================
} // END_NS_ADAPTIVE
} // END_NS_SUBDIVIDER
} // END_NS_OPENMESH
//=============================================================================
#endif // OPENMESH_SUBDIVIDER_ADAPTIVE_RULEINTERFACET_HH defined
//=============================================================================

