#ifndef BST_ITERATOR_HPP
# define BST_ITERATOR_HPP

# include <cstddef> // NULL, ptrdiff_t
# include <iterator> // iterator_tag

# include "pair.hpp"
# include "bst_node.hpp"

namespace ft {

template <
	typename Key,
	typename T,
	typename Compare,
	typename Allocator
> class Bst_iterator
{
	private:
		typedef Bst_iterator	Self;

	public:
		typedef std::bidirectional_iterator_tag		iterator_category;
		typedef pair<const Key, T>					value_type;
		typedef std::ptrdiff_t						difference_type;
		typedef value_type*							pointer;
		typedef value_type&							reference;

		Bst_iterator() : _node(), _sentinel() {}
		Bst_iterator(node<value_type>* n, node_sentinel<value_type>* sentinel) : _node(n), _sentinel(sentinel) {}
		//Bst_iterator(const Bst_iterator& src) : _node(src._node), _sentinel(src._sentinel) {} //implicit

		reference	operator*() const
			{ return _node->value; }
		pointer		operator->() const
			{ return &_node->value; }

		Self&	operator++()
		{
			if (_node == NULL)
				_node = _sentinel->child->min();
			else if (_node->right)
				_node = _node->right->min();
			else
			{
				node<value_type>*	up = _node->up;
				while (up != NULL && _node == up->right)
				{
					_node = up;
					up = up->up;
				}
				_node = up;
			}
			return *this;
		}

		Self&	operator--()
		{
			if (_node == NULL)
				_node = _sentinel->child->max();
			else if (_node->left)
				_node = _node->left->max();
			else
			{
				node<value_type>*	up = _node->up;
				while (up != NULL && _node == up->left)
				{
					_node = up;
					up = up->up;
				}
				_node = up;
			}
			return *this;
		}
		
		Self	operator++(int)
		{
			//Self old(*this);
			Self old = *this;
			++(*this);
			return old;
		}

		Self	operator--(int)
		{
			//Self old(*this);
			Self old = *this;
			--(*this);
			return old;
		}	

		node<value_type>*	getNode()
			{ return _node; }
		const node<value_type>*	getNode() const
			{ return _node; }
		const node_sentinel<value_type>*	getSentinel() const
			{ return _sentinel; }

		// TODO : friend Non-member functions syntaxe pas clair.
		friend bool operator==(const Self &lhs, const Self &rhs)
			{ return lhs._node == rhs._node; }
		friend bool operator!=(const Self &lhs, const Self &rhs)
			{ return !(lhs._node == rhs._node); }

	private:
		node<value_type>*			_node;
		node_sentinel<value_type>*	_sentinel;
};

template <
	typename Key,
	typename T,
	typename Compare,
	typename Allocator
> class Bst_const_iterator
{
	private:
		typedef Bst_const_iterator	Self;

	public:
		typedef std::bidirectional_iterator_tag		iterator_category;
		typedef pair<const Key, T>					value_type;
		typedef std::ptrdiff_t						difference_type;
		typedef const value_type*					pointer;
		typedef const value_type&					reference;

		Bst_const_iterator() : _node(), _sentinel() {}
		Bst_const_iterator(const node<value_type>* node, const node_sentinel<value_type>* sentinel) : _node(node), _sentinel(sentinel) {}
		//Bst_const_iterator(const Bst_const_iterator& src) : _node(src._node), _sentinel(src._sentinel) {} //implicit
		Bst_const_iterator(const Bst_iterator<Key, T, Compare, Allocator>& src) : _node(src.getNode()), _sentinel(src.getSentinel()) {}

		reference	operator*() const
			{ return _node->value; }
		pointer		operator->() const
			{ return &_node->value; }

		Self&	operator++()
		{
			if (_node == NULL)
				_node = _sentinel->child->min();
			else if (_node->right)
				_node = _node->right->min();
			else
			{
				node<value_type>*	up = _node->up;
				while (up != NULL && _node == up->right)
				{
					_node = up;
					up = up->up;
				}
				_node = up;
			}
			return *this;
		}

		Self&	operator--()
		{
			if (_node == NULL)
				_node = _sentinel->child->max();
			else if (_node->left)
				_node = _node->left->max();
			else
			{
				node<value_type>*	up = _node->up;
				while (up != NULL && _node == up->left)
				{
					_node = up;
					up = up->up;
				}
				_node = up;
			}
			return *this;
		}
		
		Self	operator++(int)
		{
			//Self old(*this);
			Self old = *this;
			++(*this);
			return old;
		}

		Self	operator--(int)
		{
			//Self old(*this);
			Self old = *this;
			--(*this);
			return old;
		}	

		node<value_type>*	getNode() const
			{ return _node; }

		friend bool operator==(const Self &lhs, const Self &rhs)
			{ return lhs._node == rhs._node; }
		friend bool operator!=(const Self &lhs, const Self &rhs)
			{ return !(lhs._node == rhs._node); }

	private:
		const node<value_type>*				_node;
		const node_sentinel<value_type>*	_sentinel;
};

} // namespace ft

#endif

