/***************************************************************************
 * By Théophile Bastian, 2017
 * M1 Network course project at ENS Cachan, Juliusz Chroboczek.
 * License: WTFPL v2 <http://www.wtfpl.net/>
 **************************************************************************/

#include "data.h"
#include "protocol.h"
#include "nw_constants.h"
#include "neighbours.h"
#include "packetParser.h"
#include "configFile.h"
#include "dataStore.h"
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <signal.h>

bool terminate=false;

int main(int argc, char** argv) {
	bool hasConfig = false;
	char* configFilePath = nullptr;
	if(argc > 1) {
		hasConfig = true;
		configFilePath = argv[1];
	}

	srand(time(NULL)+42);

	signal(SIGINT, [](int) { terminate = true; });

	ConfigFile cfg;
	if(hasConfig) {
		if(!cfg.read(configFilePath) || !cfg.write(configFilePath)) {
			fprintf(stderr, "Could not read/write on '%s'.\n", configFilePath);
			exit(1);
		}
	}

	SockAddr addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons(csts::DEFAULT_PORT);

	fprintf(stderr, "[INFO] Starting with ID %lX\n", cfg.getSelfId());

	Protocol proto(addr, cfg.getSelfId());

	DataStore dataStore(&proto);
	for(auto dat : cfg.getData()) {
		Bytes pck;
		pck << csts::TLV_DATA_TEXT << (u8)dat.second.size();
		for(u8 chr : dat.second)
			pck << chr;
		dataStore.addData(pck, time(NULL), dat.first, true);
		fprintf(stderr, "[INFO] Adding data `%s`\n", dat.second.c_str());
	}

	Neighbours neighboursManager(&proto, &dataStore);

	for(const Neighbour& nei : cfg.getBootstrapNodes()) {
		char addr[54];
		inet_ntop(AF_INET6, &nei.addr.sin6_addr, addr, 54);
		fprintf(stderr, "[INFO] Bootstrap neighbour: %lX [%s]:%hu\n",
				nei.id, addr, ntohs(nei.addr.sin6_port));
		neighboursManager.addPotentialNei(nei);
	}

	PacketParser pckParser(&neighboursManager, &proto, &dataStore);

	while(!terminate) {
		neighboursManager.fullUpdate();

		while(proto.readyRead()) {
			SockAddr fromAddr;
			Bytes pck = proto.getPacket(&fromAddr);
			pckParser.parse(pck, fromAddr);
		}

		dataStore.update();

		sleep(1);
	}

	return 0;
}