// Copyright (c) 2009 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// 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 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
//
//
// Author(s)     : Stephane Tayeb
//
//******************************************************************************
// File Description :
//
//******************************************************************************

#ifndef CGAL_MESH_3_MESH_STANDARD_CRITERIA_H
#define CGAL_MESH_3_MESH_STANDARD_CRITERIA_H

#include <CGAL/license/Mesh_3.h>



#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

namespace CGAL {

namespace Mesh_3 {


/**
 * @class Abstract_criterion
 */
template <typename Tr, typename Visitor_>
class Abstract_criterion
{
  typedef typename Tr::Geom_traits::FT FT;
  typedef Abstract_criterion<Tr,Visitor_> Self;

public:
  typedef FT Quality;
  typedef boost::optional<Quality> Badness;
  typedef typename Visitor_::Handle Handle;

  /// Destructor
  virtual ~Abstract_criterion() {}

  void accept(Visitor_& v) const { do_accept(v); }
  Badness is_bad(const Handle& h) const { return do_is_bad(h); }
  Self* clone() const { return do_clone(); }

protected:
  virtual void do_accept(Visitor_& v) const = 0;
  virtual Badness do_is_bad(const Handle& h) const = 0;
  virtual Self* do_clone() const = 0;

};  // end class Abstract_criterion


template <typename Tr, typename Visitor_>
inline
Abstract_criterion<Tr,Visitor_>*
new_clone(const Abstract_criterion<Tr,Visitor_>& criterion)
{
  return criterion.clone();
}



template <typename Tr, typename Handle_>
class Criterion_visitor
{
  typedef Criterion_visitor<Tr, Handle_> Self;
public:
  typedef Handle_ Handle;

protected:
  typedef Abstract_criterion<Tr, Self> Criterion;

public:
  typedef std::pair<int, typename Criterion::Quality> Quality;
  typedef boost::optional<Quality> Badness;


  // Constructor
  Criterion_visitor(const Handle_& h)
    : handle_(h)
    , badness_()
    , criterion_counter_(0) {}

  // Destructor
  ~Criterion_visitor() {}

  Badness badness() const
  {
    return badness_;
  }

  bool go_further() const
  {
    return !badness_;
  }

protected:
  void increment_counter()
  {
    ++criterion_counter_;
  }

  void set_badness(const Badness& badness)
  {
    badness_ = badness;
  }

  template<typename Derived>
  void do_visit(const Abstract_criterion<Tr, Derived>& criterion)
  {
    typedef typename Abstract_criterion<Tr, Derived>::Badness Badness;

    const Badness badness = criterion.is_bad(handle_);
    if ( badness )
      badness_ = std::make_pair(criterion_counter_, *badness);

    increment_counter();
  }

private:
  Handle_ handle_;
  Badness badness_;
  int criterion_counter_;

};  // end class Criterion_visitor




template <typename Tr, typename Visitor_>
class Criteria
{
  typedef Criteria<Tr, Visitor_> Self;

  typedef Abstract_criterion<Tr,Visitor_> Criterion;
  typedef boost::ptr_vector<Criterion> Criterion_vector;
  typedef typename Visitor_::Quality Quality;
  typedef typename Visitor_::Badness Badness;

public:
  /// Constructor
  Criteria() {}

  /// Copy constructor
  Criteria(const Self& rhs)
    : criterion_vector_(rhs.criterion_vector_.clone())    { }

  /// Destructor
  ~Criteria() {} // ptr_vector do the job of criterion deleting

  /// Add a criterion
  void add(Criterion* criterion)
  {
    criterion_vector_.push_back(criterion);
  }

  Badness operator()(const typename Visitor_::Handle& h) const
  {
    Visitor_ visitor(h);

    typename Criterion_vector::const_iterator it = criterion_vector_.begin();
    for (  ; it != criterion_vector_.end() ; ++it )
    {
      it->accept(visitor);
      if ( ! visitor.go_further() ) return visitor.badness();
    }

    return visitor.badness();
  }

private:
  Criterion_vector criterion_vector_;
};  // end class Criteria


}  // end namespace Mesh_3

}  // end namespace CGAL

#endif // CGAL_MESH_3_MESH_STANDARD_CRITERIA_H
