#include "Interpreter.hpp"

using namespace std;

void interpreter::load_commands() {
	while (input_.good()) {
		string line;
		getline(input_, line);
		process_line_(line);
	}
}

void interpreter::execute_commands() {
	while (current_instruction_ < instructions_.size()) {
		instructions_.at(current_instruction_)->execute(*this);
		current_instruction_++;
	}
}

void interpreter::process_line_(const std::string& line) {
	
	// Nejdriv se zbavim pripadnych komentaru.
	string line_ = line;
	for (int i = 0; i < line.size(); i++) {
		if (line_.at(i) == '#') {
			line_ = line.substr(0, i);
			break;
		}
	}

	if (line_ == "") return; // Prazdna radka, jenom komentar.

	// Nyni odeberu prebytecne whitespace a rozdelim radku na jednotlive tokeny. Na to mam funkci.
	vector<string> tokens_with_label = get_tokens_(line_);
	vector<string> tokens;

	// Nyni se podivam, jestli radka obsahuje navesti. Muze byt pouze v prvnim tokenu.
	if (tokens_with_label.at(0)[tokens_with_label.at(0).size() - 1] == ':') {
		labels_.insert(make_pair(tokens_with_label.at(0), instructions_.size() - 1));
		for (int i = 1; i < tokens_with_label.size(); i++) {
			tokens.push_back(tokens_with_label.at(i));
		}
	}
	else tokens = tokens_with_label;

	if (tokens_with_label.at(tokens.size() - 1) == "") tokens.pop_back();
	if (tokens.at(tokens.size() - 1) == "") tokens.pop_back();

	if (tokens.size() == 0 && tokens_with_label.size() > 0) throw invalid_argument("Label found but no instruction given.");
	
	// Na nultem miste ve vektoru by mel byt vzdy nazev instrukce.

	if (tokens.size() == 0) return; // Prazdna radka -> skip.

	if (tokens.at(0) == "ldconst") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		int value;
		try {
			value = stoi(tokens.at(1));
		}
		catch (exception ex) {
			throw invalid_argument("Error while parsing constant.");
		}
		instructions_.push_back(make_unique<ldconst>(value));
	}

	else if (tokens.at(0) == "stloc") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		int index;
		try {
			index = stoi(tokens.at(1));
		}
		catch (exception ex) {
			throw invalid_argument("Error while parsing constant.");
		}
		if (index < 0 || index > 9) throw invalid_argument("Invalid index of variable.");
		instructions_.push_back(make_unique<stloc>(index));
	}

	else if (tokens.at(0) == "ldloc") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		int index;
		try {
			index = stoi(tokens.at(1));
		}
		catch (exception ex) {
			throw invalid_argument("Error while parsing constant.");
		}
		if (index < 0 || index > 9) throw invalid_argument("Invalid index of variable.");
		instructions_.push_back(make_unique<ldloc>(index));
	}

	else if (tokens.at(0) == "pop") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<pop>());
	}

	else if (tokens.at(0) == "out") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<out>());
	}

	else if (tokens.at(0) == "add") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<add>());
	}

	else if (tokens.at(0) == "sub") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<sub>());
	}

	else if (tokens.at(0) == "div") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<div>());
	}

	else if (tokens.at(0) == "mul") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<mul>());
	}

	else if (tokens.at(0) == "lt") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<lt>());
	}

	else if (tokens.at(0) == "gt") {
		if (tokens.size() != 1) throw invalid_argument("Invalid argument count.");
		instructions_.push_back(make_unique<gt>());
	}

	else if (tokens.at(0) == "goto") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		string label = tokens.at(1);
		instructions_.push_back(make_unique<gotoi>(label));
	}

	else if (tokens.at(0) == "brt") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		string label = tokens.at(1);
		instructions_.push_back(make_unique<brt>(label));
	}

	else if (tokens.at(0) == "brf") {
		if (tokens.size() != 2) throw invalid_argument("Invalid argument count.");
		string label = tokens.at(1);
		instructions_.push_back(make_unique<brf>(label));
	}

	else throw invalid_argument("Unknown token.");
}

std::vector<std::string> interpreter::get_tokens_(const std::string& line) {
	vector<string> tokens;
	tokens.push_back(string());

	for (auto&& c : line) {
		if (c == ' ' || c == '\t') {
			if (tokens.at(tokens.size() - 1) != "") { // Pokud je posledni token prazdny, tzn. ze zatim tam byly pouze whitespace, nedelame nic. Jinak vytvorime novy token.
				tokens.push_back(string());
			}
		}
		else
			tokens.at(tokens.size() - 1) += c; // Jinak pridame znak do posledniho tokenu.
	}

	return tokens;
}