#include "Map.h"
#include "MapIterator.h"
#include <math.h>
#include <iostream>

Map::Map() {
	//O(1)
	this->_capacity=INITIAL_CAPACITY;
	this->_size=0;
	this->_elements=new TElem[this->_capacity];
	this->_next=new int[this->_capacity];
	for(int i=0;i<this->_capacity;i++){
		this->_elements[i]=NULL_TELEM;
		this->_next[i]=-1;
	}
	this->_nextFree=INITIAL_CAPACITY-1;
}

Map::~Map() {
	//O(1)
	delete[] this->_elements;
	delete[] this->_next;
}

TValue Map::add(TKey c, TValue v){
	// O(1) - amortized
	if(this->_size>=this->_capacity*ALPHA)
		this->_resize();
	int hash=abs(c % this->_capacity);
	while(this->_next[hash]!=-1 && this->_elements[hash].first!=c)
		hash=this->_next[hash];
	if(this->_elements[hash].first==c){
		TValue oldValue=this->_elements[hash].second;
		this->_elements[hash].second=v;
		return oldValue;
	}
	this->_elements[this->_nextFree]=std::make_pair(c,v);	
	this->_next[hash]=this->_nextFree;
	while(this->_elements[this->_nextFree]!=NULL_TELEM)
		this->_nextFree--;
	this->_size++;
	return NULL_TVALUE;
}

TValue Map::search(TKey c) const{
	// O(1) - amortized
	int hash = abs(c % this->_capacity);
	while (this->_next[hash] != -1 && this->_elements[hash].first != c)
		hash = this->_next[hash];
	if (this->_elements[hash].first == c)
		return this->_elements[hash].second;
	return NULL_TVALUE;
}

TValue Map::remove(TKey c){
	//O(1) - amortized
	int hash = abs(c % this->_capacity);
	int prev = -1;
	while (this->_next[hash] != -1 && this->_elements[hash].first != c) {
		prev = hash;
		hash = this->_next[hash];
	}
	if (this->_elements[hash].first == c) {
		TValue oldValue = this->_elements[hash].second;
		if (prev == -1) {
			if(this->_next[hash]!=-1){
				int remove = this->_next[hash];
				this->_elements[hash] = this->_elements[this->_next[hash]];
				this->_next[hash] = this->_next[this->_next[hash]];
				this->_next[remove] = -1;
				this->_elements[remove] = NULL_TELEM;
				hash = remove;
			}
			else
				this->_elements[hash] = NULL_TELEM;
		}
		else {
			this->_next[prev] = this->_next[hash];
			this->_elements[hash] = NULL_TELEM;
			this->_next[hash] = -1;
		}
		if(hash>this->_nextFree)
			this->_nextFree = hash;
		this->_size--;
		return oldValue;
	}
	return NULL_TVALUE;
}


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

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

MapIterator Map::iterator() const {
	return MapIterator(*this);
}

void Map::_resize() {
	//O(n)
	int oldCapacity=this->_capacity;
	this->_capacity*=2;
	while(true){
		bool ok=true;
		for(int i=2;i<=sqrt(this->_capacity);i++)
			if(this->_capacity%i==0){
				ok=false;
				break;
			}
		if(ok)
			break;
		this->_capacity++;
	}
	TElem* newElements=new TElem[this->_capacity];
	int* newNext=new int[this->_capacity];
	for(int i=0;i<this->_capacity;i++){
		newElements[i]=NULL_TELEM;
		newNext[i]=-1;
	}
	this->_nextFree=this->_capacity-1;
	for(int i=0;i<oldCapacity;i++){
		if(this->_elements[i]!=NULL_TELEM){
			int hash=abs(this->_elements[i].first % this->_capacity);
			while(newNext[hash]!=-1)
				hash=this->_next[hash];
			if(newElements[hash]==NULL_TELEM){
				newElements[hash]=this->_elements[i];
				newNext[hash]=-1;
			}
			else{
				newElements[this->_nextFree]=this->_elements[i];
				newNext[hash]=this->_nextFree;
				while(newElements[this->_nextFree]!=NULL_TELEM)
					this->_nextFree--;
			}

		}
	}
	delete[] this->_elements;
	delete[] this->_next;
	this->_elements=newElements;
	this->_next=newNext;


}

int Map::getKeyRange() const{
	//O(n)
	MapIterator it = this->iterator();
	if (!it.valid())
		return -1;
	int min = it.getCurrent().first;
	int max = it.getCurrent().first;
	while (it.valid()) {
		if (it.getCurrent().first < min)
			min = it.getCurrent().first;
		if (it.getCurrent().first > max)
			max = it.getCurrent().first;
		it.next();
	}
	return max - min;
}


