#ifndef TREE_NODE_HPP
#define TREE_NODE_HPP

#include <cstdlib>
#include <iterator>
#include <vector>

#include <boost/iterator/iterator_facade.hpp>

#include "Body.hpp"
#include "Box.hpp"

namespace nbody {
	class Tree;
	//class for storing node information
	class Node {
		friend class Tree;
		friend class BarnesHutTree;
	protected:
		std::vector<Body> bodies;
		Box bb;
		Node* prev{ this }; // TODO: use a vector for child relations
		Node* next{ this };
		Node* nextSibling{ nullptr };
		Node* prevSibling{ nullptr };
		Node* afterSubtree{ nullptr };
		bool leaf{ true };
		Tree* tree{ nullptr };
		Body representative;
	public:
		explicit Node(Tree* tree_);
		Node(Box bb_, std::vector<Body> bodies_, Tree* tree_);
		Node(std::vector<Body> bodies_, Tree* tree_);
		bool isSplitable() const;
		void extendBBforBodies();
		void extendBBtoCube();
		Box getBB() const;
		std::vector<Body> getBodies() const;
		void insertBefore(Node* node);
		void insertAfter(Node* node);
		void update();
		double getL() const;
		bool isCorrect() const;
		void print(std::size_t parallelId) const;
		bool sufficientForBody(const Body& body) const;
		bool sufficientForBox(Box box) const;

		// iterator classes for traversing of pointered node data structures
		class NodeIterator : public boost::iterator_facade<
			NodeIterator,
			Node,
			boost::bidirectional_traversal_tag> {
		public:
			NodeIterator() = default;
			explicit NodeIterator(Node * node) :node_(node) {}
		private:
			friend class boost::iterator_core_access;

			void increment() { node_ = node_->next; }
			void decrement() { node_ = node_->prev; }

			bool equal(NodeIterator const& other) const { return this->node_ == other.node_; }
			Node& dereference() const { return *node_; }
			Node* node_{ nullptr };
		};

		class SiblingNodeIterator : public boost::iterator_facade<
			SiblingNodeIterator,
			Node,
			boost::bidirectional_traversal_tag> {
		public:
			SiblingNodeIterator() = default;
			explicit SiblingNodeIterator(Node * node) :node_(node) {}
		private:
			friend class boost::iterator_core_access;

			void increment() { node_ = node_->nextSibling; }
			void decrement() { node_ = node_->prevSibling; }

			bool equal(SiblingNodeIterator const& other) const { return this->node_ == other.node_; }
			Node& dereference() const { return *node_; }
			Node* node_{ nullptr };
		};
	};
} // namespace nbody

#endif
