// Copyright (c) 2007,2008,2009,2010,2011 Max-Planck-Institute Saarbruecken (Germany), 
// and Tel-Aviv University (Israel).  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 Lesser 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$
// SPDX-License-Identifier: LGPL-3.0+
//
//
// Author(s)     : Eric Berberich <eric@mpi-inf.mpg.de>
//                 Pavel Emeliyanenko <asm@mpi-sb.mpg.de>

#ifndef CGAL_CURVED_KERNEL_VIA_ANALYSIS_2_IMPL_H
#define CGAL_CURVED_KERNEL_VIA_ANALYSIS_2_IMPL_H

/*!\file include/CGAL/Curved_kernel_via_analysis_2.h
 * \brief defines class \c Curved_kernel_via_analysis_2
 *  
 * Defines points and arcs supported by curves that can be analyzed.
 */

#include <CGAL/config.h>

#include <CGAL/tss.h>
#include <CGAL/Arr_enums.h>
#include <CGAL/Arr_tags.h>
#include <CGAL/Curved_kernel_via_analysis_2/Point_2.h>
#include <CGAL/Curved_kernel_via_analysis_2/Arc_2.h>

#include <CGAL/Curved_kernel_via_analysis_2/Non_x_monotone_arc_2.h>

#include <CGAL/Curved_kernel_via_analysis_2/Curve_interval_arcno_cache.h>
#include <CGAL/Curved_kernel_via_analysis_2/Curved_kernel_via_analysis_2_functors.h>

namespace CGAL {

namespace internal {

/*!\brief
 * Provides basic types for Curved_kernel_via_analysis_2
 */
template < class NewCKvA, class BaseCKvA, class CurveKernel_2,
        template <class, class> class FunctorBase =
             Curved_kernel_via_analysis_2_functors >
class Curved_kernel_via_analysis_2_base 
       : public FunctorBase< NewCKvA, BaseCKvA > {
    
public:
    //!\name Global types
    //!@{
    
    //! type of curve kernel
    typedef CurveKernel_2 Curve_kernel_2;

    //! rebinds functor base to a new CKvA
    template <class X>
    struct rebind {

        typedef FunctorBase<X, typename
            BaseCKvA::Curved_kernel_via_analysis_2> Functor_base;      
    };

    //!\name Embedded types to fulfill \c ArrangementTraits_2 concept

    //! type of curve that can be analyzed
    typedef typename Curve_kernel_2::Curve_analysis_2 Curve_2;

    //! type of non x-monotone arc on a curve that can be analyzed
    typedef internal::Non_x_monotone_arc_2<NewCKvA>
             Non_x_monotone_arc_2;

    //! the multiplicity type
    typedef unsigned int Multiplicity;
    
    //!@}

public:

    //!\name Tags
    //!@{
    
    //! tag specifies that "to the left of" comparisons are supported
    typedef CGAL::Tag_true Has_left_category;

    //! tag specifies that merge and split functors are supported
    typedef CGAL::Tag_true Has_merge_category; 

    typedef CGAL::Tag_false Has_do_intersect_category;

    typedef Arr_open_side_tag Left_side_category;
    typedef Arr_open_side_tag Bottom_side_category;
    typedef Arr_open_side_tag Top_side_category;
    typedef Arr_open_side_tag Right_side_category;

    //!@}

public:
    //!\name Caching
    
    //!@{
    
    //! type of inverval arcno cache
    typedef internal::Curve_interval_arcno_cache< Curve_kernel_2 > 
        Curve_interval_arcno_cache;

    //!@}
    
public:
    //! \name Constructors
    //!@{

    //! default constructor
    Curved_kernel_via_analysis_2_base() :
        _m_kernel(Curve_kernel_2()) {
    }

    //! construct using specific Curve_kernel_2 instance \c kernel
    Curved_kernel_via_analysis_2_base(const Curve_kernel_2& kernel) :
        _m_kernel(kernel) {
    }
    
    //!@}

    //!\name underlying curve kernel + caching
    //!@{
    
    /*!\brief
     * access to static Curve_interval_arcno_cache
     */
    const Curve_interval_arcno_cache& interval_arcno_cache() const {
        return _m_interval_arcno_cache;
    }
            
    /*!\brief
     * instance of internal Curve_kernel_2 instance
     *
     * \return 
     */
    Curve_kernel_2& kernel() const {
        return _m_kernel;
    }

    //!@}

protected:
    //!\name private members
    //!@{
    
    //! an instance of \c Curve_kernel_2
    mutable Curve_kernel_2 _m_kernel;
    
    //! an instance of \c Curve_interval_arcno_cache
    mutable Curve_interval_arcno_cache _m_interval_arcno_cache;
    
    //!@}

public:
    //!\name Static Member to provide CKvA instance
    //!@{

    /*!\brief
     * a default instance of \c Curved_kernel_via_analysis_2
     *
     * \return static instance of \c Curved_kernel_via_analysis_2
     */
    static NewCKvA& instance() {
        return set_instance(_set_instance());
    }
    
    /*!\brief
     * sets static instance of \c Curved_kernel_via_analysis_2 to \c ckva
     *
     * \param ckva The instance that should be stored
     * \return the stored instance
     */
    static NewCKvA& set_instance(
            const NewCKvA& ckva
    ) {
      CGAL_STATIC_THREAD_LOCAL_VARIABLE_0(NewCKvA, instance);
      CGAL_STATIC_THREAD_LOCAL_VARIABLE_0(NewCKvA, binstance);
        
        if (&ckva == &_reset_instance()) {
            instance = binstance; 
        } else if (&ckva != &_set_instance()) {
            binstance = instance;
            instance = ckva;
        }
        return instance;
        
    }
    
    /*!\brief
     * resets static instance to original one
     */ 
    static void reset_instance() {
        set_instance(_reset_instance());
    }
    
private:
    /*!\brief 
     * sets instance to default for internal purposes
     */
    static NewCKvA& _set_instance() {
      CGAL_STATIC_THREAD_LOCAL_VARIABLE_0(NewCKvA, instance);
        return instance;
        
    }
    
    /*!\brief 
     * sets instance to default for internal purposes
     */
    static NewCKvA& _reset_instance() {
      CGAL_STATIC_THREAD_LOCAL_VARIABLE_0(NewCKvA, instance);
        return instance;
    }
    
    //!@}
};    
    
} // namespace internal

template < class CurveKernel_2 >
class Curved_kernel_via_analysis_2 :
    public internal::Curved_kernel_via_analysis_2_base<
        Curved_kernel_via_analysis_2<CurveKernel_2>,
        void, CurveKernel_2 > 
{
public: 
    //! type of curve kernel
    typedef CurveKernel_2 Curve_kernel_2;

    //! this instance itself
    typedef Curved_kernel_via_analysis_2< Curve_kernel_2 > Self;
    
    //! redefine rebind to terminate unrolling nested templates 
    template < class X >
    struct rebind {

        typedef internal::Curved_kernel_via_analysis_2_functors< X >
             Functor_base;      
    };

public:
    //!\name Embedded types to fulfill \c ArrangementTraits_2 concept
    //!@{

    typedef internal::Point_2< Self > Point_2;
    
    typedef internal::Arc_2< Self > Arc_2;
    
    //! type of weakly x-monotone arc for \c ArrangementTraits_2
    typedef Arc_2 X_monotone_curve_2;

    //!@}
protected:
    //!\name Protected base types
    //!@{
    
    //! class collecting basic types
    typedef internal::Curved_kernel_via_analysis_2_base < Self, void,
         CurveKernel_2 > Base_kernel;

    //!@}
public:
    //! \name Constructors
    //!@{

    /*!\brief
     * default constructor
     */
    Curved_kernel_via_analysis_2() :
      Base_kernel(Curve_kernel_2::get_static_instance()) {
    }
    
    /*!\brief
     * construct from \c kernel
     *
     * \param kernel Kernel to use internally
     */
    Curved_kernel_via_analysis_2(const Curve_kernel_2& kernel) :
        Base_kernel(kernel) {
    }
    
    //!@}
}; 

} //namespace CGAL

#endif // CGAL_CURVED_KERNEL_VIA_ANALYSIS_2_IMPL_H
// EOF
