//                    (C) 2016 SiLeader.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)
#pragma once

#include <developer_settings.hpp>
#include <ncxx/utility/iterator_check.hpp>

#include <cstddef>

namespace STD
{
    //classes
    //iterator
    template<class Category, class T, class Distance=STD::ptrdiff_t, class Pointer=T*, class Reference=T&> struct iterator
    {
        using value_type=T;
        using difference_type=Distance;
        using pointer=Pointer;
        using reference=Reference;
        using iterator_category=Category;
    };

    //iterator tag
    struct input_iterator_tag{};
    struct output_iterator_tag{};
    struct forward_iterator_tag : public input_iterator_tag{};
    struct bidirectional_iterator_tag : public forward_iterator_tag{};
    struct random_access_iterator_tag : public bidirectional_iterator_tag{};

    //iterator traits
    template<class Iterator> struct iterator_traits
    {
        using difference_type=Iterator::difference_type;
        using value_type=Iterator::value_type;
        using pointer=Iterator::pointer;
        using reference=Iterator::reference;
        using iterator_category=Iterator::iterator_category;
    };

    template<class T> struct iterator_traits<T*>
    {
        using difference_type=STD::ptrdiff_t;
        using value_type=T;
        using pointer=T*;
        using reference=T&;
        using iterator_category=STD::random_access_iterator_tag;
    };

    template<class T> struct iterator_traits<const T*>
    {
        using difference_type=STD::ptrdiff_t;
        using value_type=T;
        using pointer=const T*;
        using reference=const T&;
        using iterator_category=STD::random_access_iterator_tag;
    };

    //functions
    //advance's detail
    namespace detail{
        template <class InputIterator, class Distance> void advance_impl(InputIterator& i, Distance n, std::input_iterator_tag)
        {
            if(n<0)return;
            for (; 0 < n; --n)++i;
        }

        template <class BidirectionalIterator, class Distance> void advance_impl(BidirectionalIterator& i, Distance n, std::bidirectional_iterator_tag)
        {
            if (n > 0){
                for (; 0 < n; --n)++i;
            }else{
                for (; n < 0; ++n)--i;
            }
        }

        template <class RandomAccessIterator, class Distance> void advance_impl(RandomAccessIterator& i, Distance n, std::random_access_iterator_tag)
        {
            i += n;
        }
    }

    template <class Iterator, class Distance> void advance(Iterator& i, Distance n)
    {
        detail::advance_impl(i, n, typename std::iterator_traits<Iterator>::iterator_category());
    }

    template<class BidirIt> BidirIt prev(BidirIt it, typename STD::iterator_traits<BidirIt>::difference_type n=1)
    {
        STD::advance(it, -n);
        return it;
    }

    template<class ForwardIt> BidirIt prev(ForwardIt it, typename STD::iterator_traits<ForwardIt>::difference_type n=1)
    {
        STD::advance(it, n);
        return it;
    }

    //distance's detail
    namespace detail{
        template <class InputIterator> typename std::iterator_traits<InputIterator>::difference_type distance_impl(InputIterator first, InputIterator last, std::input_iterator_tag)
        {
            typedef typename std::iterator_traits<InputIterator>::difference_type result_type;
            result_type n = 0;
            for (; first != last; ++first) {
                ++n;
            }
            return n;
        }

        template <class RandomAccessIterator> typename std::iterator_traits<RandomAccessIterator>::difference_type distance_impl(RandomAccessIterator first, RandomAccessIterator last, std::random_access_iterator_tag)
        {
            return last - first;
        }
    }

    template <class InputIterator> typename std::iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last)
    {
        return detail::distance_impl(first, last, typename std::iterator_traits<InputIterator>::iterator_category());
    }

}
