#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include "Tree.hpp"
#include "Node.hpp"

namespace nbody {
	using namespace std;

	Tree::Tree() {
		//insert dummy root node
		this->nodes = new Node(this);
		this->maxLeafBodies = 16;
	}

	Tree::~Tree() {
		this->clean();
		delete this->nodes;
	}

	void Tree::clean() {
		//remove all nodes; refresh root node
		while (this->nodes->next != this->nodes) {
			Node* node = this->nodes->next;

			node->unlink();
			delete node;
		}
		delete this->nodes;
		this->nodes = new Node(this);
	}

	bool Tree::isCorrect() {
		Node* current = this->nodes->next;

		while (current != this->nodes) {
			if (!current->isCorrect()) {
				return false;
			}
			current = current->next;
		}
		return true;
	}

	unsigned long Tree::numberOfNodes() {
		unsigned long nodes = 0;

		for (Node* node = this->nodes->next; node != this->nodes; node = node->next) {
			nodes++;
		}
		return nodes;
	}

	void Tree::accumulateForceOnto(Body& body) {
		Node* n = this->nodes->next;

		body.resetAcceleration();
		while (n != this->nodes) {
			if (n->sufficientForBody(body)) {
				n->representative.accumulateForceOnto(body);
			} else if (n->leaf) {
				for (vector<Body>::iterator it = n->bodies.begin(); it != n->bodies.end(); it++) {
					it->accumulateForceOnto(body);
				}
			}
			n = n->afterSubtree;
		}
	}

	void Tree::computeForces() {
		for (Node* n = this->nodes->next; n != this->nodes; n = n->next) {
			if (n->leaf) {
				for (vector<Body>::iterator it = n->bodies.begin(); it != n->bodies.end(); it++) {
					this->accumulateForceOnto(*it);
				}
			}
		}
	}

	vector<Body> Tree::dubinskiParse(string filename) {
		vector<Body> result;
		string line;
		ifstream infile(filename.c_str(), ifstream::in);
		double mass, px, py, pz;

		while (infile >> mass >> px >> py >> pz) {
			Body b(px, py, pz, mass);

			result.push_back(b);
		}
		infile.close();
		return result;
	}

	vector<Body> Tree::extractLocalBodies() {
		vector<Body> result;

		while (this->nodes->next != this->nodes) {
			if (this->nodes->next->leaf) {
				for (vector<Body>::iterator it = this->nodes->next->bodies.begin(); it != this->nodes->next->bodies.end(); it++) {
					if (!it->refinement) {
						result.push_back(*it);
					}
				}
			}
			Node* h = this->nodes->next;
			this->nodes->next->unlink();
			delete(h);
		}
		this->clean();
		return result;
	}

	vector<Body> Tree::copyRefinements(Box domain) {
		vector<Body> result;
		Node* current = this->nodes->next;

		while (current != this->nodes) {
			bool sufficient = current->sufficientForBox(domain);

			if (sufficient) {
				result.push_back(current->representative);
				current = current->afterSubtree;
			} else if (current->leaf) {
				result.insert(result.end(), current->bodies.begin(), current->bodies.end());
				current = current->next;
			} else {
				current = current->next;
			}
		}
		return result;
	}

	Box Tree::getRootBB() {
		return this->nodes->next->bb;
	}

	void Tree::rebuild(Box domain) {
		this->build(this->extractLocalBodies(), domain);
	}

	void Tree::advance() {
		for (Node* n = this->nodes->next; n != this->nodes; n = n->next) {
			if (n->leaf) {
				for (vector<Body>::iterator it = n->bodies.begin(); it != n->bodies.end(); it++) {
					if (!it->refinement) {
						it->integrate();
					}
				}
			}
		}
	}

	Box Tree::getLocalBB() {
		Box result;

		for (Node* n = this->nodes->next; n != this->nodes; n = n->next) {
			if (n->leaf) {
				for (vector<Body>::iterator it = n->bodies.begin(); it != n->bodies.end(); it++) {
					if (!it->refinement) {
						result.extend(*it);
					}
				}
			}
		}
		return result;
	}

	void Tree::print() {
		this->nodes->next->bb.print();
	}
}
