// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  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 3 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, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/containers/rdtree.h
/// \brief A header containing the definition for a tree with a root and directed edges
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_RDTREE_H
#define FENNEC_CONTAINERS_RDTREE_H

#include <fennec/containers/list.h>
#include <fennec/containers/deque.h>
#include <fennec/containers/traversal.h>
#include <fennec/containers/pair.h>

#include <fennec/memory/allocator.h>

namespace fennec
{

///
/// \brief Rooted-Directed Tree
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ⛔     |
/// | dynamic     |      ✅     |
/// | homogeneous |      ✅     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ⛔     |
/// | space       | \emph{O(N)} |
/// | access      | \emph{O(1)} |
/// | find        | \emph{O(N)} |
/// | insertion   | \emph{O(1)} |
/// | deletion    | \emph{O(1)} |
///
/// \tparam TypeT Data type
/// \tparam AllocT Allocator Type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct rdtree {

// Definitions =========================================================================================================
private:
	struct node;

public:

	/// \name Definitions
	/// @{

	using value_t = TypeT;                                                    //!< the held value type
	using alloc_t = typename allocator_traits<AllocT>::template rebind<node>; //!< the underlying allocator type

	/// @}


	/// \name Constants
	/// @{

	static constexpr size_t root = 0;  //!< the id of the root node
	static constexpr size_t npos = -1; //!< the id of a null node

	/// @}

private:
	struct node {
		value_t value;
		size_t  parent, child, prev, next;
		size_t  depth, num_children;

		constexpr node()
			: value()
			, parent(npos), child(npos)
			, prev(npos), next(npos)
			, depth(0), num_children(0) {
		}

		template<typename...ArgsT>
		constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
			: value(fennec::forward<ArgsT>(args)...)
			, parent(p), child(c), prev(v), next(n)
			, depth(d), num_children(0) {
		}

		constexpr ~node() {
			parent       = npos;
			child        = npos;
			prev         = npos;
			next         = npos;
			depth        = 0;
			num_children = 0;
		}
	};

	using table_t = allocation<node, alloc_t>;
	using freed_t = list<size_t>;

public:

// Constructors ========================================================================================================

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Root Constructor, constructs the root node of the tree
	/// \tparam ArgsT The argument types
	/// \param args The arguments to construct the root with
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename...ArgsT>
	explicit constexpr rdtree(ArgsT&&...args)
		: _table(), _freed(), _size(1) {
		_table.reallocate(8);
		fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
	}

	///
	/// \brief Copy Constructor, copies the contents of \emph{tree}
	/// \param tree the rdtree to copy
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr rdtree(const rdtree& tree) {
		_table.reallocate(tree.size());

		// Stack used for depth-first traversal
		deque<size_t> visit;
		deque<size_t> stack;

		// Push the root to the visit queue if the tree isn't empty
		if (not tree.is_empty()) {
			stack.push_back(npos);
			visit.push_back(root);
		}

		// Iterate
		while (not visit.is_empty()) {

			// Pop the next node
			const size_t node = visit.front();
			visit.pop_front();

			// Get the next node and the child
			const size_t next  = tree.next(node);
			const size_t child = tree.child(node);

			// Fix stack
			while (tree.depth(node) < stack.size()) {
				stack.pop_back();
			}

			// Add this node
			stack.push_back(insert(stack.back(), npos, tree[node]));

			// Push the next node
			if (next != npos) {
				visit.push_front(next);
			}

			// Push the child
			if (child != npos) {
				visit.push_front(child);
			}
		}
	}

	///
	/// \brief Move Constructor, takes ownership over the contents of \emph{tree}
	/// \param tree the rdtree to move
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr rdtree(rdtree&& tree) noexcept
		: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
	}

	/// @}


// Assignment ==========================================================================================================

	/// \name Assignment Operators
	/// @{

	///
	/// \brief Copy Assignment Operator
	/// \param rhs the rdtree to copy
	/// \returns \emph{this} after copying the contents of \emph{rhs}
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr rdtree& operator=(const rdtree& rhs) {
		// TODO: properly invoke copy constructor for all elements
		for (value_t* it : this->_table) {
			fennec::destruct(it);
		}
		_table = rhs._table;
		_freed = rhs._freed;
		_size  = rhs._size;
		return *this;
	}

	///
	/// \brief Move Assignment Operator
	/// \param rhs the rdtree to move
	/// \returns \emph{this} after taking ownership over the contents of \emph{rhs}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr rdtree& operator=(rdtree&& rhs) noexcept {
		for (value_t* it : _table) {
			fennec::destruct(it);
		}
		_table = fennec::move(rhs._table);
		_freed = fennec::move(rhs._freed);
		_size  = rhs._size;
		return *this;
	}

	/// @}

// Properties ==========================================================================================================

	/// \name Properties
	/// @{

	///
	/// \returns The number of nodes in the tree
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t size() const {
		return _size;
	}

	///
	/// \returns The capacity of the underlying allocation
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t capacity() const {
		return _table.capacity();
	}

	///
	/// \returns \emph{true} when there are no nodes in the tree, \emph{false} otherwise
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool is_empty() const {
		return _size == 0;
	}

	/// @}


// Access ==============================================================================================================

	/// \name Access
	/// @{

	///
	/// \param i The id of the node to check
	/// \returns The id of the parent node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t parent(size_t i) const {
		if (i >= _table.capacity()) return npos;
		return i == npos ? npos : _table[i].parent;
	}

	///
	/// \param i The id of the node to check
	/// \param n The index of the child relative to the parent
	/// \returns The id of the child node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t child(size_t i, size_t n = 0) const {
		if (i >= _table.capacity() && n != npos) return npos;
		const size_t c = i == npos ? npos : _table[i].child;
		if (n != 0)
			return next(c, n == npos ? npos : n - 1);
		return c;
	}

	///
	/// \param i The id of the node to check
	/// \param n The index of the child relative to the parent
	/// \returns The id of the next node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t next(size_t i, size_t n = 0) const {
		if (i >= _table.capacity() && n != npos) return npos;
		if (i == npos) {
			return npos;
		}

		const size_t org = i;
		size_t nxt = _table[i].next;
		while (nxt != npos) {
			i = nxt;
			nxt = _table[i].next;
			if (n != npos) {
				if (n-- == 0) {
					break;
				}
			}
		}

		return i == org && n != npos ? npos : i;
	}

	///
	/// \param i The id of the node to check
	/// \param n The index of the child relative to the parent
	/// \returns The id of the previous node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t prev(size_t i, size_t n = 0) const {
		if (i >= _table.capacity()) return npos;
		if (i == npos) {
			return npos;
		}

		const size_t org = i;
		size_t prv = _table[i].prev;
		while (prv != npos) {
			i = prv;
			prv = _table[i].prev;
			if (n != npos) {
				if (n-- == 0) {
					break;
				}
			}
		}

		return i == org && n != npos ? npos : i;
	}

	///
	/// \param i the node to start at
	/// \returns the left-most child of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(\log N)}
	///
	constexpr size_t left_most(size_t i) const {
		if (i >= _table.capacity()) return npos;
		size_t n = i;
		if ((n = child(n)) == npos) {
			return i;
		}
		while (true) {
			const size_t p = n;
			if ((n = child(n)) == npos) {
				return p;
			}
		}
	}

	///
	/// \param i the node to start at
	/// \returns the right-most child of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(\log N)}
	///
	constexpr size_t right_most(size_t i) const {
		if (i >= _table.capacity()) return npos;
		if ((i = child(i)) == npos) {
			return npos;
		}
		while (true) {
			size_t n;
			while ((n = next(i)) != npos) {
				i = n;
			}
			n = i;
			if ((i = child(i)) == npos) {
				return n;
			}
		}
	}

	///
	/// \param i The id of the node to check
	/// \returns The depth of the node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t depth(size_t i) const {
		if (i >= _table.capacity()) return npos;
		return i == npos ? npos : _table[i].depth;
	}

	///
	/// \param i The id of the node to check
	/// \returns The number of children the node has
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t num_children(size_t i) const {
		if (i >= _table.capacity()) return 0;
		return i == npos ? 0 : _table[i].num_children;
	}

	///
	/// \returns The next node id were \emph{insert} or \emph{emplace} to be called
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t next_id() const {
		size_t i = _size;
		if (not _freed.is_empty()) {
			i = _freed.front();
		}
		return i;
	}

	///
	/// \param i The id of the node to access
	/// \returns A reference to the value of the node wrapped in an optional
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr value_t& operator[](size_t i) {
		return _table[i].value;
	}

	///
	/// \param i The id of the node to access
	/// \returns A const-qualified reference to the value of the node wrapped in an optional
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr const value_t& operator[](size_t i) const {
		return _table[i].value;
	}

	/// @}


// Modifiers ===========================================================================================================

	/// \name Modifiers
	/// @{

	///
	/// \brief Insertion, creates a node in the tree with parent \emph{parent}
	/// \param parent the parent node, if \emph{npos} sets the value of the root node
	/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
	/// \param val the value to insert
	/// \returns the index of the created node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
		return this->_insert(parent, next, val);
	}

	///
	/// \brief Insertion, creates a node in the tree with parent \emph{parent}
	/// \param parent the parent node, if \emph{npos} sets the value of the root node
	/// \param next the next node, as an index relative to the parent
	/// \param val the value to insert
	/// \returns the index of the created node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
		return this->_insert(parent, next, fennec::forward<value_t>(val));
	}

	///
	/// \brief tree insertion, copies the contents of tree into self with the root at the specified node location
	/// \param parent the parent node, if \emph{npos} sets the value of the root node
	/// \param next the next node, as an index relative to the parent
	/// \param tree the tree to insert
	/// \returns the index of the inserted root
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert(size_t parent, size_t next, const rdtree& tree) {
		list<pair<size_t, size_t>> visit;
		visit.push_front({ root, parent });
		size_t res = npos;

		while (not visit.is_empty()) {
			auto node = visit.front();
			visit.pop_front();

			size_t p = this->_insert(node.second, node.second == parent ? next : npos, tree[node.first]);

			res = (res == npos) ? p : res;

			size_t c = tree.child(node.first, npos);
			while (c != npos) {
				visit.push_front({ c, p });
				c = tree._table[c].prev;
			}
		}

		return res;
	}

	///
	/// \brief Insertion, creates a node in the tree with parent \emph{parent}
	/// \param parent the parent node, if \emph{npos} sets the value of the root node
	/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
	/// \param args the args to construct the value to insert
	/// \returns the index of the created node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename...ArgsT>
	constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
		return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
	}

	///
	/// \brief Swap two nodes
	/// \param i0 The id of the first node
	/// \param i1 The id of the second node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr void swap(size_t i0, size_t i1) {
		assertf(i0 != root and i1 != root, "Cannot Swap With Root");

		size_t p0 = parent(i0);
		size_t p1 = parent(i1);

		fennec::swap(_table[i0].parent,       _table[i1].parent);
		fennec::swap(_table[i0].child,        _table[i1].child);
		fennec::swap(_table[i0].next,         _table[i1].next);
		fennec::swap(_table[i0].prev,         _table[i1].prev);
		fennec::swap(_table[i0].depth,        _table[i1].depth);
		fennec::swap(_table[i0].num_children, _table[i1].num_children);

		if (child(p0) == i0) _table[p0].child = i1;
		if (child(p1) == i1) _table[p1].child = i0;
	}


	///
	/// \brief Erase a node in the tree and all of it's children
	/// \param i the index of the node
	constexpr void erase(size_t i) {
		_erase(i);
	}

	/// @}


// Traversal ===========================================================================================================

	/// \name Traversal
	/// @{

	///
	/// \brief Traverse the tree using a specified order and visiting functor
	///
	/// \details
	/// The visitor should accept a reference to a value of type \emph{TypeT} and a \emph{size_t} which contains the node's id.
	/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
	///
	/// \tparam OrderT The order with which to traverse the tree.
	/// \tparam VisitorT The visitor, should fulfill the signature \emph{uint8_t visit(TypeT&, size_t)}
	/// \param visit The visiting object
	/// \param i The node to start at
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<typename OrderT, typename VisitorT>
	constexpr void traverse(VisitorT&& visit, size_t i = root) {
		OrderT order;
		i = order(*this, i);
		while (i != npos) {
			uint8_t mode = traversal_control_continue;
			if (_table[i].value) {
				mode = visit(_table[i].value, i);
			}
			if (mode == traversal_control_break) {
				break;
			}
			i = order[*this, i, mode];
		}
	}

	///
	/// \brief Traverse the tree using a specified order and visiting functor
	///
	/// \details
	/// The visitor should accept a reference to a value of type \emph{TypeT} and a \emph{size_t} which contains the node's id.
	/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
	///
	/// \tparam OrderT The order with which to traverse the tree.
	/// \tparam VisitorT The visitor, should fulfill the signature \emph{uint8_t visit(TypeT&, size_t)}
	/// \param visit The visiting object
	/// \param i The node to start at
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<typename OrderT, typename VisitorT>
	constexpr void traverse(VisitorT&& visit, size_t i = root) const {
		OrderT order;
		i = order(*this, i);
		while (i != npos) {
			uint8_t mode = traversal_control_continue;
			if (_table[i].value) {
				mode = visit(_table[i].value, i);
			}
			if (mode == traversal_control_break) {
				break;
			}
			i = order[*this, i, mode];
		}
	}

	///
	/// \brief Traverser pattern for breadth-first traversal
	struct breadth_first {

		///
		/// \brief Traversal Init
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const rdtree&, size_t start) {
			head = start;
			return start;
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \param mode The mode specifying branch conditions
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
			if (node == npos) {
				return npos;
			}

			const size_t nxt = tree.next(node);
			const size_t chd = tree.next(node);

			if (nxt != npos && node != head) {
				visit.push_front(nxt);
			}

			if (chd != npos && mode != traversal_control_jump_over) {
				visit.push_back(chd);
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t head = npos;
	};

	///
	/// \brief Traverser pattern for pre-order traversal
	struct pre_order {

		///
		/// \brief Traversal Init
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const rdtree&, size_t start) {
			head = start;
			return start;
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \param mode The mode specifying branch conditions
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
			if (node == npos) {
				return npos;
			}

			const size_t nxt = tree.next(node);
			const size_t chd = tree.child(node);

			if (nxt != npos && node != head) {
				visit.push_front(nxt);
			}

			if (chd != npos && mode != traversal_control_jump_over) {
				visit.push_front(chd);
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = npos;
	};

	///
	/// \brief Traverser pattern for in-order traversal
	struct in_order {

		///
		/// \brief Traversal Init
		/// \param tree The tree we are operating on
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const rdtree& tree, size_t start) {
			head = start;
			return tree.left_most(start);
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
			if (node == npos) {
				return npos;
			}

			const size_t prnt = tree.parent(node);
			const size_t next = tree.next(node);
			if (node != head) {
				if (tree.child(prnt) == node) {
					visit.push_back(prnt);
					if (next != npos) {
						visit.push_back(tree.left_most(next));
					}
				} else if (next != npos) {
					visit.push_front(tree.left_most(next));
				}
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = npos;
	};

	///
	/// \brief Traverser pattern for post-order traversal
	struct post_order {

		///
		/// \brief Traversal Init
		/// \param tree The tree we are operating on
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const rdtree& tree, size_t start) {
			head = start;
			return tree.left_most(start);
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
			if (node == npos) {
				return npos;
			}

			const size_t prnt = tree.parent(node);
			const size_t next = tree.next(node);

			if (node != head) {
				if (next != npos) {
					visit.push_front(tree.left_most(next));
				} else {
					visit.push_front(prnt);
				}
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = npos;
	};

	/// @}


// Private Member Variables ============================================================================================
private:
	table_t _table;
	freed_t _freed;
	size_t  _size;


// Private Helpers =====================================================================================================
private:

	void _expand() {
		_table.reallocate(_table.capacity() * 2);
	}

	size_t _next_free() {
		size_t next = _size;
		if (not _freed.is_empty()) {
			next = _freed.front();
			_freed.pop_front();
		}
		if (_size >= capacity()) {
			_expand();
		}
		++_size;
		return next;
	}

	template<typename...ArgsT>
	constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) {
		if (_size == 0) {
			fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
			_size = 1;
			return root;
		}

		if (p == npos) {
			_table[root].value = value_t(fennec::forward<ArgsT>(args)...);
			_size = _size == 0 ? 1 : _size;
			return root;
		}

		size_t idx = _next_free();
		size_t nxt = child(p, n);
		size_t prv = n == npos ? npos : prev(n);

		++_table[p].num_children;
		if ((nxt == child(p) && n != npos) || nxt == npos) {
			_table[p].child = idx;
		}

		if (n == npos) {
			if (nxt != npos) {
				_table[nxt].next = idx;
			}
			fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward<ArgsT>(args)...);
		} else {
			if (nxt != npos) {
				_table[nxt].prev = idx;
			}
			if (prv != npos) {
				_table[prv].next = idx;
			}
			fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward<ArgsT>(args)...);
		}
		return idx;
	}

	constexpr void _erase(size_t i) {
		list<size_t> queue;
		queue.push_back(child(i));
		while (not queue.is_empty()) {
			size_t n = queue.front(); queue.pop_front();
			if (n == npos) continue;
			queue.push_back(next(n));
			queue.push_back(child(n));
			fennec::destruct(&_table[n]);
			_freed.push_back(n);
			--_size;
		}

		fennec::destruct(&_table[i]);
		if (i != root) _freed.push_back(i);
		--_size;
	}
};

}

#endif // FENNEC_CONTAINERS_RDTREE_H
