#ifndef BST_HPP
# define BST_HPP

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

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

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

namespace ft {

template <
	typename Key,
	typename T,
	typename Compare = std::less<Key>,
	typename Allocator = std::allocator< ft::pair<const Key, T> >
> class Bst {

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

	typedef Bst_iterator<Key, T, Compare, Allocator>		iterator;
	typedef Bst_const_iterator<Key, T, Compare, Allocator>	const_iterator;
	typedef ft::reverse_iterator<iterator>					reverse_iterator;
	typedef ft::reverse_iterator<const_iterator>			const_reverse_iterator;


	/************
	 * copliens :
	 ************/
// constructors ------------------------------
	explicit Bst(
		const key_compare& comp = Compare(),
		const Allocator& alloc = Allocator()
	);
	template < typename InputIt >
	    Bst(
			InputIt first, InputIt last,
			const key_compare& comp = Compare(),
			const allocator_type& alloc = Allocator()
		);
	Bst(const Bst& src);
// destructor --------------------------------
	~Bst();
// operator= ---------------------------------
	Bst&	operator=(const Bst& rhs);


	/*************
	 * 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& key);


	/*************
	 * modifiers :
	 *************/
// insert ------------------------------------
	pair<iterator, bool>	insert(const value_type& value);
	iterator				insert(iterator hint, const value_type& value);
	template < typename InputIt >
	    void				insert(InputIt first, InputIt last);
// erase -------------------------------------
	void					erase(iterator pos);
	size_type				erase(const key_type& key);
	void					erase(iterator first, iterator last);
// swap --------------------------------------
	void					swap(Bst& other);
// clear -------------------------------------
	void					clear();

	/**************
	 * operations :
	 **************/
// find --------------------------------------
	iterator		find(const key_type& key);
	const_iterator	find(const key_type& key) const;
// count -------------------------------------
	size_type		count(const key_type& key) const;

private:
	size_type					_size;
	node<value_type>*			_root;
	node_sentinel<value_type>*	_sentinel;
	Compare						_comp;
	Allocator					_allocator;

	// https://stackoverflow.com/questions/14148756/what-does-template-rebind-do
	typename Allocator::template
		rebind< node<value_type> >::other _allocator_node;
	typename Allocator::template
		rebind< node_sentinel<value_type> >::other _allocator_node_sentinel;

	void						_init_sentinel();
	pair<iterator, bool>		_insert(const value_type& value);
	node<value_type>*			_erase(iterator pos);
	node<value_type>*			_subtree_shift(
									node<value_type>* st_old,
									node<value_type>* st_new
								);

	// BBST
	// https://www.youtube.com/watch?v=vRwi_UcZGjU
	void						_insert_rebalancing(node<value_type>* n);
	void						_erase_rebalancing(node<value_type>* n);

	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
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator==(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator!=(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator<(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator>(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator<=(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);
	template < typename Key, typename T, typename Compare, typename Alloc >
		bool    operator>=(const Bst<Key,T,Compare,Alloc>& lhs, const Bst<Key,T,Compare,Alloc>& rhs);

	template < typename Key, typename T, typename Compare, typename Alloc >
		void    swap(Bst<Key,T,Compare,Alloc>& lhs, Bst<Key,T,Compare,Alloc>& rhs);

} // namespace ft

# include "bst.tpp"

// https://en.wikipedia.org/wiki/Binary_search_tree
// https://en.wikipedia.org/wiki/AVL_tree

// https://fr.wikipedia.org/wiki/Arbre_binaire_de_recherche
// https://fr.wikipedia.org/wiki/Arbre_AVL

// https://visualgo.net/en/bst
// https://visualgo.net/en/bst?slide=14-8 // --> to 14-13


#endif

