#include "db_if.h"
#include "ConfigFile.h"
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <stdint.h>
#include <ctime>

DbIf::DbIf(int argc, char ** argv) {
	if(argc != 2) {
		fprintf(stderr, "Give a configuration file as the first argument to this program.\n");
		abort();
	}

	std::string db_file;
	try {
		ConfigFile conf(argv[1]);
		salt_size = conf.read<size_t>("salt_size", 256);
		key_lifetime = conf.read<uint64_t>("key_lifetime", 120);
		secret = conf.read<std::string>("secret");
		db_file = conf.read<std::string>("db_file");
	} catch(ConfigFile::file_not_found & e) {
		fprintf(stderr, "Could not open configuration file '%s'!\n", e.filename.c_str());
		abort();
	} catch(ConfigFile::key_not_found & e) {
		fprintf(stderr, "Could not find required configuration key '%s'!\n", e.key.c_str());
		abort();
	}

	db = new PosDb(db_file.c_str());
}

DbIf::~DbIf() {
	delete db;
}

std::string DbIf::getSalt() {
	expireKeys();
	char bfr[salt_size];
	FILE * rand_in = fopen("/dev/urandom", "r");
	if(!rand_in) {
		fprintf(stderr, "Could not open /dev/urandom!\n");
		return "";
	}
	if(salt_size != fread(bfr, sizeof(char), salt_size, rand_in)) {
		fclose(rand_in);
		fprintf(stderr, "Could not read from /dev/urandom!\n");
		return "";
	}
	fclose(rand_in);

	// zero-terminated strings suck
	for(unsigned int i=0;i<salt_size;++i)
		if(bfr[i] == 0)
			bfr[i] = 1;
	bfr[salt_size-1] = 0;

	std::string salt(bfr);

	valid_keys.push_back(std::pair<uint64_t, SHA1Hash>((uint64_t)time(NULL), SHA1Hash(salt + secret)));
	return salt;
}

bool DbIf::consumeKey(std::string key) {
	expireKeys();
	SHA1Hash h;
	h.fromHex(key);
	std::list<std::pair<uint64_t, SHA1Hash> >::iterator p;
	for(p = valid_keys.begin();p != valid_keys.end(); ++p) {
		if(p->second == h) {
			valid_keys.erase(p);
			return true;
		}
	}
	return false;
}

PosDb & DbIf::getDb() {
	return *db;
}

void DbIf::expireKeys() {
	std::list<std::pair<uint64_t, SHA1Hash> >::iterator p;
	uint64_t cur_t = (uint64_t)time(NULL);
	for(p = valid_keys.begin();p != valid_keys.end();++p) {
		if((cur_t - p->first) > key_lifetime)
			p = valid_keys.erase(p);
	}
}

