#ifndef TREE_HPP
#define TREE_HPP

#include <cstdlib>
#include <string>
#include <vector>

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

namespace nbody {
	class Simulation;

	//superclass for Barnes-Hut tree
	class Tree {
		friend class Node;
	protected:
		Node* nodes;
		std::size_t maxLeafBodies{ 0 };
		std::size_t parallelId{ 0 };
		Simulation* simulation{ nullptr };
	public:
		Tree(std::size_t parallelId_);
		virtual ~Tree();
		virtual void setSimulation(Simulation* simulation_);
		virtual void clean();
		virtual void build(const std::vector<Body>& bodies) = 0;
		virtual void build(const std::vector<Body>& bodies, const Box& domain) = 0;
		virtual std::size_t numberOfChildren() const = 0;
		virtual std::size_t numberOfNodes() const;
		virtual bool isCorrect() const;
		virtual void accumulateForceOnto(Body& body);
		virtual void computeForces();
		virtual std::vector<Body> extractLocalBodies();
		virtual std::vector<Body> copyRefinements(const Box& domain) const;
		virtual void rebuild(const Box& domain);
		virtual void rebuild(const Box& domain, const std::vector<Body>& bodies);
		virtual void rebuild();
		virtual Box getRootBB() const;
		virtual void print(std::size_t parallelId) const;
		virtual Box advance();
		virtual Box getLocalBB() const;
	protected:
		// represents a traversable view over nodes->next
		class NodesView {
			Tree* tree_;
		public:
			NodesView(Tree* tree) : tree_(tree) {};
			auto begin()        { return                            Node::NodeIterator{ tree_->nodes->next }; }
			auto end()          { return                            Node::NodeIterator{ tree_->nodes }; }
			auto begin() const  { return                            Node::NodeIterator{ tree_->nodes->next }; }
			auto end() const    { return                            Node::NodeIterator{ tree_->nodes }; }
			auto rbegin()       { return std::make_reverse_iterator(Node::NodeIterator{ tree_->nodes->prev }); }
			auto rend()         { return std::make_reverse_iterator(Node::NodeIterator{ tree_->nodes }); }
			auto rbegin() const { return std::make_reverse_iterator(Node::NodeIterator{ tree_->nodes->prev }); }
			auto rend() const   { return std::make_reverse_iterator(Node::NodeIterator{ tree_->nodes }); }
		};
		NodesView nodesView;

		// represents a traversable view over nodes->nextSibling
		class SiblingNodesView {
			Node* start_;
		public:
			SiblingNodesView(Node* start) : start_(start) {}
			auto begin()       { return Node::SiblingNodeIterator{ start_->next }; }
			auto end()         { return Node::SiblingNodeIterator{ nullptr }; }
			auto begin() const { return Node::SiblingNodeIterator{ start_->next }; }
			auto end() const   { return Node::SiblingNodeIterator{ nullptr }; }
		};
		SiblingNodesView getSiblingNodesView(Node* start) { return{ start }; }
	};
} // namespace nbody

#endif
