/*
    This file is a part of libcds - Concurrent Data Structures library

    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016

    Source code repo: http://github.com/khizmax/libcds/
    Download: http://sourceforge.net/projects/libcds/files/
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
*/

#ifndef CDSLIB_OPT_OPTIONS_H
#define CDSLIB_OPT_OPTIONS_H

/*
    Framework to define template options

    Editions:
        2011.01.23 khizmax  Created
*/

#include <cstdlib> // rand, srand

#include <cds/details/aligned_type.h>
#include <cds/user_setup/allocator.h>
#include <cds/user_setup/cache_line.h>
#include <cds/algo/atomic.h>

namespace cds {

/// Framework to define template options
/**
    There are two kind of options:
    - \p type-option - option that determines a data type. The template argument \p Type of the option is a type.
    - \p value-option - option that determines a value. The template argument \p Value of the option is a value.
*/
namespace opt {

    /// Type indicates that an option is not specified and the default one should be used
    struct none
    {
        //@cond
        template <class Base> struct pack: public Base
        {};
        //@endcond
    };

    /// Metafunction for selecting default option value
    /**
        Template parameters:
        - \p Option - option value
        - \p Default - default option value
        - \p Value - option value if \p Option is not opt::none

        If \p Option is opt::none, the metafunction result is \p Default, otherwise
        the result is \p Value.

        Examples:
        \code
        // default_spin is cds::sync::spin
        typedef typename cds::opt::select_default< cds::opt::none, cds::sync::spin >::type  default_spin;

        // spin_32bit is cds::sync::reentrant_spin32
        typedef typename cds::opt::select_default< cds::opt::none, cds::sync::reentrant_spin32 >::type  spin_32bit;
        \endcode
    */
    template <typename Option, typename Default, typename Value = Option>
    struct select_default
    {
        typedef Value type ;   ///< metafunction result
    };
    //@cond
    template <typename Default>
    struct select_default< none, Default >
    {
        typedef Default type;
    };
    //@endcond

    /// Metafunction to select option value
    /**
        This metafunction is intended for extracting the value of the \p Option option.
        For example,
        \code
        #include <cds/opt/options.h>
        #include <type_traits> // only for testing purpose (static_assert)

        struct tag_a;

        // Define option
        typedef cds::opt::tag< tag_a >  tag_option;

        // What is the value of the tag_option?
        // How we can extract tag_a from tag_option?
        // Here is a solution:
        typedef cds::opt::value< tag_option >::tag  tag_option_value;

        // tag_option_value is the same as tag_a
        static_assert( std::is_same< tag_option_value, tag_a >::value, "Error: tag_option_value != tag_a" );

        \endcode
    */
    template <typename Option>
    struct value: public Option::template pack<none>
    {};


    /// [type-option] Option setter specifies a tag
    /**
        Suppose, you have a struct
        \code
        struct Feature
        {  .... };
        \endcode
        and you want that your class \p X would be derived from several \p Feature:
        \code
            class X: public Feature, public Feature
            { .... };
        \endcode

        How can you distinguish one \p Feature from another?
        You may use a tag option:
        \code
            template <typename Tag>
            struct Feature
            { .... };

            class tag_a;
            class tag_b;
            class X: public Feature< tag_a >, public Feature< tag_b >
            { .... };
        \endcode
        Now you can distinguish one \p Feature from another:
        \code
            X x;
            Feature<tag_a>& fa = static_cast< Feature<tag_a> >( x );
            Feature<tag_b>& fb = static_cast< Feature<tag_b> >( x );
        \endcode

        \p tag option setter allows you to do things like this for an option-centric approach:
        \code
        template <typename ...Options>
        struct Feature
        { .... };

        class tag_a;
        class tag_b;
        class X: public Feature< tag<tag_a> >, public Feature< tag<tag_b> >
        { .... };
        \endcode

        This option setter is widely used in cds::intrusive containers to distinguish
        between different intrusive part of container's node.

        An incomplete type can serve as a \p Tag.
    */
    template <typename Tag>
    struct tag {
        //@cond
        template<class Base> struct pack: public Base
        {
            typedef Tag tag;
        };
        //@endcond
    };

    /// [type-option] Option setter specifies lock class
    /**
        Specification of the \p Type class is:
        \code
        struct Lock {
            void lock();
            void unlock();
        };
        \endcode
    */
    template <typename Type>
    struct lock_type {
        //@cond
        template<class Base> struct pack: public Base
        {
            typedef Type lock_type;
        };
        //@endcond
    };

    /// [type-option] @ref cds_sync_monitor "Monitor" type setter
    /**
        This option setter specifyes @ref cds_sync_monitor "synchronization monitor"
        for blocking container.
    */
    template <typename Type>
    struct sync_monitor {
        //@cond
        template <class Base> struct pack : public Base
        {
            typedef Type sync_monitor;
        };
        //@endcond
    };

    /// [type-option] Back-off strategy option setter
    /**
        Back-off strategy used in some algorithm.
        See cds::backoff namespace for back-off explanation and supported interface.
    */
    template <typename Type>
    struct back_off {
        //@cond
        template <class Base> struct pack: public Base
        {
            typedef Type back_off;
        };
        //@endcond
    };

    /// [type-option] Option setter for garbage collecting schema used
    /**
        Possible values of \p GC template parameter are:
        - cds::gc::HP - Hazard Pointer garbage collector
        - cds::gc::DHP - Dynamic Hazard Pointer garbage collector
        - cds::gc::none::GC - No garbage collector (not supported for some containers)
    */
    template <typename GC>
    struct gc {
        //@cond
        template <class Base> struct pack: public Base
        {
            typedef GC gc;
        };
        //@endcond
    };

    /// [type-option] Option setter for an allocator
    /**
        \p Type is allocator with \p std::allocator interface. Default is value of macro CDS_DEFAULT_ALLOCATOR
        that, in turn, is \p std::allocator.

        The \p libcds containers actively use rebinding to convert an allocator of one type to another. Thus,
        you may specify any valid type as std::allocator's template parameter.

        See also opt::node_allocator
    */
    template <typename Type>
    struct allocator {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type allocator;
        };
        //@endcond
    };

    /// [type-option] Option setter for node allocator
    /**
        \p Type is allocator with \p std::allocator interface. Default is value of macro CDS_DEFAULT_ALLOCATOR
        that, in turn, is \p std::allocator.

        Many node-base containers require an allocator for maintaining data (container's node) and for internal use.
        Sometimes, this types of allocator should be different for performance reason.
        For example, we should like to allocate the node from a pool of preallocated nodes.
        Such pool can be seen as the node allocator.

        Usually, if a container supports \p opt::allocator and \p %opt::node_allocator options
        and \p opt::node_allocator is not specified the \p %opt::allocator option is used for maintaining the nodes.

        The \p libcds containers actively use rebinding to convert an allocator of one type to another. Thus,
        you may specify any valid type as std::allocator's template parameter.
    */
    template <typename Type>
    struct node_allocator {
        //@cond
            template <typename Base> struct pack: public Base
            {
                typedef Type node_allocator;
            };
        //@endcond
    };

    /// [type-option] Option setter for item counting
    /**
        Some data structure (for example, queues) has additional feature for item counting.
        This option allows to set up appropriate item counting policy for that data structure.

        Predefined option \p Type:
        - \p atomicity::empty_item_counter - no item counting performed. It is default policy for many
            containers
        - \p atomicity::item_counter - the class that provides atomically item counting
        - \p opt::v::sequential_item_counter - simple non-atomic item counter. This item counter is not intended for
            concurrent containers and may be used only if it is explicitly noted.

        You may provide other implementation of \p atomicity::item_counter interface for your needs.

        Note, the item counting in lock-free containers cannot be exact; for example, if
        item counter for a container returns zero it is not mean that the container is empty.
        Thus, the item counter may be used for statistical purposes only.
    */
    template <typename Type>
    struct item_counter {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type item_counter;
        };
        //@endcond
    };

    /// Special alignment constants for \ref cds::opt::alignment option
    enum special_alignment {
        no_special_alignment = 0,   ///< no special alignment
        cache_line_alignment = 1    ///< use cache line size defined in cds/user_setup/cache_line.h
    };

    /// [value-option] Alignment option setter
    /**
        Alignment for some internal data of containers. May be useful to solve false sharing problem.
        \p Value defines desired alignment and it may be power of two integer or predefined values from
        \ref special_alignment enum.
    */
    template <unsigned int Value>
    struct alignment {
        //@cond
        template <typename Base> struct pack: public Base
        {
            enum { alignment = Value };
        };
        //@endcond
    };

    //@cond
    namespace details {
        template <typename Type, unsigned int Alignment>
        struct alignment_setter {
            typedef typename cds::details::aligned_type< Type, Alignment >::type  type;
        };

        template <typename Type>
        struct alignment_setter<Type, no_special_alignment> {
            typedef Type type;
        };

        template <typename Type>
        struct alignment_setter<Type, cache_line_alignment> {
            typedef typename cds::details::aligned_type< Type, c_nCacheLineSize >::type  type;
        };
    } // namespace details
    //@endcond

    /// Special padding constants for \p cds::opt::padding option
    enum special_padding {
        no_special_padding = 0,   ///< no special padding
        cache_line_padding = 1,   ///< use cache line size defined in cds/user_setup/cache_line.h

        /// Apply padding only for tiny data when data size is less than required padding
        /**
            The flag means that if your data size is less than the casheline size, the padding is applyed.
            Otherwise no padding will be applyed.

            This flag is applyed for padding value:
            \code
            cds::opt::padding< cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only >;
            cds::opt::padding< 256 | cds::opt::padding_tiny_data_only >;
            \endcode
        */
        padding_tiny_data_only = 0x80000000,

        //@cond
        padding_flags = padding_tiny_data_only
        //@endcond
    };

    /// [value-option] Padding option setter
    /**
        The padding for the internal data of some containers. May be useful to solve false sharing problem.
        \p Value defines desired padding and it may be power of two integer or predefined values from
        \p special_padding enum.
    */
    template <unsigned int Value>
    struct padding {
        //@cond
        template <typename Base> struct pack: public Base
        {
            enum { padding = Value };
        };
        //@endcond
    };

    //@cond
    namespace details {
        enum padding_vs_datasize {
            padding_datasize_less,
            padding_datasize_equal,
            padding_datasize_greater
        };

        template < typename T, unsigned int Padding, bool NoPadding, padding_vs_datasize Relation, bool TinyOnly >
        struct apply_padding_helper;

        template <typename T, padding_vs_datasize Relation, bool TinyOnly >
        struct apply_padding_helper < T, 0, true, Relation, TinyOnly >
        {
            struct type {
                T   data;
            };
            typedef void padding_type;
        };

        template <typename T, unsigned int Padding, bool TinyOnly >
        struct apply_padding_helper < T, Padding, false, padding_datasize_equal, TinyOnly >
        {
            struct type {
                T   data;
            };
            typedef void padding_type;
        };

        template <typename T, unsigned int Padding, bool TinyOnly >
        struct apply_padding_helper < T, Padding, false, padding_datasize_less, TinyOnly >
        {
            typedef uint8_t padding_type[Padding - sizeof( T )];
            struct type {
                T data;
                padding_type pad_;
            };

        };

        template <typename T, unsigned int Padding >
        struct apply_padding_helper < T, Padding, false, padding_datasize_greater, false >
        {
            typedef uint8_t padding_type[Padding - sizeof( T ) % Padding];
            struct type {
                T data;
                padding_type pad_;
            };
        };

        template <typename T, unsigned int Padding >
        struct apply_padding_helper < T, Padding, false, padding_datasize_greater, true >
        {
            struct type {
                T data;
            };
            typedef void padding_type;
        };

        template <typename T, unsigned int Padding >
        struct apply_padding
        {
        private:
            enum { padding = Padding & ~padding_flags };

        public:
            static CDS_CONSTEXPR const size_t c_nPadding =
                static_cast<unsigned int>(padding) == static_cast<unsigned int>(cache_line_padding) ? cds::c_nCacheLineSize :
                static_cast<unsigned int>(padding) == static_cast<unsigned int>(no_special_padding) ? 0 : padding;

            static_assert( (c_nPadding & (c_nPadding - 1)) == 0, "Padding must be a power-of-two number" );

            typedef apply_padding_helper< T,
                c_nPadding,
                c_nPadding == 0,
                sizeof( T ) < c_nPadding ? padding_datasize_less : sizeof( T ) == c_nPadding ? padding_datasize_equal : padding_datasize_greater,
                (Padding & padding_tiny_data_only) != 0
            > result;

            typedef typename result::type type;

            typedef typename std::conditional<
                std::is_same< typename result::padding_type, void >::value,
                unsigned int,
                typename result::padding_type
            >::type padding_type;
        };

    } // namespace details
    //@endcond


    /// [type-option] Generic option setter for statisitcs
    /**
        This option sets a type to gather statistics.
        The option is generic - no predefined type(s) is provided.
        The particular \p Type of statistics depends on internal structure of the object.
    */
    template <typename Type>
    struct stat {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type stat;
        };
        //@endcond
    };

    /// [type-option] Option setter for C++ memory model
    /**
        The <b>cds</b> library supports following memory ordering constraints for atomic operations in container implementation:
        - \p v::relaxed_ordering - relaxed C++ memory model. This mode supports full set of memory ordering constraints:
            \p memory_order_relaxed, \p memory_order_acquire, \p memory_order_release and so on.
        - \p v::sequential_consistent - sequentially consistent C++ memory model (default memory ordering for C++). In
            this mode any memory ordering constraint maps to \p memory_order_seq_cst.

        The \p Type template parameter can be \p v::relaxed_ordering or \p v::sequential_consistent.

        You may mix different memory ordering options for different containers: one declare as sequentially consistent,
        another declare as relaxed.
        Usually, \p v::relaxed_ordering is the default memory ordering for <b>libcds</b> containers.
    */
    template <typename Type>
    struct memory_model {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type memory_model;
        };
        //@endcond
    };

    /// [type-option] Base type traits option setter
    /**
        This option setter is intended generally for internal use for type rebinding.
    */
    template <typename Type>
    struct type_traits {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type type_traits;
        };
        //@endcond
    };

    /// Resizing policy option
    /**
        This option specifies the resizing policy that decides when to resize a container.
        Used in some containers, for example, in container::StripedHashSet, intrusive::StripedHashSet.

        The real resizing policy specified by \p Type does strongly depend on a container
        that supports this option, see container documentation about possibly \p Type values.
    */
    template <typename Type>
    struct resizing_policy {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type resizing_policy;
        };
        //@endcond
    };

    /// Copy policy option
    /**
        The copy policy defines an item copying algorithm which is used, for example, when a container is resized.
        It is very specific algorithm depending on type of the container.
    */
    template <typename Type>
    struct copy_policy {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type copy_policy;
        };
        //@endcond
    };

    /// Swap policy option
    /**
        The swap policy specifies an algorithm for swapping two objects.
        Usually, the default policy is \p std::swap (see opt::v::default_swap_policy):

        @code
        struct std_swap {
            template <typename T>
            void operator ()( T& v1, T& v2 )
            {
                std::swap( v1, v2 );
            }
        };
        @endcode
    */
    template <typename Type>
    struct swap_policy {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type swap_policy;
        };
        //@endcond
    };

    /// Move policy option
    /**
        The move policy specifies an algorithm for moving object content.
        In trivial case, it can be simple assignment.

        The move interface is:
        \code
        template <typename T>
        struct move_policy {
            void operator()( T& dest, T& src );
        };
        \endcode

        Note that in move algorithm the \p src source argument can be changed too.
        So you can use move semantics.

        Usually, the default move policy is opt::v::assignment_move_policy
    */
    template <typename Type>
    struct move_policy {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Type move_policy;
        };
        //@endcond
    };

    /// [value-option] Enable sorting
    /**
        This option enables (<tt>Enable = true</tt>) or disables (<tt>Enable == false</tt>)
        sorting of a container.
    */
    template <bool Enable>
    struct sort {
        //@cond
        template <typename Base> struct pack: public Base
        {
            static bool const sort = Enable;
        };
        //@endcond
    };

    /// [type-option] Concurrent access policy
    /**
        This option specifies synchronization strategy for fine-grained lock-based containers.
        The option has no predefined \p Policy type.
        For each container that accepts this option the range of available \p Policy types
        is unique.
    */
    template <typename Policy>
    struct mutex_policy {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Policy mutex_policy;
        };
        //@endcond
    };


    /// [type-option] Random number generator
    /**
        The option specifies a random number generator.
        \p Random can be any STL random number generator producing
        unsigned integer: \p std::linear_congruential_engine,
        \p std::mersenne_twister_engine, \p std::subtract_with_carry_engine
        and so on, or \p opt::v::c_rand.

    */
    template <typename Random>
    struct random_engine {
        //@cond
        template <typename Base> struct pack: public Base
        {
            typedef Random random_engine;
        };
        //@endcond
    };

    //@cond
    // For internal use
    template <typename Accessor>
    struct key_accessor {
        template <typename Base> struct pack: public Base
        {
            typedef Accessor key_accessor;
        };
    };

    template <typename Traits, typename ReplaceWith, typename WhatReplace = none >
    struct replace_key_accessor {
        typedef typename std::conditional<
            std::is_same< typename Traits::key_accessor, WhatReplace >::value,
            typename opt::key_accessor< ReplaceWith >::template pack< Traits >,
            Traits
        >::type type;
    };
    //@endcond

}}  // namespace cds::opt


// ****************************************************
// Options predefined types and values

namespace cds { namespace opt {

    /// Predefined options value
    namespace v {

        /// Sequential non-atomic item counter
        /**
            This type of \p opt::item_counter option is not intended for concurrent containers
            and may be used only if it is explicitly noted.
        */
        class sequential_item_counter
        {
        public:
            typedef size_t counter_type    ;  ///< Counter type
        protected:
            counter_type  m_nCounter ;      ///< Counter

        public:
            sequential_item_counter()
                : m_nCounter(0)
            {}

            /// Returns current value of the counter
            counter_type    value() const
            {
                return m_nCounter;
            }

            /// Same as \ref value() with relaxed memory ordering
            operator counter_type() const
            {
                return value();
            }

            /// Increments the counter. Semantics: postincrement
            counter_type inc()
            {
                return m_nCounter++;
            }

            /// Decrements the counter. Semantics: postdecrement
            counter_type dec()
            {
                return m_nCounter--;
            }

            /// Preincrement
            counter_type operator ++()
            {
                return inc() + 1;
            }
            /// Postincrement
            counter_type operator ++(int)
            {
                return inc();
            }

            /// Predecrement
            counter_type operator --()
            {
                return dec() - 1;
            }
            /// Postdecrement
            counter_type operator --(int)
            {
                return dec();
            }

            /// Resets count to 0
            void reset()
            {
                m_nCounter = 0;
            }
        };

        /// Relaxed memory ordering \p opt::memory_model
        /**
            In this ordering the memory constraints are defined according to C++ Memory Model specification:
            each constraint is mapped to \p std::memory_order constraints one-to-one
        */
        struct relaxed_ordering {
            //@cond
            static const atomics::memory_order memory_order_relaxed    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_consume    = atomics::memory_order_consume;
            static const atomics::memory_order memory_order_acquire    = atomics::memory_order_acquire;
            static const atomics::memory_order memory_order_release    = atomics::memory_order_release;
            static const atomics::memory_order memory_order_acq_rel    = atomics::memory_order_acq_rel;
            static const atomics::memory_order memory_order_seq_cst    = atomics::memory_order_seq_cst;
            //@endcond
        };

        /// Sequential consistent \p opt::memory_memory ordering
        /**
            In this memory model any memory constraint is equivalent to \p std::memory_order_seq_cst.
        */
        struct sequential_consistent {
            //@cond
            static const atomics::memory_order memory_order_relaxed    = atomics::memory_order_seq_cst;
            static const atomics::memory_order memory_order_consume    = atomics::memory_order_seq_cst;
            static const atomics::memory_order memory_order_acquire    = atomics::memory_order_seq_cst;
            static const atomics::memory_order memory_order_release    = atomics::memory_order_seq_cst;
            static const atomics::memory_order memory_order_acq_rel    = atomics::memory_order_seq_cst;
            static const atomics::memory_order memory_order_seq_cst    = atomics::memory_order_seq_cst;
            //@endcond
        };

        //@cond
        /// Totally relaxed \p opt::memory_model ordering (do not use!)
        /**
            In this memory model any memory constraint is equivalent to \p std::memory_order_relaxed.
            @warning Do not use this model! It intended for testing purposes only
            to verify debugging instruments like Thread Sanitizer.
        */
        struct total_relaxed_ordering {
            static const atomics::memory_order memory_order_relaxed    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_consume    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_acquire    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_release    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_acq_rel    = atomics::memory_order_relaxed;
            static const atomics::memory_order memory_order_seq_cst    = atomics::memory_order_relaxed;
        };
        //@endcond


        /// Default swap policy for \p opt::swap_policy option
        /**
            The default swap policy is wrappr around \p std::swap algorithm.
        */
        struct default_swap_policy {
            /// Performs swapping of \p v1 and \p v2 using \p std::swap algo
            template <typename T>
            void operator()( T& v1, T& v2 ) const
            {
                std::swap( v1, v2 );
            }
        };

        /// \p opt::move_policy based on move-assignment operator
        struct assignment_move_policy
        {
            /// <tt> dest = std::move( src ) </tt>
            template <typename T>
            void operator()( T& dest, T&& src ) const
            {
                dest = std::move( src );
            }
        };

        /// \p rand() -base random number generator for \p opt::random_engine
        /**
            This generator returns a pseudorandom integer in the range 0 to \p RAND_MAX (32767).
        */
        struct c_rand {
            typedef unsigned int result_type; ///< Result type

            /// Constructor initializes object calling \p std::srand()
            c_rand()
            {
                std::srand(1);
            }

            /// Returns next random number calling \p std::rand()
            result_type operator()()
            {
                return (result_type) std::rand();
            }
        };

    } // namespace v

}} // namespace cds::opt


// ****************************************************
// Options metafunctions

namespace cds { namespace opt {

    //@cond
    namespace details {
        template <typename OptionList, typename Option>
        struct do_pack
        {
            // Use "pack" member template to pack options
            typedef typename Option::template pack<OptionList> type;
        };

        template <typename ...T> class typelist;

        template <typename Typelist> struct typelist_head;
        template <typename Head, typename ...Tail>
        struct typelist_head< typelist<Head, Tail...> > {
            typedef Head type;
        };
        template <typename Head>
        struct typelist_head< typelist<Head> > {
            typedef Head type;
        };

        template <typename Typelist> struct typelist_tail;
        template <typename Head, typename ...Tail>
        struct typelist_tail< typelist<Head, Tail...> > {
            typedef typelist<Tail...> type;
        };
        template <typename Head>
        struct typelist_tail< typelist<Head> > {
            typedef typelist<> type;
        };

        template <typename OptionList, typename Typelist>
        struct make_options_impl {
            typedef typename make_options_impl<
                typename do_pack<
                    OptionList,
                    typename typelist_head< Typelist >::type
                >::type,
                typename typelist_tail<Typelist>::type
            >::type type;
        };

        template <typename OptionList>
        struct make_options_impl<OptionList, typelist<> > {
            typedef OptionList type;
        };
    }   // namespace details
    //@endcond

    /// make_options metafunction
    /** @headerfile cds/opt/options.h

        The metafunction converts option list \p Options to traits structure.
        The result of metafunction is \p type.

        Template parameter \p OptionList is default option set (default traits).
        \p Options is option list.
    */
    template <typename OptionList, typename... Options>
    struct make_options {
#ifdef CDS_DOXYGEN_INVOKED
        typedef implementation_defined type ;   ///< Result of the metafunction
#else
        typedef typename details::make_options_impl< OptionList, details::typelist<Options...> >::type type;
#endif
    };


    // *****************************************************************
    // find_type_traits metafunction
    // *****************************************************************

    //@cond
    namespace details {
        template <typename... Options>
        struct find_type_traits_option;

        template <>
        struct find_type_traits_option<> {
            typedef cds::opt::none  type;
        };

        template <typename Any>
        struct find_type_traits_option< Any > {
            typedef cds::opt::none type;
        };

        template <typename Any>
        struct find_type_traits_option< cds::opt::type_traits< Any > > {
            typedef Any type;
        };

        template <typename Any, typename... Options>
        struct find_type_traits_option< cds::opt::type_traits< Any >, Options... > {
            typedef Any type;
        };

        template <typename Any, typename... Options>
        struct find_type_traits_option< Any, Options... > {
            typedef typename find_type_traits_option< Options... >::type type;
        };
    } // namespace details
    //@endcond

    /// Metafunction to find opt::type_traits option in \p Options list
    /** @headerfile cds/opt/options.h

        If \p Options contains \p opt::type_traits option then it is the metafunction result.
        Otherwise the result is \p DefaultOptons.
    */
    template <typename DefaultOptions, typename... Options>
    struct find_type_traits {
        typedef typename select_default< typename details::find_type_traits_option<Options...>::type, DefaultOptions>::type type ;  ///< Metafunction result
    };


    // *****************************************************************
    // find_option metafunction
    // *****************************************************************

    //@cond
    namespace details {
        template <typename What, typename... Options>
        struct find_option;

        struct compare_ok;
        struct compare_fail;

        template <typename A, typename B>
        struct compare_option
        {
            typedef compare_fail type;
        };

        template <template <typename> class Opt, typename A, typename B>
        struct compare_option< Opt<A>, Opt<B> >
        {
            typedef compare_ok   type;
        };

        // Specializations for integral type of option
#define CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_( _type ) template <template <_type> class What, _type A, _type B> \
        struct compare_option< What<A>, What<B> > { typedef compare_ok type ; };

        // For user-defined enum types
#define CDS_DECLARE_FIND_OPTION_INTEGRAL_SPECIALIZATION( _type ) namespace cds { namespace opt { namespace details { CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(_type ) }}}

        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(bool)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(char)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(unsigned char)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(signed char)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(short int)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(unsigned short int)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(int)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(unsigned int)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(long)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(unsigned long)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(long long)
        CDS_FIND_OPTION_INTEGRAL_SPECIALIZATION_(unsigned long long)


        template <typename CompResult, typename Ok, typename Fail>
        struct select_option
        {
            typedef Fail    type;
        };

        template <typename Ok, typename Fail>
        struct select_option< compare_ok, Ok, Fail >
        {
            typedef Ok      type;
        };

        template <typename What>
        struct find_option< What > {
            typedef What    type;
        };

        template <typename What, typename Opt>
        struct find_option< What, Opt > {
            typedef typename select_option<
                typename compare_option< What, Opt >::type
                ,Opt
                ,What
            >::type type;
        };

        template <typename What, typename Opt, typename... Options>
        struct find_option< What, Opt, Options... > {
            typedef typename select_option<
                typename compare_option< What, Opt >::type
                ,Opt
                ,typename find_option< What, Options... >::type
            >::type type;
        };
    } // namespace details
    //@endcond

    /// Metafunction to find \p What option in \p Options list
    /** @headerfile cds/opt/options.h

        If \p Options contains \p What< Val > option for any \p Val then the result is \p What< Val >
        Otherwise the result is \p What.

        Example:
        \code
        #include <cds/opt/options.h>
        namespace co = cds::opt;

        struct default_tag;
        struct tag_a;
        struct tag_b;

        // Find option co::tag.

        // res1 is co::tag< tag_a >
        typedef co::find_option< co::tag< default_tag >, co::gc< cds::gc::HP >, co::tag< tag_a > >::type res1;

        // res2 is default co::tag< default_tag >
        typedef co::find_option< co::tag< default_tag >, co::less< x >, co::hash< H > >::type res2;

        // Multiple option co::tag. The first option is selected
        // res3 is default co::tag< tag_a >
        typedef co::find_option< co::tag< default_tag >, co::tag< tag_a >, co::tag< tag_b > >::type res3;

        \endcode
    */
    template <typename What, typename... Options>
    struct find_option {
        typedef typename details::find_option<What, Options...>::type   type ;  ///< Metafunction result
    };


    // *****************************************************************
    // select metafunction
    // *****************************************************************

    //@cond
    namespace details {

        template <typename What, typename... Pairs>
        struct select;

        template <typename What, typename Value>
        struct select< What, What, Value>
        {
            typedef Value   type;
        };

        template <typename What, typename Tag, typename Value>
        struct select<What, Tag, Value>
        {
            typedef What    type;
        };

        template <typename What, typename Value, typename... Pairs>
        struct select< What, What, Value, Pairs...>
        {
            typedef Value   type;
        };

        template <typename What, typename Tag, typename Value, typename... Pairs>
        struct select< What, Tag, Value, Pairs...>
        {
            typedef typename select<What, Pairs...>::type   type;
        };
    }   // namespace details
    //@endcond

    /// Select option metafunction
    /** @headerfile cds/opt/options.h

        Pseudocode:
        \code
        select <What, T1, R1, T2, R2, ... Tn, Rn> ::=
            if What == T1 then return R1
            if What == T2 then return R2
            ...
            if What == Tn then return Rn
            else return What
        \endcode
    */
    template <typename What, typename... Pairs>
    struct select {
        typedef typename details::select< What, Pairs...>::type  type    ;   ///< Metafunction result
    };

}}  // namespace cds::opt


#endif  // #ifndef CDSLIB_OPT_OPTIONS_H
