#include "BarnesHutTree.hpp"
#include "Node.hpp"
#include <iostream>

namespace nbody {
	using namespace std;

	BarnesHutTree::BarnesHutTree() {
	}

	BarnesHutTree::~BarnesHutTree() {
	}

	vector<Box> BarnesHutTree::splitBB(Node* node) {
		vector<Box> result;

		for (unsigned int i = 0; i < 8; i++) {
			Box current = node->getBB();

			for (unsigned int j = 0; j < 3; j++) {
				double middle = current.getMin(j) + (current.getMax(j) - current.getMin(j)) / 2.0;

				if (i & (1 << j)) {
					current.setMin(j, middle);
				} else {
					current.setMax(j, middle);
				}
			}
			result.push_back(current);
		}
		return result;
	}

	int BarnesHutTree::numberOfChildren() {
		return 8;
	}

	void BarnesHutTree::build(vector<Body> bodies) {
		Node* current;

		this->clean();
		if (bodies.empty()) return;
		//insert root node
		this->nodes->insertAfter(new Node(this));
		current = this->nodes->next;
		//assign bodies to root node
		current->bodies = bodies;
		//setup proper bounding box
		current->extendBBforBodies();
		current->extendBBtoCube();
		current->afterSubtree = current->next;

		//iterate over existing boxes and split if it contains too much bodies
		while (current != this->nodes) {
			if (current->isSplitable()) {
				vector<Box> subboxes = this->splitBB(current);
				current->leaf = false;

				for (vector<Box>::iterator it = subboxes.begin(); it != subboxes.end(); it++) {
					Node* child = new Node(this);

					child->parent = current;
					child->bb = *it;
					child->bodies = it->copyBodies(current->bodies);
					current->insertAfter(child);
					child->nextSibling = current->next;
					current->next->prevSibling = child;
					if (it != subboxes.begin()) {
						child->afterSubtree = child->next;
					} else {
						child->afterSubtree = current->afterSubtree;
					}
				}
				current->bodies.clear();
			}
			current = current->next;
		}
	}

}
