// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

#if !defined(CPPLINQ_LINQ_UTIL_HPP)
#define CPPLINQ_LINQ_UTIL_HPP
#pragma once

namespace cpplinq { namespace util {

    template <class Container>
    struct container_traits {
        typedef typename Container::iterator iterator;
        typedef typename std::iterator_traits<iterator>::value_type value_type;
        typedef typename std::iterator_traits<iterator>::iterator_category iterator_category;

        // TODO: conservative definition for now. 
        enum { is_writable_iterator = 
                   std::is_reference<typename std::iterator_traits<iterator>::reference>::value
                   && std::is_same<typename std::remove_cv<value_type>::type,
                                   typename std::remove_cv<typename std::remove_reference<typename std::iterator_traits<iterator>::reference>::type>::type>::value
        };
    };

    template <>
    struct container_traits<int>;

    template <class Container>
    struct container_traits<Container&>
        : container_traits<Container>
    {};
    template <class Container>
    struct container_traits<const Container>
        : container_traits<Container>
    {
        typedef typename Container::const_iterator iterator;
    };

    // Note: returns false if no partial order exists between two 
    // particular iterator categories, such as with some of the boost categories
    template <class Cat1, class Cat2>
    struct less_or_equal_iterator_category
    {
    private:
        typedef char yes;
        typedef struct { char c1,c2; } no;
        static yes invoke(Cat1);
        static no invoke(...);
    public:
        enum { value = (sizeof(invoke(Cat2())) == sizeof(yes)) };
    };

    // Return the weaker of the two iterator categories. Make sure 
    //   a non-standard category is in the second argument position, as 
    //   this metafunction will default to the first value if the order is undefined
    template <class Cat1, class Cat2>
    struct min_iterator_category
        : std::conditional<
            less_or_equal_iterator_category<Cat2, Cat1>::value,
            Cat2,
            Cat1>
    {
    };

#if 0
#define CppLinq_GET_ITERATOR_TYPE(TContainer) \
    decltype(begin(static_cast<TContainer*>(0)))
#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \
    decltype(begin(static_cast<const TContainer*>(0)))
#else
#define CppLinq_GET_ITERATOR_TYPE(TContainer) \
    typename ::cpplinq::util::container_traits<TContainer>::iterator
#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \
    typename ::cpplinq::util::container_traits<TContainer>::const_iterator
#endif

    // VC10's std::tr1::result_of is busted with lambdas. use decltype instead on vc10 and later
#if defined(_MSC_VER) && _MSC_VER >= 1600
    namespace detail {
        template <class T> T instance();
    };
    template <class Fn> struct result_of;
    template <class Fn>
    struct result_of<Fn()> {
        typedef decltype(detail::instance<Fn>()()) type;
    };
    template <class Fn, class A0>
    struct result_of<Fn(A0)> {
        typedef decltype(detail::instance<Fn>()(detail::instance<A0>())) type;
    };
    template <class Fn, class A0, class A1>
    struct result_of<Fn(A0,A1)> {
        typedef decltype(detail::instance<Fn>()(detail::instance<A0>(),
                                                detail::instance<A1>())) type;
    };
    template <class Fn, class A0, class A1, class A2>
    struct result_of<Fn(A0,A1,A2)> {
        typedef decltype(detail::instance<Fn>()(detail::instance<A0>(),
                                                detail::instance<A1>(),
                                                detail::instance<A2>())) type;
    };
    template <class Fn, class A0, class A1, class A2, class A3>
    struct result_of<Fn(A0,A1,A2,A3)> {
        typedef decltype(detail::instance<Fn>()(detail::instance<A0>(),
                                                detail::instance<A1>(),
                                                detail::instance<A2>(),
                                                detail::instance<A3>())) type;
    };
#elif defined(_MSC_VER)
    template <class T>
    struct result_of<T> : std::tr1::result_of<T> {};
#else
    using std::result_of;
#endif

    template<class Type>
    struct identity 
    {
        typedef Type type;
        Type operator()(const Type& left) const {return left;}
    };

    // faux pointer proxy for iterators that dereference to a value rather than reference, such as selectors
    template <class T>
    struct value_ptr
    {
        T value;
        value_ptr(const T& value) : value(value)
        {}
        value_ptr(const T* pvalue) : value(*pvalue)
        {}
        const T* operator->()
        {
            return &value;
        }
    };
     

    template <class T>
    class maybe
    {
        bool is_set;
        typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type 
            storage;
    public:
        maybe()
        : is_set(false)
        {
        }

        maybe(T value)
        : is_set(false)
        {
            new (reinterpret_cast<T*>(&storage)) T(value);
            is_set = true;
        }

        maybe(const maybe& other)
        : is_set(false)
        {
            if (other.is_set) {
                new (reinterpret_cast<T*>(&storage)) T(*other.get());
                is_set = true;
            }
        }
        maybe(maybe&& other)
        : is_set(false)
        {
            if (other.is_set) {
                new (reinterpret_cast<T*>(&storage)) T(std::move(*other.get()));
                is_set = true;
                other.reset();
            }
        }

        ~maybe()
        {
            reset();
        }

        void reset()
        {
            if (is_set) {
                is_set = false;
                reinterpret_cast<T*>(&storage)->~T();
            }
        }

        T* get() {
            return is_set ? reinterpret_cast<T*>(&storage) : 0;
        }

        const T* get() const {
            return is_set ? reinterpret_cast<const T*>(&storage) : 0;
        }

        void set(T value) {
            if (is_set) {
                *reinterpret_cast<T*>(&storage) = std::move(value);
            } else {
                new (reinterpret_cast<T*>(&storage)) T(std::move(value));
                is_set = true;
            }
        }

        T& operator*() { return *get(); }
        const T& operator*() const { return *get(); }
        T* operator->() { return get(); }
        const T* operator->() const { return get(); }

        maybe& operator=(const T& other) {
            set(other);
        }
        maybe& operator=(const maybe& other) {
            if (const T* pother = other.get()) {
                set(*pother);
            } else {
                reset();
            }
            return *this;
        }

        // boolean-like operators
        operator T*() { return get(); }
        operator const T*() const { return get(); }

    private:
        
    };
}}


#endif //CPPLINQ_UTIL_HPP