#include "ApidMatcher.h"
#include "PacketMatchTree.h"
#include "ServiceMatcher.h"
#include "SubserviceMatcher.h"

// This should be configurable..
const LocalPool::LocalPoolConfig PacketMatchTree::poolConfig = {
		{10, sizeof(ServiceMatcher)},
		{20, sizeof(SubServiceMatcher)},
		{2, sizeof(ApidMatcher)},
		{40, sizeof(PacketMatchTree::Node)}
};

PacketMatchTree::PacketMatchTree(Node* root) :
		MatchTree<TmPacketMinimal*>(root, 2),
		factoryBackend(0, poolConfig, false, true),
		factory(&factoryBackend) {
}

PacketMatchTree::PacketMatchTree(iterator root) :
		MatchTree<TmPacketMinimal*>(root.element, 2),
		factoryBackend(0, poolConfig, false, true),
		factory(&factoryBackend) {
}

PacketMatchTree::PacketMatchTree() :
		MatchTree<TmPacketMinimal*>((Node*) NULL, 2),
		factoryBackend(0, poolConfig, false, true),
		factory(&factoryBackend) {
}

PacketMatchTree::~PacketMatchTree() {
}

ReturnValue_t PacketMatchTree::addMatch(uint16_t apid, uint8_t type,
		uint8_t subtype) {
	//We assume adding APID is always requested.
	TmPacketMinimal::TmPacketMinimalPointer data;
	data.data_field.service_type = type;
	data.data_field.service_subtype = subtype;
	TmPacketMinimal testPacket((uint8_t*) &data);
	testPacket.setAPID(apid);
	iterator lastTest;
	iterator rollback;
	ReturnValue_t result = findOrInsertMatch<TmPacketMinimal*, ApidMatcher>(
			this->begin(), &testPacket, &lastTest);
	if (result == NEW_NODE_CREATED) {
		rollback = lastTest;
	} else if (result != RETURN_OK) {
		return result;
	}
	if (type == 0) {
		//Check if lastTest has no children, otherwise, delete them,
		//as a more general check is requested.
		if (lastTest.left() != this->end()) {
			removeElementAndAllChildren(lastTest.left());
		}
		return RETURN_OK;
	}
	//Type insertion required.
	result = findOrInsertMatch<TmPacketMinimal*, ServiceMatcher>(
			lastTest.left(), &testPacket, &lastTest);
	if (result == NEW_NODE_CREATED) {
		if  (rollback == this->end()) {
			rollback = lastTest;
		}
	} else if (result != RETURN_OK) {
		if (rollback != this->end()) {
			removeElementAndAllChildren(rollback);
		}
		return result;
	}
	if (subtype == 0) {
		if (lastTest.left() != this->end()) {
			//See above
			removeElementAndAllChildren(lastTest.left());
		}
		return RETURN_OK;
	}
	//Subtype insertion required.
	result = findOrInsertMatch<TmPacketMinimal*, SubServiceMatcher>(
			lastTest.left(), &testPacket, &lastTest);
	if (result == NEW_NODE_CREATED) {
		return RETURN_OK;
	} else if (result != RETURN_OK) {
		if (rollback != this->end()) {
			removeElementAndAllChildren(rollback);
		}
		return result;
	}
	return RETURN_OK;
}

template<typename VALUE_T, typename INSERTION_T>
ReturnValue_t PacketMatchTree::findOrInsertMatch(iterator startAt, VALUE_T test,
		iterator* lastTest) {
	bool attachToBranch = AND;
	iterator iter = startAt;
	while (iter != this->end()) {
		bool isMatch = iter->match(test);
		attachToBranch = OR;
		*lastTest = iter;
		if (isMatch) {
			return RETURN_OK;
		} else {
			//Go down OR branch.
			iter = iter.right();
		}
	}
	//Only reached if nothing was found.
	SerializeableMatcherIF<VALUE_T>* newContent = factory.generate<INSERTION_T>(
			test);
	if (newContent == NULL) {
		return FULL;
	}
	Node* newNode = factory.generate<Node>(newContent);
	if (newNode == NULL) {
		//Need to make sure partially generated content is deleted, otherwise, that's a leak.
		factory.destroy<INSERTION_T>(static_cast<INSERTION_T*>(newContent));
		return FULL;
	}
	*lastTest = insert(attachToBranch, *lastTest, newNode);
	if (*lastTest == end()) {
		//This actaully never fails, so creating a dedicated returncode seems an overshoot.
		return RETURN_FAILED;
	}
	return NEW_NODE_CREATED;
}

ReturnValue_t PacketMatchTree::removeMatch(uint16_t apid, uint8_t type,
		uint8_t subtype) {
	TmPacketMinimal::TmPacketMinimalPointer data;
	data.data_field.service_type = type;
	data.data_field.service_subtype = subtype;
	TmPacketMinimal testPacket((uint8_t*) &data);
	testPacket.setAPID(apid);
	iterator foundElement = findMatch(begin(), &testPacket);
	if (foundElement == this->end()) {
		return NO_MATCH;
	}
	if (type == 0) {
		if (foundElement.left() == end()) {
			return removeElementAndReconnectChildren(foundElement);
		} else {
			return TOO_GENERAL_REQUEST;
		}
	}
	//Go down AND branch. Will abort if empty.
	foundElement = findMatch(foundElement.left(), &testPacket);
	if (foundElement == this->end()) {
		return NO_MATCH;
	}
	if (subtype == 0) {
		if (foundElement.left() == end()) {
			return removeElementAndReconnectChildren(foundElement);
		} else {
			return TOO_GENERAL_REQUEST;
		}
	}
	//Again, go down AND branch.
	foundElement = findMatch(foundElement.left(), &testPacket);
	if (foundElement == end()) {
		return NO_MATCH;
	}
	return removeElementAndReconnectChildren(foundElement);
}

PacketMatchTree::iterator PacketMatchTree::findMatch(iterator startAt,
		TmPacketMinimal* test) {
	iterator iter = startAt;
	while (iter != end()) {
		bool isMatch = iter->match(test);
		if (isMatch) {
			break;
		} else {
			iter = iter.right(); //next OR element
		}
	}
	return iter;
}

ReturnValue_t PacketMatchTree::initialize() {
	return factoryBackend.initialize();
}


ReturnValue_t PacketMatchTree::changeMatch(bool addToMatch, uint16_t apid,
		uint8_t type, uint8_t subtype) {
	if (addToMatch) {
		return addMatch(apid, type, subtype);
	} else {
		return removeMatch(apid, type, subtype);
	}
}

ReturnValue_t PacketMatchTree::cleanUpElement(iterator position) {
	factory.destroy(position.element->value);
	//Go on anyway, there's nothing we can do.
	//SHOULDDO: Throw event, or write debug message?
	return factory.destroy(position.element);
}