#include "Exchange.h"

Exchange::Exchange() {
}

Exchange::Exchange(const string& type, const int& weight, const int& length, const vector<int>& sequence, const int vertexNumber) {
	this->vertexNumber = vertexNumber;
	this->id = lastId;
	this->type = type;
	this->weight = weight;
	this->length = length;
	this->sequence = sequence;
	this->visits = vector<int>(vertexNumber, 0);
	buildVisits();
	buildTraverses();

	this->lastId++;
}

Exchange::Exchange(const Exchange& e) {
	this->vertexNumber = e.vertexNumber;
	this->id = e.id;
	this->type = e.type;
	this->weight = e.weight;
	this->length = e.length;
	this->sequence = e.sequence;
	this->visits = e.visits;
	this->traverses = e.traverses;
}

Exchange::~Exchange()
{
}

bool operator==(const Exchange& e1, const Exchange& e2) {
	return e1.id == e2.id;
}

bool operator!=(const Exchange& e1, const Exchange& e2) {
	return !(e1 == e2);
}

Exchange& Exchange::operator=(const Exchange& e) {
	this->vertexNumber = e.vertexNumber;
	this->id = e.id;
	this->type = e.type;
	this->weight = e.weight;
	this->length = e.length;
	this->sequence = e.sequence;
	this->visits = e.visits;
	this->traverses = e.traverses;
	return *this;
}

void Exchange::restartIds() {
	Exchange::lastId = 0;
}

void Exchange::buildVisits() {
	int firstPos = 0;
	if (this->type == "cycle") {
		firstPos = 1;
	}
	for (int i = firstPos; i < this->sequence.size(); i++) {
		int id = this->sequence.at(i);
		this->visits.at(id)++;
	}
}

void Exchange::buildTraverses() {
	for (int i = 0; i < this->sequence.size() - 1; i++) {
		int id1 = this->sequence.at(i);
		int id2 = this->sequence.at(i + 1);
		int hash = (this->vertexNumber * id1) + id2;
		if (this->traverses.find(hash) == this->traverses.end()) {
			this->traverses.insert(make_pair(hash, 1));
		}
		else {
			this->traverses.at(hash)++;
		}
	}
}

bool Exchange::isVertexInExchange(const int& id) const {
	return (this->visits.at(id) > 0);
}
bool Exchange::conflicts_with(const Exchange& e) const {
	for (int i = 1; i < this->sequence.size(); i++) {
		if (e.getVisit(this->sequence.at(i)) > 0) {
			return true;
		}
	}
	return false;
}
int Exchange::first_vertex_in_common(const Exchange& e) const {
	for (int i = 1; i < this->sequence.size(); i++) {
		int id = this->sequence.at(i);
		if (e.getVisit(id) > 0) {
			return id;
		}
	}
	return -1;
}
unordered_set<int> Exchange::vertices_in_common(const Exchange& e) const {
	unordered_set<int> common;
	if (e.getSequence().size() <= this->sequence.size()) {
		for (int i = 1; i < e.getSequence().size(); i++) {
			int id = e.getSequence().at(i);
			if (this->visits.at(id) > 0) {
				common.insert(id);
			}
		}
	}
	else {
		for (int i = 1; i < this->sequence.size(); i++) {
			int id = this->sequence.at(i);
			if (e.getVisit(id) > 0) {
				common.insert(id);
			}
		}
	}
	return common;
}
const int& Exchange::getId() const {
	return this->id;
}
const string& Exchange::getType() const {
	return this->type;
}
const int& Exchange::getWeight() const {
	return this->weight;
}
const int& Exchange::getLength() const {
	return this->length;
}
const vector<int>& Exchange::getSequence() const {
	return this->sequence;
}

int Exchange::getVisit(const int& id) const {
	return this->visits.at(id);
}

int Exchange::getTraverses(const int& id1, const int& id2) const {
	int hash = (this->vertexNumber * id1) + id2;
	if (this->traverses.find(hash) == this->traverses.end()) {
		return 0;
	}
	return this->traverses.at(hash);
}

const string Exchange::toString() const {
	string str;
	str.append("ID: " + to_string(this->id) + "\tTYPE: " + this->type + "\tWEIGHT: " + to_string(this->weight) + "\tLENGTH: " + to_string(this->length) + "\n\tSEQUENCE: ");
	for (int i = 0; i < this->sequence.size(); i++) {
		str.append(to_string(this->sequence.at(i)) + " ");
	}
	str.append("\n\tVISITS: ");
	for (int i = 0; i < this->visits.size(); i++) {
		if (this->visits.at(i) > 0) {
			str.append(to_string(i) + "->" + to_string(this->visits.at(i)) + " ");
		}
	}
	str.append("\n\tTRAVERSES: ");
	for (unordered_map<int, int>::const_iterator it = this->traverses.begin(); it != this->traverses.end(); it++) {
		str.append(to_string(it->first) + "->" + to_string(it->second) + " ");
	}
	//str.append("\n");
	return str;
}