// Copyright (c) 2003-2006  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org); you may redistribute it under
// the terms of the Q Public License version 1.0.
// See the file LICENSE.QPL distributed with CGAL.
//
// 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: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.2-branch/Curved_kernel/include/CGAL/Arr_circular_line_arc_traits.h $
// $Id: Arr_circular_line_arc_traits.h 30674 2006-04-20 08:33:48Z afabri $
//
// Author(s)     : Monique Teillaud, Sylvain Pion, Julien Hazebrouck

// Partially supported by the IST Programme of the EU as a Shared-cost
// RTD (FET Open) Project under Contract No  IST-2000-26473 
// (ECG - Effective Computational Geometry for Curves and Surfaces) 
// and a STREP (FET Open) Project under Contract No  IST-006413 
// (ACS -- Algorithms for Complex Shapes)

#ifndef CGAL_VARIANT_TRAITS_H
#define CGAL_VARIANT_TRAITS_H

#include <CGAL/basic.h>
#include <cassert>
#include <boost/variant.hpp>

namespace CGAL {
  namespace VariantFunctors{

    // Takes an iterator range of Object(Line/Circular_arc),
    // returns an Object(Variant(Line/Circular_arc)).
    // Do nothing for Object(Endpoint).
    template <class CK, class Arc1, class Arc2, class OutputIterator>
      OutputIterator object_to_object_variant(const std::vector<CGAL::Object>& res1, OutputIterator res2){
      
      for(std::vector<CGAL::Object>::const_iterator it = res1.begin(); 
	  it != res1.end(); ++it ){
	if(const Arc1 *arc = CGAL::object_cast< Arc1 >(&*it)){
	  boost::variant< Arc1, Arc2 > v =  *arc;
	  *res2++ = make_object(v);
	}
	else if (const Arc2 *line = CGAL::object_cast< Arc2 >(&*it)){
	  boost::variant< Arc1, Arc2 > v =  *line;
	  *res2++ = make_object(v);
	}
	else{
	  *res2++ = *it;
	}
      }
      return res2;	  
    }


    template <class CircularKernel, class Arc1, class Arc2>
    class In_x_range_2
    {
    public:
      typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
      typedef bool result_type;
      
      result_type
	operator()(const boost::variant< Arc1, Arc2 > &a,
		   const Circular_arc_point_2 &p) const
      { 
	if ( const Arc1* arc1 = boost::get<Arc1>( &a ) ){
	  return CircularKernel().in_x_range_2_object()(*arc1, p);
	}
	else {
	  const Arc2* arc2 = boost::get<Arc2>( &a );
	  return CircularKernel().in_x_range_2_object()(*arc2, p);
	}
      }
    };
  

    template <class CircularKernel, class Arc1, class Arc2>
    class Compare_y_to_right_2
    {
    public:
      typedef CGAL::Comparison_result result_type;
      typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
    
      result_type
	operator()(const boost::variant< Arc1, Arc2 > &a1,
		   const boost::variant< Arc1, Arc2 > &a2,
		   const Circular_arc_point_2 &p) const
      { 
	if ( const Arc1* arc1 = boost::get<Arc1>( &a1 ) ){
	  if ( const Arc1* arc2 = boost::get<Arc1>( &a2 ) ){
	    return CircularKernel().compare_y_to_right_2_object()(*arc1, *arc2, p);
	  }
	  else {
	    const Arc2* arc2e = boost::get<Arc2>( &a2 );
	    return CircularKernel().compare_y_to_right_2_object()(*arc1, *arc2e, p);
	  }
	}
	const Arc2* arc1 = boost::get<Arc2>( &a1 );
	if ( const Arc1* arc2 = boost::get<Arc1>( &a2 ) ){
	  return CircularKernel().compare_y_to_right_2_object()(*arc1, *arc2, p);
	}
	const Arc2* arc2e = boost::get<Arc2>( &a2 );
	return CircularKernel().compare_y_to_right_2_object()(*arc1, *arc2e, p);
	
      }  
    };


    template <class CircularKernel>
    class Variant_Equal_2
      : public boost::static_visitor<bool>
    {
    public :

      template < typename T >
      bool
      operator()(const T &a0, const T &a1) const
      {
	return CircularKernel().equal_2_object()(a0,a1);
      }

      template < typename T1, typename T2 >      
      bool
      operator()(const T1 &, const T2 &) const
      {
	return false;
      }
    };

      

    template <class CircularKernel, class Arc1, class Arc2>
    class Equal_2
#ifndef CGAL_CFG_MATCHING_BUG_6 
      : public 
      CircularKernel::Equal_2
#endif
    {
    public:
      typedef boost::variant< Arc1, Arc2 >  Curve_2;
      typedef bool result_type;
#ifndef CGAL_CFG_MATCHING_BUG_6 
      using CircularKernel::Equal_2::operator();
#else 
      typedef typename CircularKernel::Circular_arc_point_2 Circular_arc_point_2;
      typedef typename CircularKernel::Line_arc_2 Line_arc_2;
      typedef typename CircularKernel::Circular_arc_2 Circular_arc_2;
      typedef typename CircularKernel::Equal_2 CK_Equal_2;

    result_type
    operator() (const Circular_arc_point_2 &p0,
                const Circular_arc_point_2 &p1) const
    { return CK_Equal_2()(p0, p1); }
    
    result_type
    operator() (const Circular_arc_2 &a0, const Circular_arc_2 &a1) const
    { return CK_Equal_2()(a0, a1); }

    result_type
    operator() (const Line_arc_2 &a0, const Line_arc_2 &a1) const
    { return CK_Equal_2()(a0, a1); }

    result_type
      operator() ( const Line_arc_2 &a0, const Circular_arc_2 &a1) const
    {return false;}

    result_type
      operator() ( const Circular_arc_2 &a0, const Line_arc_2 &a1) const
    {return false;}
    
#endif

      result_type
	operator()(const Curve_2 &a0, const Curve_2 &a1) const
      {
	return boost::apply_visitor( Variant_Equal_2<CircularKernel>(), a0, a1 );
      }
      
    };

    


    template <class CircularKernel, class Arc1, class Arc2>
    class Compare_y_at_x_2
    {
    public:
      typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
      typedef CGAL::Comparison_result result_type;

      result_type
	operator() (const Circular_arc_point_2 &p,
		    const boost::variant< Arc1, Arc2 > &A1) const
      { 
	if ( const Arc1* arc1 = boost::get<Arc1>( &A1 ) ){
	  return CircularKernel().compare_y_at_x_2_object()(p, *arc1);
	}
	else {
	  const Arc2* arc2 = boost::get<Arc2>( &A1 );
	  return CircularKernel().compare_y_at_x_2_object()(p, *arc2);
	}
      }
    };

     template <class CircularKernel>
    class Variant_Do_overlap_2
      : public boost::static_visitor<bool>
    {
    public :

      template < typename T >
      bool
      operator()(const T &a0, const T &a1) const
      {
	return CircularKernel().do_overlap_2_object()(a0, a1);
      }

      template < typename T1, typename T2 >      
      bool
      operator()(const T1 &, const T2 &) const
      {
	return false;
      }
    };
    

    template <class CircularKernel, class Arc1, class Arc2>
    class Do_overlap_2
    {
    public:
      typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
      typedef bool result_type;

      result_type
	operator()(const boost::variant< Arc1, Arc2 > &A0,
		   const boost::variant< Arc1, Arc2 > &A1) const
      { 
	return boost::apply_visitor( Variant_Do_overlap_2<CircularKernel>(), A0, A1 );
      }    
    };


    template <class CircularKernel, class Arc1, class Arc2>
    class Make_x_monotone_2
    {
    public:
      typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;

      template < class OutputIterator >
	OutputIterator
	operator()(const boost::variant< Arc1, Arc2 > &A, OutputIterator res)
      { if ( const Arc1* arc1 = boost::get<Arc1>( &A ) ){
	  std::vector<CGAL::Object> container;
	  CircularKernel().make_x_monotone_2_object()(*arc1,  std::back_inserter(container));
	  return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	}
	else {
	  const Arc2* arc2 = boost::get<Arc2>( &A );
	  std::vector<CGAL::Object> container;
	  CircularKernel().make_x_monotone_2_object()(*arc2,  std::back_inserter(container));
	  return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	}
      }
    };


    
    template <class CircularKernel, class Arc1, class Arc2>
    class Intersect_2
    {
    public:
    typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
      
      template < class OutputIterator >
	OutputIterator
	operator()(const boost::variant< Arc1, Arc2 > &c1,
		   const boost::variant< Arc1, Arc2 > &c2,
		   OutputIterator res) const
      { 
	if ( const Arc1* arc1 = boost::get<Arc1>( &c1 ) ){
	  if ( const Arc1* arc2 = boost::get<Arc1>( &c2 ) ){
	    std::vector<CGAL::Object> container;
	    CircularKernel().intersect_2_object()(*arc1,*arc2,  std::back_inserter(container));
	    return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	  }
	  else if ( const Arc2* arc2 = boost::get<Arc2>( &c2 ) ){
	    std::vector<CGAL::Object> container;
	    CircularKernel().intersect_2_object()(*arc1,*arc2,  std::back_inserter(container));
	    return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	  }
	}
	else {
	  const Arc2* arc1e = boost::get<Arc2>( &c1 );
	  if ( const Arc1* arc2 = boost::get<Arc1>( &c2 ) ){
	    std::vector<CGAL::Object> container;
	    CircularKernel().intersect_2_object()(*arc1e,*arc2,  std::back_inserter(container));
	    return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	  }
	  const Arc2* arc2 = boost::get<Arc2>( &c2 );
	  std::vector<CGAL::Object> container;
	  CircularKernel().intersect_2_object()(*arc1e,*arc2,  std::back_inserter(container));
	  return object_to_object_variant<CircularKernel, Arc1, Arc2>(container, res);
	}
	std::abort();
	return res;//for no warning
      }
    
    };

    
    template <class CircularKernel, class Arc1, class Arc2>
    class Split_2
    {

    public:
    typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;
      typedef void result_type;
      result_type
	operator()(const boost::variant< Arc1, Arc2 > &A, 
		   const Circular_arc_point_2 &p,
		   boost::variant< Arc1, Arc2 > &ca1,
		   boost::variant< Arc1, Arc2 > &ca2) const
      { 
	// TODO : optimize by extracting the references from the variants ?
	if ( const Arc1* arc1 = boost::get<Arc1>( &A ) ){
	  Arc1 carc1;
	  Arc1 carc2;
	  CircularKernel().split_2_object()(*arc1, p, carc1, carc2);
	  ca1 = carc1;
	  ca2 = carc2;
	  return ;
	
	}
	else{
	  const Arc2* arc2 = boost::get<Arc2>( &A );
	  Arc2 cline1;
	  Arc2 cline2;
	  CircularKernel().split_2_object()(*arc2, p, cline1, cline2); 
	  ca1 = cline1;
	  ca2 = cline2;
	  return ;
	
	}
      }
    };


     template <class CircularKernel>
    class Variant_Construct_min_vertex_2 
      : public boost::static_visitor<const typename CircularKernel::Circular_arc_point_2&>
    {
      typedef typename CircularKernel::Circular_arc_point_2 Circular_arc_point_2; 
    
    public :
    
      typedef Circular_arc_point_2  result_type;
      //typedef const result_type&       qualified_result_type;
      
      template < typename T >
      //typename boost::remove_reference<qualified_result_type>::type
	Circular_arc_point_2
      operator()(const T &a) const
      {
	//CGAL_kernel_precondition(CircularKernel().compare_xy_2_object()(a.left(), a.right())==CGAL::SMALLER);
	return CircularKernel().construct_circular_min_vertex_2_object()(a);
      }
    };

    template <class CircularKernel, class Arc1, class Arc2>
    class Construct_min_vertex_2//: public Has_qrt
    {
      typedef typename CircularKernel::Circular_arc_point_2      Point_2;
    public:
      
      typedef Point_2                  result_type;
      //typedef const result_type&       qualified_result_type;
      
      //typename boost::remove_reference<qualified_result_type>::type 
      result_type
      operator() (const boost::variant< Arc1, Arc2 > & cv) const
      {
	return boost::apply_visitor( Variant_Construct_min_vertex_2<CircularKernel>(), cv );
      }
    };





     template <class CircularKernel>
    class Variant_Construct_max_vertex_2
       : public boost::static_visitor<const typename CircularKernel::Circular_arc_point_2&>
    {
      typedef typename CircularKernel::Circular_arc_point_2 Circular_arc_point_2;
    
    public :
    
      typedef Circular_arc_point_2  result_type;
      //typedef const result_type&       qualified_result_type;
    
      template < typename T >
      //typename boost::remove_reference<qualified_result_type>::type
	Circular_arc_point_2
      operator()(const T &a) const
      {
	//CGAL_kernel_precondition(CircularKernel().compare_xy_2_object()(a.left(), a.right())==CGAL::SMALLER);
	return (CircularKernel().construct_circular_max_vertex_2_object()(a));
      }
    };


    
    template <class CircularKernel, class Arc1, class Arc2>
    class Construct_max_vertex_2//: public Has_qrt
    {
      typedef typename CircularKernel::Circular_arc_point_2      Point_2;
    public:
      /*!
       * Get the right endpoint of the x-monotone curve (segment).
       * \param cv The curve.
       * \return The right endpoint.
       */
      typedef Point_2                  result_type;
      //typedef const result_type&       qualified_result_type; 
       
       //typename boost::remove_reference<qualified_result_type>::type
      result_type
       operator() (const boost::variant< Arc1, Arc2 > & cv) const
      {
	return boost::apply_visitor( Variant_Construct_max_vertex_2<CircularKernel>(), cv );
      }
    };

      template <class CircularKernel>
    class Variant_Is_vertical_2
      : public boost::static_visitor<bool>
    {
    public :

      template < typename T >
      bool
      operator()(const T &a) const
      {
	return CircularKernel().is_vertical_2_object()(a);
      }
    };

    template <class CircularKernel, class Arc1, class Arc2>
    class Is_vertical_2
    {
    public:
      typedef bool result_type;

      bool operator() (const boost::variant< Arc1, Arc2 >& cv) const
      {
	return boost::apply_visitor( Variant_Is_vertical_2<CircularKernel>(), cv );
      }
    };

  }
  /// Traits class for CGAL::Arrangement_2 (and similar) based on a CircularKernel.

    template < typename CircularKernel, typename Arc1, typename Arc2>
    class Arr_circular_line_arc_traits {

    typedef Arr_circular_line_arc_traits< CircularKernel, Arc1, Arc2 >   Self;

  public:
  
    typedef CircularKernel Kernel;
    typedef typename CircularKernel::Circular_arc_point_2      Circular_arc_point_2;

    typedef typename CircularKernel::Circular_arc_point_2      Point;
    typedef typename CircularKernel::Circular_arc_point_2      Point_2;
  
    typedef CGAL::Tag_false                        Has_left_category;
    typedef CGAL::Tag_false 			 Has_merge_category;
  
    typedef boost::variant< Arc1, Arc2 > Curve_2;
    typedef boost::variant< Arc1, Arc2 > X_monotone_curve_2;

  private:
    CircularKernel ck;
  public:
  
    Arr_circular_line_arc_traits(const CircularKernel &k = CircularKernel())
      : ck(k) {}

    typedef typename CircularKernel::Compare_x_2           Compare_x_2;
    typedef typename CircularKernel::Compare_xy_2          Compare_xy_2;
    typedef typename VariantFunctors::Construct_min_vertex_2<CircularKernel, Arc1, Arc2>  Construct_min_vertex_2;
    typedef VariantFunctors::Construct_max_vertex_2<CircularKernel, Arc1, Arc2>  Construct_max_vertex_2;
    typedef VariantFunctors::Is_vertical_2<CircularKernel, Arc1, Arc2>           Is_vertical_2;
    typedef VariantFunctors::Compare_y_at_x_2<CircularKernel, Arc1, Arc2>      Compare_y_at_x_2;
    typedef VariantFunctors::Compare_y_to_right_2<CircularKernel, Arc1, Arc2>  Compare_y_at_x_right_2; 
    typedef VariantFunctors::Equal_2<CircularKernel, Arc1, Arc2>               Equal_2;
    typedef VariantFunctors::Make_x_monotone_2<CircularKernel, Arc1, Arc2>     Make_x_monotone_2;
    typedef VariantFunctors::Split_2<CircularKernel, Arc1, Arc2>               Split_2;
    typedef VariantFunctors::Intersect_2<CircularKernel, Arc1, Arc2> Intersect_2;

  
 Compare_x_2 compare_x_2_object() const
  { return ck.compare_x_2_object(); }

  Compare_xy_2 compare_xy_2_object() const
  { return ck.compare_xy_2_object(); }

  Compare_y_at_x_2 compare_y_at_x_2_object() const 
  { return Compare_y_at_x_2(); }

  Compare_y_at_x_right_2 compare_y_at_x_right_2_object() const 
  { return Compare_y_at_x_right_2(); }

  Equal_2 equal_2_object() const
  { return Equal_2(); }

  Make_x_monotone_2 make_x_monotone_2_object() const
  { return Make_x_monotone_2(); }

  Split_2 split_2_object() const
  { return Split_2(); }

  Intersect_2 intersect_2_object() const
    { return Intersect_2(); }

  Construct_min_vertex_2 construct_min_vertex_2_object() const
    { return Construct_min_vertex_2(); }

  Construct_max_vertex_2 construct_max_vertex_2_object() const
    { return Construct_max_vertex_2(); }

  Is_vertical_2 is_vertical_2_object() const
    { return Is_vertical_2();}


};

} // namespace CGAL

#endif // CGAL_VARIANT_TRAITS_H
