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

#include "configFile.h"
#include <fstream>
#include <cstring>
#include <cstdlib>
using namespace std;

ConfigFile::ConfigFile() {
	selfId = randId();
}

bool ConfigFile::read(const char* path) {
	ifstream handle(path);
	if(!handle.is_open())
		return false;

	while(handle.good()) {
		string attr;
		handle >> attr;

		if(attr == "id") {
			handle >> hex >> selfId >> dec;
		}
		else if(attr == "bootstrap") {
			u16 port;
			u64 id;
			SockAddr addr;
			memset(&addr, 0, sizeof(addr));
			addr.sin6_family = AF_INET6;
			string addrStr;

			handle >> hex >> id >> dec;
			handle >> addrStr;
			handle >> port;

			addr.sin6_port = htons(port);
			if(inet_pton(AF_INET6, addrStr.c_str(), &(addr.sin6_addr)) != 1) {
				fprintf(stderr, "Could not convert '%s' to IPv6 address\n",
						addrStr.c_str());
				continue;
			}

			bootstrapNodes.push_back(Neighbour(id, addr));
		}
		else if(attr == "data") {
			u64 id;
			handle >> hex >> id >> dec;
			if(id == 0)
				id = randId();

			string dataStr;
			getline(handle, dataStr);

			size_t nonWSPos = 0;
			for(; nonWSPos < dataStr.size(); nonWSPos++)
				if(dataStr[nonWSPos] != ' ' && dataStr[nonWSPos] != '\t')
					break;
			if(nonWSPos == dataStr.size())
				continue;

			data.push_back(make_pair(id, dataStr.substr(nonWSPos)));
		}
		else if(attr.empty())
			continue;
		else {
			fprintf(stderr, "Unknown configuration item: '%s'\n",
					attr.c_str());
			continue;
		}
	}
	handle.close();

	return true;
}

bool ConfigFile::write(const char* path) {
	ofstream handle(path, ofstream::out | ofstream::trunc);

	if(!handle.is_open())
		return false;

	handle << "id " << hex << selfId << dec << '\n';
	for(Neighbour nei : bootstrapNodes) {
		char addr[54];
		if(inet_ntop(AF_INET6, &nei.addr.sin6_addr, addr, 54) == NULL) {
			perror("Could not convert IPv6 back to string");
			continue;
		}
		handle << "bootstrap " << hex << nei.id << dec << ' '
			<< addr << ' ' << ntohs(nei.addr.sin6_port) << '\n';
	}

	for(auto dat : data) {
		handle << "data " << hex << dat.first << dec << " "
			<< dat.second << '\n';
	}

	handle.close();

	return true;
}

u64 ConfigFile::randId() const {
	u64 out=0;
	for(int i=0; i < 8; i++) {
		out <<= 8;
		out += rand() % 0xFF;
	}
	return out;
}