#pragma once

#include "int.qh"

// Databases (hash tables)
const int DB_BUCKETS = 8192;
ERASEABLE
void db_save(int db, string filename)
{
	int fh = fopen(filename, FILE_WRITE);
	if (fh < 0)
	{
		LOG_WARNF("^1Can't write DB to %s", filename);
		return;
	}
	fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
	for (int i = 0, n = buf_getsize(db); i < n; ++i)
		fputs(fh, strcat(bufstr_get(db, i), "\n"));
	fclose(fh);
}

USING(HashMap, int);

ERASEABLE
int db_create()
{
	return buf_create();
}
#define HM_NEW(this) (this = db_create())

ERASEABLE
void db_put(int db, string key, string value);

ERASEABLE
int db_load(string filename)
{
	int db = buf_create();
	if (db < 0)
		return -1;
	int fh = fopen(filename, FILE_READ);
	if (fh < 0)
		return db;
	string l = fgets(fh);
	if (stoi(l) == DB_BUCKETS)
	{
		for (int i = 0; (l = fgets(fh)); ++i)
			if (l != "")
				bufstr_set(db, i, l);
	}
	else
	{
		// different count of buckets, or a dump?
		// need to reorganize the database then (SLOW)
		//
		// note: we also parse the first line (l) in case the DB file is
		// missing the bucket count
		do
		{
			int n = tokenizebyseparator(l, "\\");
			for (int j = 2; j < n; j += 2)
				db_put(db, argv(j - 1), uri_unescape(argv(j)));
		}
		while ((l = fgets(fh)));
	}
	fclose(fh);
	return db;
}

ERASEABLE
void db_dump(int db, string filename)
{
	int fh = fopen(filename, FILE_WRITE);
	if (fh < 0)
		LOG_FATALF("Can't dump DB to %s", filename);
	fputs(fh, "0\n");
	for (int m, j, i = 0, n = buf_getsize(db); i < n; ++i)
	{
		m = tokenizebyseparator(bufstr_get(db, i), "\\");
		for (j = 2; j < m; j += 2)
			fputs(fh, strcat("\\", argv(j - 1), "\\", argv(j), "\n"));
	}
	fclose(fh);
}

ERASEABLE
void db_close(int db)
{
	buf_del(db);
}
#define HM_DELETE(this) db_close(this)

ERASEABLE
string db_get(int db, string key)
{
	int h = crc16(false, key) % DB_BUCKETS;
	return uri_unescape(infoget(bufstr_get(db, h), key));
}
#define HM_gets(this, k) db_get(this, k)

#define db_remove(db, key) db_put(db, key, "")

ERASEABLE
void db_put(int db, string key, string value)
{
	int h = crc16(false, key) % DB_BUCKETS;
	bufstr_set(db, h, infoadd(bufstr_get(db, h), key, uri_escape(value)));
}
#define HM_sets(this, key, val) db_put(this, key, val)

/*
void db_test()
{
	LOG_INFO("LOAD...\n");
	int db = db_load("foo.db");
	LOG_INFO("LOADED. FILL...\n");
	for (int i = 0; i < DB_BUCKETS; ++i)
		db_put(db, ftos(random()), "X");
	LOG_INFO("FILLED. SAVE...\n");
	db_save(db, "foo.db");
	LOG_INFO("SAVED. CLOSE...\n");
	db_close(db);
	LOG_INFO("CLOSED.\n");
}
*/
