//
// Copyright (C) 2008, 2009 Francesco Salvestrini
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

#ifndef ELKLIB_LIBCXX_ITERATOR
#define ELKLIB_LIBCXX_ITERATOR

#include "elklib.h"
#include "cstddef"

namespace ktl {
	template<class Iterator> struct iterator_traits;
	template<class T>        struct iterator_traits<T *>;
	template<class Category,
		 class T,
		 class Distance = ptrdiff_t,
		 class Pointer = T *,
		 class Reference = T &> struct iterator;

	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 { };

	template <class InputIterator, class Distance>
	void advance(InputIterator & i, Distance n) {
		while(n > 0) {
			--n;
			++i;
		}
	}

	template <class InputIterator>
	typename iterator_traits<InputIterator>::difference_type
	distance(InputIterator first, InputIterator last)
	{
		typename iterator_traits<InputIterator>::difference_type d = 0;
		while (first++ !=last) {
			d++;
		}
		return d;
	}

	template <class Iterator> class reverse_iterator;

	template <class Iterator>
	bool operator==(const reverse_iterator<Iterator> & x,
			const reverse_iterator<Iterator> & y);

	template <class Iterator>
	bool operator<(const reverse_iterator<Iterator> & x,
		       const reverse_iterator<Iterator> & y);

	template <class Iterator>
	bool operator!=(const reverse_iterator<Iterator> & x,
			const reverse_iterator<Iterator> & y);

	template <class Iterator>
	bool operator>(const reverse_iterator<Iterator> & x,
		       const reverse_iterator<Iterator> & y);

	template <class Iterator>
	bool operator>=(const reverse_iterator<Iterator> & x,
			const reverse_iterator<Iterator> & y);

	template <class Iterator>
	bool operator<=(const reverse_iterator<Iterator> & x,
			const reverse_iterator<Iterator> & y);

	template <class Iterator>
	typename reverse_iterator<Iterator>::difference_type
	operator-(const reverse_iterator<Iterator> & x,
		  const reverse_iterator<Iterator> & y);

	template <class Iterator> reverse_iterator<Iterator>
	operator+(typename reverse_iterator<Iterator>::difference_type n,
		  const reverse_iterator<Iterator> &                   x);

	template <class Container> class back_insert_iterator;

	template <class Container>
	back_insert_iterator<Container> back_inserter(Container & x);

	template <class Container> class front_insert_iterator;

	template <class Container>
	front_insert_iterator<Container> front_inserter(Container & x);

	template <class Container> class insert_iterator;

	template <class Container, class Iterator>
	insert_iterator<Container> inserter(Container& x, Iterator i);

	template<class Iterator> struct iterator_traits {
		typedef typename Iterator::difference_type difference_type;
		typedef typename Iterator::value_type value_type;
		typedef typename Iterator::pointer pointer;
		typedef typename Iterator::reference reference;
		typedef typename Iterator::iterator_category iterator_category;
	};

	template<class T> struct iterator_traits<T *> {
		typedef ptrdiff_t                  difference_type;
		typedef T                          value_type;
		typedef T *                        pointer;
		typedef T &                        reference;
		typedef random_access_iterator_tag iterator_category;
	};

	template <class Category,
		  class T,
		  class Distance,
		  class Pointer,
		  class Reference>
	struct iterator {
		typedef T         value_type;
		typedef Distance  difference_type;
		typedef Pointer   pointer;
		typedef Reference reference;
		typedef Category  iterator_category;
	};

	template <class Iterator> class reverse_iterator :
		public iterator<typename iterator_traits<Iterator>::iterator_category,
				typename iterator_traits<Iterator>::value_type, typename iterator_traits<Iterator>::difference_type,
				typename iterator_traits<Iterator>::pointer,
				typename iterator_traits<Iterator>::reference> {
	protected:
		Iterator current;
		friend bool operator== <Iterator>(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y);
		friend bool operator< <Iterator>(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y);

	public:
		typedef Iterator iterator_type;

		reverse_iterator() : current() { };
		explicit reverse_iterator(Iterator x) : current(x) { }
		template<class U> reverse_iterator(const reverse_iterator<U> &x) : current(x.base()) { }

		Iterator base() const { return current; }

		typename iterator_traits<Iterator>::reference operator*() const { Iterator tmp = current; return *--tmp; }
		typename iterator_traits<Iterator>::pointer   operator->() const { return &(operator*()); }
		typename iterator_traits<Iterator>::reference operator[](typename iterator_traits<Iterator>::difference_type n) const{
			return current[-n-1];
		}

		reverse_iterator& operator++() { --current; return *this;	}
		reverse_iterator  operator++(int) {reverse_iterator tmp = *this; --current; return tmp; }
		reverse_iterator& operator--() { ++ current; return *this; }
		reverse_iterator  operator--(int) {reverse_iterator tmp = *this; ++current; return tmp; }

		reverse_iterator  operator+ (typename iterator_traits<Iterator>::difference_type n) const{
			reverse_iterator retval( *this );
			retval+=n;
			return retval;
		}
		reverse_iterator& operator+=(typename iterator_traits<Iterator>::difference_type n) {
			current -= n;
			return *this;
		}
		reverse_iterator operator- (typename iterator_traits<Iterator>::difference_type n) const{
			reverse_iterator retval( *this );
			retval-=n;
			return retval;
		}
		reverse_iterator& operator-=(typename iterator_traits<Iterator>::difference_type n) {
			current += n;
			return *this;
		}
	};

template <class Iterator> bool
operator==(const reverse_iterator<Iterator> & x,
	   const reverse_iterator<Iterator> & y)
{
	return x.base() == y.base();
}

template <class Iterator> bool
operator<(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return x.base() < y.base();
}
template <class Iterator> bool
operator!=(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return x.base() != y.base();
}
template <class Iterator> bool
operator>(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return x.base() > y.base();
}
template <class Iterator> bool
operator>=(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return x.base() >= y.base();
}
template <class Iterator> bool
operator<=(const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return x.base() <= y.base();
}
template <class Iterator> typename reverse_iterator<Iterator>::difference_type
operator-( const reverse_iterator<Iterator> & x, const reverse_iterator<Iterator> & y)
{
	return y.base() - x.base();
}
template <class Iterator> reverse_iterator<Iterator>
operator+(typename reverse_iterator<Iterator>::difference_type n, const reverse_iterator<Iterator> & x)
{
	return reverse_iterator<Iterator> (x.base() - n);
}

template <class Container> class back_insert_iterator :
	public iterator<output_iterator_tag,void,void,void,void>
{
protected:
	Container& container;
public:
	typedef Container container_type;
	explicit back_insert_iterator(Container& x):container(x) { }
	back_insert_iterator<Container> & operator=(const typename Container::value_type& value) {
		container.push_back(value);
		return *this;
	}
	back_insert_iterator<Container> & operator*() {
		return *this;
	}
	back_insert_iterator<Container> & operator++() {
		return *this;
	}
	back_insert_iterator<Container>  operator++(int) {
		return *this;
	}
};

template <class Container> back_insert_iterator<Container>
back_inserter(Container& x)
{
	return back_insert_iterator<Container>(x);
}

template <class Container> class front_insert_iterator
	: public iterator<output_iterator_tag,void,void,void,void>
{
protected:
	Container& container;
public:
	typedef Container container_type;
	explicit front_insert_iterator(Container& x): container(x) { }
	front_insert_iterator<Container> & operator=(const typename Container::value_type& value) {
		container.push_front(value);
		return *this;
	}

	front_insert_iterator<Container> & operator*()     { return *this; }
	front_insert_iterator<Container> & operator++()    { return *this; }
	front_insert_iterator<Container>   operator++(int) { return *this; }
};

template <class Container> front_insert_iterator<Container>
front_inserter(Container& x) {
	return front_insert_iterator<Container>(x);
}

template <class Container> class insert_iterator
	: public iterator<output_iterator_tag,void,void,void,void> {
protected:
	Container& container;
	typename Container::iterator iter;
public:
	typedef Container container_type;
	insert_iterator(Container &                  x,
			typename Container::iterator i) :
		container(x), iter(i) { }
	insert_iterator<Container> & operator=(const typename Container::value_type& value) {
		iter = container.insert(iter, value);
		++iter;
		return *this;
	}
	insert_iterator<Container> & operator*()     { return *this; }
	insert_iterator<Container> & operator++()    { return *this; }
	insert_iterator<Container>   operator++(int) { return *this; }
};

template <class Container, class Iterator> insert_iterator<Container>
inserter(Container & x, Iterator i) {
	return insert_iterator<Container>(x, typename Container::iterator(i));
}

}

#endif // ELKLIB_LIBCXX_ITERATOR
