#include "parser.h"
#include "usql.h"

#include "linenoise.h"

// https://dev.to/joaoh82/what-would-sqlite-look-like-if-written-in-rust-part-1-2np4

using namespace std::chrono;

const std::vector<std::string> commands {
	"select", "create", "load", "table"
};

std::string get_history_file_dir() {
	std::string file{"/.usql_history.txt"};
	const char *t = std::getenv("HOME");

	if (t == nullptr) return "/tmp/" + file;
	else return std::string{t} + "/" + file;
}


size_t last_token_index( std::string str ) {
	// remove trailing white space
	while( !str.empty() && std::isspace( str.back() ) ) str.pop_back() ;

	// locate the last white space
	return str.find_last_of( "() \t\n" ) ;
}


void completion(const char *buf, linenoiseCompletions *lc) {
	if (buf != nullptr) {
		std::string str{buf};

		const auto pos = last_token_index(str);
		if (pos == std::string::npos)
			return;     // cannot find what to complete

		std::string token = str.substr(pos + 1);
		std::string begining = str.substr(0, pos + 1);

		for (const auto & command : commands) {
			if (command.find(token) == 0) {
				std::string completion_string = begining + command;
				linenoiseAddCompletion(lc, completion_string.c_str());
			}
		}
	}
}

char *hints(const char *buf, int *color, int *bold) {
	//     if (!strcasecmp(buf,"hello")) {
	//         *color = 35;
	//         *bold = 0;
	//         return " World";
	//     }
	return nullptr;
}


void setup_linenoise() {
	std::string history_file = get_history_file_dir();

	linenoiseHistorySetMaxLen(500);
	linenoiseSetCompletionCallback(completion);
	linenoiseSetHintsCallback(hints);
	linenoiseSetMultiLine(1);
	linenoiseHistoryLoad(history_file.c_str());
}

void linenoise_line_read(char *line) {
	linenoiseHistoryAdd(line);
}

void close_linenoise() {
	std::string history_file = get_history_file_dir();

	linenoiseHistorySave(history_file.c_str());
}


void repl() {
	std::string code;
	std::string input;

	setup_linenoise();

	usql::USql uSql{};


	while (true) {
		char *line = linenoise(">>> ");
		if (line == nullptr) break;

		linenoise_line_read(line);

		input = std::string(line);

		if (input == "!quit" || input == "!q")
			break;
		else if (input == "!export" || input == "!x") {
			std::cout << "File to export to: ";
			std::getline(std::cin, input);

			//write_file_contents(input, code);
		} else if (!input.empty()) {
			try {
				time_point<high_resolution_clock> start_time = high_resolution_clock::now();
				auto result = uSql.execute(input);
				time_point<high_resolution_clock> end_time = high_resolution_clock::now();

				std::cout << input << std::endl;
				std::cout << "run time: " << duration_cast<milliseconds>(end_time - start_time).count() << " ms " << std::endl <<std::endl;

				result->print();

				code += input + "\n";
			} catch (std::exception &e) {
				std::cerr << e.what() << std::endl;
			}
		}
	}

	close_linenoise();
}

void debug() {
	std::vector<std::string> sql_commands{
//		    "create table ticker ( tablee varchar(5) not null, permaticker integer, ticker varchar(10) not null, name varchar(256) not null, exchange varchar(32), isdelisted boolean, category varchar(32), cusips varchar(256), siccode integer, sicsector varchar(256), sicindustry varchar(256), famasector varchar(256), famaindustry varchar(256), sector varchar(128), industry varchar(128), scalemarketcap varchar(64), scalerevenue varchar(64), relatedtickers varchar(128), currency varchar(3), location varchar(64), lastupdated date, firstadded date, firstpricedate date, lastpricedate date, firstquarter date, lastquarter date, secfilings varchar(256), companysite varchar(256))",
//		    "load ticker from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/tickers.csv')",
//		    "select * from ticker where ticker = 'WFC' and tablee = 'SF1'",
//		    "set 'DATE_FORMAT' = '%Y-%m-%d'",
//		    "show 'DATE_FORMAT'",
		    "create table sf1 ( ticker varchar(8), dimension varchar(3), calendar_date date, date_key date, report_period date, last_updated date, accoci float, assets float, assetsavg float, assetsc float, assetsnc float, assetturnover float, bvps float, capex float, cashneq float, cashnequsd float, cor float, consolinc float, currentratio float, de float, debt float, debtc float, debtnc float, debtusd float, deferredrev float, depamor float, deposits float, divyield float, dps float, ebit float, ebitda float, ebitdamargin float, ebitdausd float, ebitusd float, ebt float, eps float, epsdil float, epsusd float, equity float, equityavg float, equityusd float, ev float, evebit float, evebitda float, fcf float, fcfps float, fxusd float, gp float, grossmargin float, intangibles float, intexp float, invcap float, invcapavg float, inventory float, investments float, investmentsc float, investmentsnc float, liabilities float, liabilitiesc float, liabilitiesnc float, marketcap float, ncf float, ncfbus float, ncfcommon float, ncfdebt float, ncfdiv float, ncff float, ncfi float, ncfinv float, ncfo float, ncfx float, netinc float, netinccmn float, netinccmnusd float, netincdis float, netincnci float, netmargin float, opex float, opinc float, payables float, payoutratio float, pb float, pe float, pe1 float, ppnenet float, prefdivis float, price float, ps float, ps1 float, receivables float, retearn float, revenue float, revenueusd float, rnd float, roa float, roe float, roic float, ros float, sbcomp float, sgna float, sharefactor float, sharesbas float, shareswa float, shareswadil float, sps float, tangibles float, taxassets float, taxexp float, taxliabilities float, tbvps float, workingcapital float)",
		    "load sf1 from '/tmp/sf1.csv'",
//		    "select ticker, calendar_date from sf1 where calendar_date > to_date('2019-01-01', '%Y-%m-%d') limit 1",
//		    "select ticker, dimension, calendar_date, eps, dps, roa*100 as roa, roe*100 as roe, pp(revenue), netinc from sf1 where (ticker = 'AIG' or ticker = 'WFC') and dimension = 'MRY' and calendar_date > to_date('2019-01-01', '%Y-%m-%d') order by 3 desc
		    "select ticker, dimension, calendar_date, eps, dps, roa*100 as roa, roe*100 as roe, pp(revenue) as revenue, pp(netinc) as netinc from sf1 where (ticker = 'AIG' or ticker = 'WFC') and dimension = 'MRY' and calendar_date > to_date('2019-01-01', '%Y-%m-%d') order by 3 desc",
//		    "create table a (i integer not null, s varchar(64), f float null, d date null,  b boolean)",
//		    "insert into a (i, s, b) values(1, upper('one'), 'Y')",
//		    "insert into a (i, s, b, f) values(1 + 10000, upper('one'), 'Y', 3.1415)",
//		    "update a set i = i * 100, f = f + 0.01 where i > 1",
//		    "select to_string(i, '%d.%m.%Y %H:%M:%S'), i, s from a where i < to_date('20.12.2019', '%d.%m.%Y')",
//		    "select i + 2 as first, i, s, b, f from a where i >=1 order by 1 desc offset 0 limit 1",
//		    "update table a set s = 'null string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
//		    "update table a set i = null",
//		    "insert into a (i, s) values(2, 'two')",
//		    "insert into a (i, s) values(3, 'two')",
//		    "insert into a (i, s) values(4, lower('FOUR'))",
//		    "insert into a (i, s) values(5, 'five')",
//		    "insert into a (i, s) values(to_date('20.12.1973', '%d.%m.%Y'), 'six')",
		    // tohle zpusobi kresh		    "insert into a (i, d) values(6', '2006-10-04')",
//		    "insert into a (i, d) values(6, '2006-10-04')",
//		    "save table a into '/tmp/a.csv'",
//		    "select i, s from a where i > 2 order by 1 desc offset 1 limit 1",
//		    "select distinct s, d from a",
//		    "select i, s from a where i = 1",
//		    "select i, s from a where s = 'two'",
//		    "select i, s from a where i <= 3 and s = 'one'",
//		    "select i, s from a where i > 0",
//		    "delete from a where i = 4",
//		    "select i, s from a where i > 0",
//		    "update a set f = 9.99 where i = 3",
//		    "select i, s, f from a where i = 3",
//		    "update a set s = 'three', f = f + 0.01 where i = 3",
//		    "select i, s, f from a where i = 3",
//    		    "create table data (ticker varchar(8), price float null)",
//    		    "load data from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/data.csv')",
//    		    "select ticker, price from data",
//    		    "select i, s, f from a where i < 300",
//    		    "create table x as select i, s, f from a where i < 300",
//    		    "select i, s, f from x where i < 300",
//    		    "drop table x",
//    		    "select i, s, f from a where i > 300",
//    		    "select i, to_string(i, '%d.%m.%Y'), s, f from a where i > 300",
//    		    "create table prices (datetime integer, symbol varchar(8), prev_close float, open float, price float, change float, change_prct varchar(16))",
//    		    "load prices from '/Users/vaclavt/Library/Mobile Documents/com~apple~CloudDocs/Development/usql/prices.csv'",
//    		    "insert into prices (datetime, symbol, prev_close, open, price, change, change_prct) values (1626979443, 'MPC', 54.08, 53.82, 53.63, -0.832101, '-0.83 %')",
//    		    "select to_string(datetime, '%d.%m.%Y %H:%M:%S'), symbol, prev_close, open, price, change, change_prct, datetime from prices where symbol = 'SYF' order by 8 desc limit 10"
	};

	usql::USql uSql{};

	for (const auto &command : sql_commands) {
		time_point<high_resolution_clock> start_time = high_resolution_clock::now();
		auto result = uSql.execute(command);
		time_point<high_resolution_clock> end_time = high_resolution_clock::now();

		std::cout << command << std::endl;
		std::cout << "run time: " << duration_cast<milliseconds>(end_time - start_time).count() << " ms "
			  << std::endl << std::endl;

		result->print();
	}

	std::cout << std::endl << std::endl;
}

int main(int argc, char *argv[]) {

	// debug();
	repl();

	return 0;
}
