#include <cstdio>

#include <iostream>
#include <string>
#include <vector>

#include <unistd.h>

#include "json.hpp"
#include "log.hpp"
#include "server.hpp"

static void request(std::vector<char> & buf);
static void reply(const json::obj & );

json::obj noop( const json::obj & ) {
	return {};
}

std::unordered_map<std::string, json::obj (*)( const json::obj & )> actions {
	{ "initialize", server::init },
	{ "initialized", noop }
};

int main(int argc, char * argv[]) {
	g_argc = argc;
	g_argv = argv;

	log() << "Language Server called with args:" << std::endl;
	for(int i = 0; i < argc; i++) {
		log() << "\t- " << argv[i] << std::endl;
	}

	// std::cout.setf(std::ios::unitbuf);

	// char buff[1000];
	// int r = 0;
	// while(0 != (r = read(STDIN_FILENO, buff, 1000))) {
	// 	for(int i = 0; i < r; i++) {
	// 		log() << buff[i];
	// 	}
	// }
	// log() << std::endl;

	// return 0;

	std::vector<char> buffer;
	while(true) {
		request(buffer);
		if(buffer.size() == 0) break;

		auto top = json::parse(buffer);

		auto action = actions.find( top["method"] );
		if(action != actions.end()) {
			log() << "Executing request '" << action->first << "'" << std::endl;
			json::obj response = {
				{"id", top["id"] }
			};

			try{
				response["result"] = action->second( top["params"] );
			} catch( const json::obj & error ) {
				response["result"] = "";
				response["error"] = error;
			}

			reply(response);
		}
		else {
			log() << "WARNING no action for method '" << top["method"].dump(4) << "'" << std::endl;
		}
	}

	log() << "Language Server closing after end of file" << std::endl;

	return 0;
}

static void request(std::vector<char> & buffer) {
	std::string lenstr;
	std::getline( std::cin, lenstr );
	if(std::cin.eof()) { buffer.clear(); return; };

	if (lenstr.rfind("Content-Length: ", 0) != 0) {
		log() << "Expected content length bug got '" << lenstr << "' instead " << std::endl;
		std::exit(EXIT_FAILURE);
	}

	size_t idx = sizeof("Content-Length:");
	size_t end;
	size_t len = std::stoul(lenstr.substr(idx), &end);
	if(lenstr.length() != (idx + end + 1)) {
		log() << "Inconsistent size of integer in content length: " << lenstr.length() << " vs " << (idx + end) << std::endl;
	}

	log() << "New request of " << len << " bytes" << std::endl;
	buffer.resize( len + 2 );
	std::cin.read(buffer.data(), buffer.size());

	for(char c : buffer) {
		log() << c;
	}
	log() << std::endl;
	return;
}

static void reply( const json::obj & response) {
	if(response["result"].is_null()) {
		log() << "No response needed" << std::endl;
		return;
	}

	log() << "Responding with : " << response.dump(4) << std::endl;
	const std::string & r = response.dump();
	std::cout << "Content-Length: " << r.length() << "\r\n\r\n";
	std::cout.flush();
	std::cout  << r << "\r\n";
	std::cout.flush();
}