#include <arpa/inet.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <netdb.h>
#include <regex>
#include <sys/sendfile.h>
#include <sys/stat.h>

#include "inc/args.hpp"
#include "inc/json.hpp"
#include "inc/macros.hpp"


using namespace std;
using json = nlohmann::json;


int port = 9070;

json asictypes = json::parse(R"([
	{"key": "A1066", "value": "Avalon 1066"}
])");


void tcpQuery(char* ip, int port, char* cmd, char* msg, int lenmsg) {
	INFO("IP: ", ip)
	INFO("Port: ", port)
	INFO("Command: ", cmd)

	struct hostent* host = gethostbyname(ip);
	sockaddr_in sendSockAddr;
	bzero((char*)&sendSockAddr, sizeof(sendSockAddr));
	sendSockAddr.sin_family = AF_INET;
	sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
	sendSockAddr.sin_port = htons(port);
	int clientSd = socket(AF_INET, SOCK_STREAM, 0);
	int status = connect(clientSd, (sockaddr*) &sendSockAddr, sizeof(sendSockAddr));
	if(status < 0) {
		ERR("Error connecting to socket!")
		return;
	}
	INFO("Connected to the server!")

	memset(msg, 0, strlen(msg));
	send(clientSd, cmd, sizeof(cmd), 0);
	INFO("Awaiting server response...");
	recv(clientSd, msg, lenmsg, MSG_WAITALL);
	INFO("Server: ", msg)
	close(clientSd);
	INFO("-------------------------")
}

void getFrom1066(char* ip, char* cmd, char* msg, int lenmsg) {
	INFO("------ AVALON 1066 ------")
	int port = 4028;
	tcpQuery(ip, port, cmd, msg, lenmsg);
}

void httpGetURL(char* url, char* line, const char* symbol) {
	char *copy = new char[strlen(line) + 1];
	strcpy(copy, line);

	char *token = new char[1024];
	strcpy(token, strtok(copy, symbol));
	int current = 0;
	int run = 1;

	while(token != nullptr && run) {
		token = strtok(nullptr, " ");
		if(current == 0) {
			strcpy(url, token);
			if (url == nullptr) {
				strcpy(url, "");
			}
			run = 0;
		}
		current = current + 1;
	}

	delete[] copy;
}

void parseUrl(char* url, char* res) {
	res = strtok(url, "/");
	while(res) {
		INFO(res);
		res = strtok(nullptr, "/");
	}
}

void getStats(char* stats) {
	ifstream f("asics.json");
	json data = json::parse(f);
	f.close();

	json res;
	if(data["mode"] == "manual") {
		for (json::iterator it = data["asics"].begin(); it != data["asics"].end(); ++it) {
			if(strcmp((*it)["type"].get<string>().c_str(), "A1066") == 0) {
				int lenmsg = 1024*8;
				char* msg = new char[lenmsg];
				getFrom1066((char*)((*it)["address"].get<string>().c_str()), (char*)"summary", msg, lenmsg);
				res["asics"][it.key()] = msg;

				delete[] msg;
			}
			// INFO(it.key(), ": ", it.value())
		}
	}
	else {
		WARN("Not implemented!")
	}

	strcat(stats, res.dump().c_str());

	data.clear();
	res.clear();
}

void httpServer(int port) {
	char http_header[25] = "HTTP/1.1 200 Ok\r\n";
	int server_fd, new_socket, pid;
	struct sockaddr_in address;
	int addrlen = sizeof(address);

	if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
		CRIT("In sockets")
		exit(EXIT_FAILURE);
	}

	address.sin_family = AF_INET;
	address.sin_addr.s_addr = INADDR_ANY;
	address.sin_port = htons(port);

	memset(address.sin_zero, '\0', sizeof address.sin_zero);

	if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
		CRIT("In bind")
		close(server_fd);
		exit(EXIT_FAILURE);
	}
	if (listen(server_fd, 10) < 0) {
		CRIT("In listen")
		exit(EXIT_FAILURE);
	}

	INFO("HTTP server started on: http://127.0.0.1:", port)

	while(1) {
		printf("\n+++++++ Waiting for new connection ++++++++\n\n");
		if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
			CRIT("In accept")
			exit(EXIT_FAILURE);
		}

		pid = fork();
		if(pid < 0) {
			CRIT("Error on fork")
			exit(EXIT_FAILURE);
		}

		if(pid == 0) {
			char *url = new char[1024];
			char *msg = new char[1024*64];
			char *buffer = new char[30000];
			char *copy_head = new char[strlen(http_header) + 200];

			read(new_socket, buffer, 30000);
			strcpy(copy_head, http_header);
			httpGetURL(url, buffer, " ");
			INFO(url)

			if(strstr(url, "/api/"))
				strcat(copy_head, "Content-Type: application/json\r\n\r\n");
			else if(strstr(url, ".js"))
				strcat(copy_head, "Content-Type: text/javascript\r\n\r\n");
			else
				strcat(copy_head, "Content-Type: text/html\r\n\r\n");

			write(new_socket, copy_head, strlen(copy_head));

			if(strstr(url, ".js")) {
				char *file_path = new char[512];
				strcpy(file_path, ".");
				strcat(file_path, url);

				int file = open(file_path, O_RDONLY);
				INFO(file);
				if(file < 0){
					WARN("Cannot Open file path : ", file_path, " with error ", file)
					send(new_socket, R"({"error":"FILE_NOT_EXISTS"})", 27, MSG_DONTWAIT);
				}

				struct stat stat_buf;
				fstat(file, &stat_buf);
				sendfile(new_socket, file, nullptr, stat_buf.st_blksize);

				delete[] file_path;
			}
			else if(strcmp(url, "/") == 0) {
				strcpy(msg, (char*)R"(<meta charset="UTF-8"><script defer src="/index.js"></script>)");
				send(new_socket, msg, strlen(msg), MSG_DONTWAIT);
			}
			else if(strcmp(url, "/api/getstats") == 0) {
				char *stats = new char[1024*64];
				getStats(stats);
				send(new_socket, stats, strlen(stats), MSG_DONTWAIT);

				delete[] stats;
			}
			else if(strcmp(url, "/api/asictypes") == 0) {
				strcpy(msg, asictypes.dump().c_str());
				send(new_socket, msg, strlen(msg), MSG_DONTWAIT);
			}
			else {
				strcpy(msg, "");
				send(new_socket, msg, strlen(msg), MSG_DONTWAIT);
			}

			delete[] msg;
			delete[] url;
			delete[] buffer;
			delete[] copy_head;
			close(new_socket);
			exit(0);
		}
		else{
			printf(">>>>>>>>>>Parent create child with pid: %d <<<<<<<<<", pid);
			close(new_socket);
		}
	}
	close(server_fd);
}

int main(int argc, char **argv) {
	INFO("Bit.ASICmon started!")

	InputParser input(argc, argv);
	if(input.cmdOptionExists("-h")) {
		INFO("HELP")

		exit(0);
	}
	if(input.cmdOptionExists("-p")) {
		const char* aport = input.getCmdOption("-p").c_str();
		if (strcmp(aport, "") == 0){
			port = atoi(aport);
			INFO("Set port: ", port)
		}
		else {
			WARN("Port invalid using default: ", port)
		}

		delete[] aport;
	}

	// char* msg = getFrom1066((char*)"128.128.128.56", (char*)"estats");
	// char* msg = getFrom1066((char*)"128.128.128.56", (char*)"summary");

	// regex re(R"(BOOTBY\[([\d\s\w.]+)\])");
	// cmatch m;

	// regex_search(msg, m, re);
	// delete[] msg;
	// INFO(m[1])

	httpServer(port);

	return 0;
}
