#include "SortedBag.h"
#include "SortedBagIterator.h"
#include <exception>

SortedBag::SortedBag(Relation r) {
	//TODO - Implementation
	this->relation = r;
	this->root = nullptr;
	this->length = 0;
}

void SortedBag::add(TComp e) {
	//Best case: O(1) Worst case: O(n) Average case: O(log n)
	BSTNode* newNode = new BSTNode;
	newNode->elem = e;
	newNode->left = nullptr;
	newNode->right = nullptr;
	newNode->parent = nullptr;
	newNode->count = 1;
	if (this->root == nullptr) {
		this->root = newNode;
		this->length++;
		return;
	}
	BSTNode* current = this->root;
	while (current != nullptr) {
		if (current->elem == e) {
			current->count++;
			this->length++;
			return;
		}
		if (this->relation(e, current->elem)) {
			if (current->left == nullptr) {
				current->left = newNode;
				newNode->parent = current;
				this->length++;
				return;
			}
			else {
				current = current->left;
			}
		}
		else {
			if (current->right == nullptr) {
				current->right = newNode;
				newNode->parent = current;
				this->length++;
				return;
			}
			else {
				current = current->right;
			}
		}
	}
}


bool SortedBag::remove(TComp e) {
	//Best case: O(1) Worst case: O(n) Average case: O(log n)
	BSTNode* current = this->root;
	while (current != nullptr) {
		if(current->elem==e){
			if(current->count>1){
				current->count--;
				this->length--;
				return true;
			}
			else{
				if(current->left==nullptr && current->right==nullptr){
					if(current->parent==nullptr){
						this->root=nullptr;
						this->length--;
						return true;
					}
					else{
						if(current->parent->left==current){
							current->parent->left=nullptr;
							this->length--;
							return true;
						}
						else{
							current->parent->right=nullptr;
							this->length--;
							return true;
						}
					}
				}
				else{
					if(current->left==nullptr){
						if(current->parent==nullptr){
							this->root=current->right;
							this->root->parent=nullptr;
							this->length--;
							return true;
						}
						else{
							if(current->parent->left==current){
								current->parent->left=current->right;
								this->length--;
								return true;
							}
							else{
								current->parent->right=current->right;
								this->length--;
								return true;
							}
						}
					}
					else{
						if(current->right==nullptr){
							if(current->parent==nullptr){
								this->root=current->left;
								this->root->parent=nullptr;
								this->length--;
								return true;
							}
							else{
								if(current->parent->left==current){
									current->parent->left=current->left;
									this->length--;
									return true;
								}
								else{
									current->parent->right=current->left;
									this->length--;
									return true;
								}
							}
						}
						else{
							BSTNode* current2=current->right;
							while(current2->left!=nullptr){
								current2=current2->left;
							}
							current->elem=current2->elem;
							current->count=current2->count;
							if(current2->parent->left==current2){
								current2->parent->left=current2->right;
								this->length--;
								return true;
							}
							else{
								current2->parent->right=current2->right;
								this->length--;
								return true;
							}
						}
					}
				}
			}
		}
		else{
			if(this->relation(e,current->elem)){
				current=current->left;
			}
			else{
				current=current->right;
			}
		}
	}
	return false;

	
}


bool SortedBag::search(TComp elem) const {
	// Best case: O(1) Worst case: O(n) Average case: O(log n)
	BSTNode* current = this->root;
	while (current != nullptr) {
		if (current->elem == elem) {
			return true;
		}
		if (this->relation(elem, current->elem)) {
			current = current->left;
		}
		else {
			current = current->right;
		}
	}
	return false;
}


int SortedBag::nrOccurrences(TComp elem) const {
	// Best case: O(1) Worst case: O(n) Average case: O(log n)
	BSTNode* current = this->root;
	while (current != nullptr) {
		if (current->elem == elem) {
			return current->count;
		}
		if (this->relation(elem, current->elem)) {
			current = current->left;
		}
		else {
			current = current->right;
		}
	}
	return 0;
}



int SortedBag::size() const {
	//O(1)
	return this->length;
}


bool SortedBag::isEmpty() const {
	//O(1)
	return (this->length == 0);
}


SortedBagIterator SortedBag::iterator() const {
	return SortedBagIterator(*this);
}


SortedBag::~SortedBag() {
	//O(n)
	delete this->root;
}

int SortedBag::elementsWithThisFrequency(int frequency) const {
	// O(n)
	if(frequency<=0){
		throw std::exception();
	}
	return reccuriveCount(this->root,frequency);
}

int SortedBag::reccuriveCount(BSTNode* node, int frequency) const {
	if(node==nullptr){
		return 0;
	}
	return reccuriveCount(node->left,frequency)+reccuriveCount(node->right, frequency) + (this->nrOccurrences(node->elem)==frequency);
}