
#ifndef MAP_HPP
# define MAP_HPP

# include <memory>		// std::allocator
# include <cstddef>		// NULL, std::size_t, std::ptrdiff_t
# include <algorithm>	// std::max()
# include <functional>	// std::less, std::binary_function

# include "reverse_iterator.hpp"
# include "equal.hpp"
# include "lexicographical_compare.hpp"

# include "pair.hpp"
# include "map_node.hpp"
# include "map_iterator.hpp"

namespace ft {

template <
	class Key,												// map::key_type
	class T,												// map::mapped_type
	class Compare = std::less<Key>,							// map::key_compare
	class Alloc = std::allocator< ft::pair<const Key, T> >	// map::allocator_type
> class map {

public:

	typedef Key											key_type;
	typedef T											mapped_type;
	typedef pair<const Key, T>							value_type;
	typedef std::size_t									size_type;
	typedef std::ptrdiff_t								difference_type;
	typedef Compare										key_compare;
	typedef Alloc										allocator_type;

	typedef map_iterator<Key, T, Compare, Alloc>		iterator;
	typedef map_const_iterator<Key, T, Compare, Alloc>	const_iterator;
	typedef ft::reverse_iterator<iterator>				reverse_iterator;
	typedef ft::reverse_iterator<const_iterator>		const_reverse_iterator;


	/****************
	 * member class :
	 ****************/
	// https://en.cppreference.com/w/cpp/container/map/value_compare
	// https://stackoverflow.com/questions/4571355/why-would-one-use-nested-classes-in-c
	class value_compare : public std::binary_function<value_type, value_type, bool> {

		friend class map;
		protected:
			Compare comp;
			value_compare(Compare c) : comp(c) {}
		public:
			bool operator() (const value_type& x, const value_type& y) const
				{ return comp(x.first, y.first); }
	};


	/************
	 * copliens :
	 ************/
// constructors ------------------------------
	explicit map (const key_compare& comp = key_compare(),
		const allocator_type& alloc = allocator_type());
	template <class InputIterator>
		map (InputIterator first, InputIterator last,
			const key_compare& comp = key_compare(),
			const allocator_type& alloc = allocator_type());
	map(const map& x);
// destructor --------------------------------
	~map();
// operator= ---------------------------------
	map&	operator= (const map& x);


	/*************
	 * iterators :
	 *************/
// begin -------------------------------------
	iterator				begin();
	const_iterator			begin() const;
// end ---------------------------------------
	iterator				end();
	const_iterator			end() const;
// rbegin ------------------------------------
	reverse_iterator		rbegin();
	const_reverse_iterator	rbegin() const;
// rend --------------------------------------
	reverse_iterator		rend();
	const_reverse_iterator	rend() const;


	/************
	 * capacity :
	 ************/
// empty -------------------------------------
	bool		empty() const;
// size --------------------------------------
	size_type	size() const;
// max_size ----------------------------------
	size_type	max_size() const;


	/******************
	 * element access :
	 ******************/
// operator[] --------------------------------
	mapped_type &	operator[] (const key_type& k);


	/*************
	 * modifiers :
	 *************/
// insert ------------------------------------
	pair<iterator,bool>	insert (const value_type& val);
	iterator			insert (iterator position, const value_type& val);
	template <class InputIterator>
		void			insert (InputIterator first, InputIterator last);
// erase -------------------------------------
	void				erase (iterator position);
	size_type			erase (const key_type& k);
	void				erase (iterator first, iterator last);
// swap --------------------------------------
	void				swap (map& x);
// clear -------------------------------------
	void				clear();


	/*************
	 * observers :
	 *************/
// key_comp ----------------------------------
	key_compare		key_comp() const;
// value_comp --------------------------------
	value_compare	value_comp() const;


	/**************
	 * operations :
	 **************/
// find --------------------------------------
	iterator							find (const key_type& k);
	const_iterator						find (const key_type& k) const;
// count -------------------------------------
	size_type							count (const key_type& k) const;
// lower_bound -------------------------------
	iterator							lower_bound (const key_type& k);
	const_iterator						lower_bound (const key_type& k) const;
// upper_bound -------------------------------
	iterator							upper_bound (const key_type& k);
	const_iterator						upper_bound (const key_type& k) const;
// equal_range -------------------------------
	pair<const_iterator,const_iterator>	equal_range (const key_type& k) const;
	pair<iterator,iterator>				equal_range (const key_type& k);


	/*************
	 * allocator :
	 *************/
// get_allocator -----------------------------
	allocator_type	get_allocator() const;

private:
	size_type				_size;
	node<value_type>*		_root;
	sentinel<value_type>*	_sentinel;
	Compare					_comp;
	Alloc					_allocator;

	// https://stackoverflow.com/questions/14148756/what-does-template-rebind-do
	typename Alloc::template rebind< node<value_type> >::other _allocator_node;
	typename Alloc::template rebind< sentinel<value_type> >::other _allocator_sentinel;

	void					_init_sentinel();
	node<value_type>*		_subtree_shift(node<value_type>* st_old, node<value_type>* st_new);

	// BBST
	enum					{INSERT, ERASE};
	void					_balance(node<value_type>* n, bool action);
	short					_compute_height(node<value_type>* n);
	short					_balance_factor(node<value_type>* n);
	node<value_type>*		_rotate_left(node<value_type>* n);
	node<value_type>*		_rotate_right(node<value_type>* n);
 };


	/************************
	 * non-member functions :
	 ************************/
// operator == -------------------------------
template< class K, class T, class Comp, class Alloc > bool operator==
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// operator != -------------------------------
template< class K, class T, class Comp, class Alloc > bool operator!=
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// operator < --------------------------------
template< class K, class T, class Comp, class Alloc > bool operator<
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// operator <= -------------------------------
template< class K, class T, class Comp, class Alloc > bool operator<=
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// operator > --------------------------------
template< class K, class T, class Comp, class Alloc > bool operator>
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// operator >= -------------------------------
template< class K, class T, class Comp, class Alloc > bool operator>=
	( const std::map<K,T,Comp,Alloc>& lhs, const std::map<K,T,Comp,Alloc>& rhs );
// swap (map) -----------------------------
template< class Key, class T, class Compare, class Alloc > void swap
	( std::map<Key,T,Compare,Alloc>& lhs, std::map<Key,T,Compare,Alloc>& rhs );

} // namespace ft

# include "map.tpp"

// banlanced binary search tree :
// https://www.youtube.com/watch?v=vRwi_UcZGjU

// entinel node :
// https://en.wikipedia.org/wiki/Sentinel_node

#endif

