#include <vector>
#include <fstream>
#include "sqlite3.h"
extern "C" {
#include "magidoor/MagiDoor.h"
}
#include "Program.h"
#include "Question.h"
#include "Answer.h"
#include "inipp.h"

bool Program::open_database(sqlite3** db) {
	static const char* qtable = "CREATE TABLE IF NOT EXISTS questions (id INTEGER PRIMARY KEY, question TEXT, addanswers INTEGER)";
	static const char* atable = "CREATE TABLE IF NOT EXISTS answers (id INTEGER PRIMARY KEY, qid INTEGER, answer TEXT)";
	static const char* rtable = "CREATE TABLE IF NOT EXISTS results (qid INTEGER, aid INTEGER, userhandle TEXT)";
	int rc;
	char* err_msg;
	rc = sqlite3_open("voting.sqlite3", db);

	if (rc) {
		return false;
	}


	sqlite3_busy_timeout(*db, 5000);

	rc = sqlite3_exec(*db, qtable, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {
		sqlite3_free(err_msg);
		sqlite3_close(*db);
		return false;
	}
	
	rc = sqlite3_exec(*db, atable, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {
		sqlite3_free(err_msg);
		sqlite3_close(*db);
		return false;
	}
	rc = sqlite3_exec(*db, rtable, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {
		sqlite3_free(err_msg);
		sqlite3_close(*db);
		return false;
	}
	return true;
}

bool Program::load_answers(Question* q) {
	sqlite3* db;
	sqlite3_stmt* stmt;

	static const char* loadsql = "SELECT id, answer FROM answers WHERE qid = ?";

	if (!open_database(&db)) {
		return false;
	}

	if (sqlite3_prepare_v2(db, loadsql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}
	sqlite3_bind_int(stmt, 1, q->id);

	while (sqlite3_step(stmt) == SQLITE_ROW) {
		Answer* a = new Answer();

		a->id = sqlite3_column_int(stmt, 0);
		a->answer = std::string((const char*)sqlite3_column_text(stmt, 1));

		q->answers.push_back(a);
	}
	sqlite3_finalize(stmt);
	sqlite3_close(db);
	return true;
}

bool Program::load_questions() {
	sqlite3* db;
	sqlite3_stmt* stmt;
	
	static const char* loadsql = "SELECT id, question, addanswers FROM questions";

	if (questions.size() > 0) {
		for (Question* q : questions) {
			delete q;
		}

		questions.clear();
	}

	if (!open_database(&db)) {
		return false;
	}
	if (sqlite3_prepare_v2(db, loadsql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}

	while (sqlite3_step(stmt) == SQLITE_ROW) {
		Question *q = new Question();

		q->id = sqlite3_column_int(stmt, 0);
		q->question = std::string((const char*)sqlite3_column_text(stmt, 1));
		q->can_add_answers = (sqlite3_column_int(stmt, 2) == 1);
		questions.push_back(q);
	}

	sqlite3_finalize(stmt);
	sqlite3_close(db);

	for (Question *q : questions) {
		if (!load_answers(q)) {
			for (Question* q2 : questions) {
				delete q2;
			}
			return false;
		}
	}

	return true;

}

bool Program::delete_question(int32_t id) {
	sqlite3* db;
	sqlite3_stmt* stmt;

	static const char* dq_sql = "DELETE FROM questions WHERE id = ?";
	static const char* da_sql = "DELETE FROM answers WHERE qid = ?";
	static const char* dr_sql = "DELETE FROM results WHERE qid = ?";

	if (!open_database(&db)) {
		return false;
	}

	if (sqlite3_prepare_v2(db, dq_sql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}

	sqlite3_bind_int(stmt, 1, id);
	sqlite3_step(stmt);
	sqlite3_finalize(stmt);

	if (sqlite3_prepare_v2(db, da_sql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}

	sqlite3_bind_int(stmt, 1, id);
	sqlite3_step(stmt);
	sqlite3_finalize(stmt);

	if (sqlite3_prepare_v2(db, dr_sql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}

	sqlite3_bind_int(stmt, 1, id);
	sqlite3_step(stmt);
	sqlite3_finalize(stmt);

	sqlite3_close(db);
	return true;
}

bool Program::view_question(Question *q) {
	sqlite3* db;
	sqlite3_stmt* stmt;

	char buffer[256];

	int32_t aid;

	static const char* check_sql = "SELECT aid FROM results WHERE qid = ? and userhandle = ?";
	static const char* add_sql = "INSERT INTO results (qid, aid, userhandle) VALUES(?, ?, ?)";
	static const char* add_answer_sql = "INSERT INTO answers(qid, answer) VALUES(?, ?);";
	static const char* q_answer_count_sql = "SELECT COUNT(*) FROM results WHERE qid = ?";
	static const char* a_answer_count_sql = "SELECT COUNT(*) FROM results WHERE qid = ? AND aid = ?";
	if (!open_database(&db)) {
		return false;
	}
	if (sqlite3_prepare_v2(db, check_sql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}

	sqlite3_bind_int(stmt, 1, q->id);
	sqlite3_bind_text(stmt, 2, mdcontrol.user_alias, -1, NULL);

	if (sqlite3_step(stmt) == SQLITE_ROW) {
		aid = sqlite3_column_int(stmt, 0);
	}
	else {
		aid = -1;
	}

	sqlite3_finalize(stmt);

	if (aid == -1) {
		md_clr_scr();
		md_printf("`bright green`%s\r\n", q->question.c_str());
		for (size_t i = 0; i < q->answers.size(); i++) {
			md_printf("`bright cyan`%d. `bright white`%s\r\n", i + 1, q->answers.at(i)->answer.c_str());
		}

		if (q->can_add_answers) {
			md_printf("`bright cyan`0. `bright white`Add your own answer...\r\n");
		}
		md_printf("\r\nYour answer? ");
		if (md_getstring(buffer, 3, '0', '9') == 0) {
			// aborted
			sqlite3_close(db);
			return true;
		}
		
		aid = strtol(buffer, NULL, 10);
		if (aid == 0) {
			md_printf("\r\n\r\n`bright cyan`Please enter your custom answer: `white`");
			if (md_getstring(buffer, 256, 32, 126) == 0) {
				sqlite3_close(db);
				return true;
			}
			if (sqlite3_prepare_v2(db, add_answer_sql, -1, &stmt, NULL) != SQLITE_OK) {
				sqlite3_close(db);
				return false;
			}
			sqlite3_bind_int(stmt, 1, q->id);
			sqlite3_bind_text(stmt, 2, buffer, -1, NULL);
			sqlite3_step(stmt);
			aid = (int32_t)sqlite3_last_insert_rowid(db);

			Answer* a = new Answer();
			a->answer = std::string(buffer);
			a->id = aid;
			q->answers.push_back(a);

			sqlite3_finalize(stmt);
			if (sqlite3_prepare_v2(db, add_sql, -1, &stmt, NULL) != SQLITE_OK) {
				sqlite3_close(db);
				return false;
			}
			sqlite3_bind_int(stmt, 1, q->id);
			sqlite3_bind_int(stmt, 2, aid);
			sqlite3_bind_text(stmt, 3, mdcontrol.user_alias, -1, NULL);

			sqlite3_step(stmt);
			sqlite3_finalize(stmt);

		}
		else if (aid > 0 && aid <= (int32_t)q->answers.size()) {
			aid = q->answers.at(aid -1)->id;
			static const char* add_sql = "INSERT INTO results (qid, aid, userhandle) VALUES(?, ?, ?)";
			if (sqlite3_prepare_v2(db, add_sql, -1, &stmt, NULL) != SQLITE_OK) {
				sqlite3_close(db);
				return false;
			}
			sqlite3_bind_int(stmt, 1, q->id);
			sqlite3_bind_int(stmt, 2, aid);
			sqlite3_bind_text(stmt, 3, mdcontrol.user_alias, -1, NULL);

			sqlite3_step(stmt);
			sqlite3_finalize(stmt);
		}
		else {
			sqlite3_close(db);
			return false;
		}
	}
	// show results

	md_clr_scr();
	md_printf("`bright green`%s\r\n", q->question.c_str());
	
	if (sqlite3_prepare_v2(db, q_answer_count_sql, -1, &stmt, NULL) != SQLITE_OK) {
		sqlite3_close(db);
		return false;
	}
	int tot_answers = 0;
	sqlite3_bind_int(stmt, 1, q->id);

	if (sqlite3_step(stmt) == SQLITE_ROW) {
		tot_answers = sqlite3_column_int(stmt, 0);
	}
	sqlite3_finalize(stmt);

	int j = 1;

	for (Answer* a : q->answers) {
		if (sqlite3_prepare_v2(db, a_answer_count_sql, -1, &stmt, NULL) != SQLITE_OK) {
			sqlite3_close(db);
			return false;
		}
		sqlite3_bind_int(stmt, 1, q->id);
		sqlite3_bind_int(stmt, 2, a->id);

		int tot_this_answer = 0;

		if (sqlite3_step(stmt) == SQLITE_ROW) {
			tot_this_answer = sqlite3_column_int(stmt, 0);
		}
		if (a->id == aid) {
			md_printf("`bright cyan`%d. `bright white`%s `bright magenta`Votes: %d (%d%%) `bright yellow`your answer`white`\r\n", j++, a->answer.c_str(), tot_this_answer, (tot_answers > 0 ? (int)((float)tot_this_answer / (float)tot_answers * 100.f) : 0));
		}
		else {
			md_printf("`bright cyan`%d. `bright white`%s `bright magenta`Votes: %d (%d%%)`white`\r\n", j++, a->answer.c_str(), tot_this_answer, (tot_answers > 0 ? (int)((float)tot_this_answer / (float)tot_answers * 100.f) : 0));
		}
		sqlite3_finalize(stmt);
	}
	sqlite3_close(db);

	return true;
}

bool Program::add_question() {
	char buffer[256];
	std::string question;
	std::vector<std::string> answers;
	int can_add_answers;

	md_clr_scr();

	md_printf("`bright white`Your Question: `white`");
	if (md_getstring(buffer, 256, 32, 126) == 0) {
		return false;
	}
	md_printf("\r\n");
	question = std::string(buffer);

	while (true) {
		md_printf("`bright green`Answer #%d: `white`", answers.size() + 1);
		if (md_getstring(buffer, 256, 32, 126) == 0) break;
		answers.push_back(std::string(buffer));
		md_printf("\r\n");
	}

	if (answers.size() < 2) {
		md_printf("\r\n`bright red`Sorry, you need to add at least two answers!`white`\r\n");
		return false;
	}
	else {
		md_printf("\r\n\r\n`bright white`Can voters add their own answer? (Y/N)");
		char c = md_get_answer((char*)"YyNn");
		if (c == 'y' || c == 'Y') {
			can_add_answers = 1;
		}
		else {
			can_add_answers = 0;
		}
		sqlite3* db;
		sqlite3_stmt* stmt;
		static const char* qsql = "INSERT INTO questions (question, addanswers) VALUES(?, ?)";
		static const char* asql = "INSERT INTO answers (qid, answer) VALUES(?, ?)";
		if (!open_database(&db)) {
			return false;
		}
		if (sqlite3_prepare_v2(db, qsql, -1, &stmt, NULL) != SQLITE_OK) {
			sqlite3_close(db);
			return false;
		}
		sqlite3_bind_text(stmt, 1, question.c_str(), -1, NULL);
		sqlite3_bind_int(stmt, 2, can_add_answers);
		sqlite3_step(stmt);
		sqlite3_finalize(stmt);

		int32_t qid = (int32_t)sqlite3_last_insert_rowid(db);

		for (std::string answer : answers) {
			if (sqlite3_prepare_v2(db, asql, -1, &stmt, NULL) != SQLITE_OK) {
				sqlite3_close(db);
				return false;
			}
			sqlite3_bind_int(stmt, 1, qid);
			sqlite3_bind_text(stmt, 2, answer.c_str(), -1, NULL);
			sqlite3_step(stmt);
			sqlite3_finalize(stmt);
		}

		sqlite3_close(db);
		return true;
	}

}

int Program::run() {
	bool redraw = true;
	bool done = false;
	size_t start = 0;
	size_t selected = 0;

	int sec_level_to_add = 0;
	int sec_level_to_delete = 255;

	inipp::Ini<char> ini;
	std::ifstream is("voteorama.ini");

	if (is.is_open()) {
		ini.parse(is);

		inipp::get_value(ini.sections["main"], "AddQuestionSecLevel", sec_level_to_add);
		inipp::get_value(ini.sections["main"], "DelQuestionSecLevel", sec_level_to_delete);
		is.close();
	}


	while (true) {
		md_clr_scr();
		md_sendfile("ansi/main.ans", 0);
		// load questions
		if (!load_questions()) {
			return -1;
		}
		done = false;

		while (!done) {
			
			if (redraw) {
				if (questions.size() > 0) {
					size_t i;
					for (i = start; i < questions.size() && i - start < 11; i++) {
						md_set_cursor(i - start + 11, 8);
						if (i == selected) {
							md_printf("`blue bright white`%-66.66s`white`", questions.at(i)->question.c_str());
						}
						else {
							md_printf("`bright white`%-66.66s`white`", questions.at(i)->question.c_str());
						}
					}
					for (size_t j = i - start; j < 11; j++) {
						md_set_cursor(j + 11, 8);
						md_printf("\x1b[K");
					}
				}
				else {
					md_set_cursor(16, 30);
					md_printf("`bright black`No Questions yet!`white`");
				}
				redraw = false;
			}
			char c;
			if (questions.size() > 0) {
				c = md_getc();

				if (c == '\x1b') {
					c = md_getc();
					if (c == '[') {
						c = md_getc();
						if (c == 'A') {
							if (selected > 0) {
								if (selected - 1 < start) {
									selected--;
									start = selected;
									redraw = true;
								}
								else {
									md_set_cursor(selected - start + 11, 8);
									md_printf("`bright white`%-66.66s`white`", questions.at(selected)->question.c_str());
									selected--;
									md_set_cursor(selected - start + 11, 8);
									md_printf("`blue bright white`%-66.66s`white`", questions.at(selected)->question.c_str());
								}
							}
						}
						else if (c == 'B') {
							if (selected < questions.size() - 1) {
								if (selected + 1 >= start + 11) {
									selected++;
									start++;
									redraw = true;
								}
								else {
									md_set_cursor(selected - start + 11, 8);
									md_printf("`bright white`%-66.66s`white`", questions.at(selected)->question.c_str());
									selected++;
									md_set_cursor(selected - start + 11, 8);
									md_printf("`blue bright white`%-66.66s`white`", questions.at(selected)->question.c_str());
								}
							}
						}
						
					}
					continue;
				}
				else if (c == '\r') {
					if (!view_question(questions.at(selected))) {
						md_printf("`bright red`Something went wrong!`white`");
					}
					md_printf("\r\n\r\n`bright white`Press any key to continue...");
					md_getc();
					redraw = true;
					done = true;
				}
			} else {
				c = md_getc();
			}

			if (c == 'a' || c == 'A') {
				if (sec_level_to_add <= mdcontrol.user_seclevel) {
					if (add_question()) {
						md_printf("\r\n`bright green`Your question has been added!");
					}
					else {
						md_printf("\r\n`bright red`Your question has not been added!");
					}
					md_printf("\r\n\r\n`bright white`Press any key to continue...");
					md_getc();
				}
				redraw = true;
				done = true;
			}

			if (c == 'd' || c == 'D') {
				if (questions.size() > 0) {
					if (sec_level_to_delete <= mdcontrol.user_seclevel) {
						delete_question(questions.at(selected)->id);
					}
					start = 0;
					selected = 0;
				}
				redraw = true;
				done = true;
			}

			if (c == 'q' || c == 'Q') {
				return 0;
			}
		}
	}
}