/*
 * names.c		db names
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "names.h"
#include "utils.h"

#define MAX_ENTRIES  256
#define NAME_MAX_LEN 512

static int read_id_name(FILE *fp, int *id, char *name)
{
	char buf[NAME_MAX_LEN];
	int min, maj;

	while (fgets(buf, sizeof(buf), fp)) {
		char *p = buf;

		while (*p == ' ' || *p == '\t')
			p++;

		if (*p == '#' || *p == '\n' || *p == 0)
			continue;

		if (sscanf(p, "%x:%x %s\n", &maj, &min, name) == 3) {
			*id = (maj << 16) | min;
		} else if (sscanf(p, "%x:%x %s #", &maj, &min, name) == 3) {
			*id = (maj << 16) | min;
		} else if (sscanf(p, "0x%x %s\n", id, name) != 2 &&
				sscanf(p, "0x%x %s #", id, name) != 2 &&
				sscanf(p, "%d %s\n", id, name) != 2 &&
				sscanf(p, "%d %s #", id, name) != 2) {
			strcpy(name, p);
			return -1;
		}
		return 1;
	}

	return 0;
}

struct db_names *db_names_alloc(void)
{
	struct db_names *db;

	db = malloc(sizeof(*db));
	if (!db)
		return NULL;

	memset(db, 0, sizeof(*db));

	db->size = MAX_ENTRIES;
	db->hash = malloc(sizeof(struct db_entry *) * db->size);
	memset(db->hash, 0, sizeof(struct db_entry *) * db->size);

	return db;
}

int db_names_load(struct db_names *db, const char *path)
{
	struct db_entry *entry;
	FILE *fp;
	int id;
	char namebuf[NAME_MAX_LEN] = {0};
	int ret = -1;

	fp = fopen(path, "r");
	if (!fp)
		return -ENOENT;

	while ((ret = read_id_name(fp, &id, &namebuf[0]))) {
		if (ret == -1) {
			fprintf(stderr, "Database %s is corrupted at %s\n",
					path, namebuf);
			goto Exit;
		}
		ret = -1;

		if (id < 0)
			continue;

		entry = malloc(sizeof(*entry));
		if (!entry)
			goto Exit;

		entry->name = strdup(namebuf);
		if (!entry->name) {
			free(entry);
			goto Exit;
		}

		entry->id   = id;
		entry->next = db->hash[id & (db->size - 1)];
		db->hash[id & (db->size - 1)] = entry;
	}
	ret = 0;

Exit:
	fclose(fp);
	return ret;
}

void db_names_free(struct db_names *db)
{
	int i;

	if (!db)
		return;

	for (i = 0; i < db->size; i++) {
		struct db_entry *entry = db->hash[i];

		while (entry) {
			struct db_entry *next = entry->next;

			free(entry->name);
			free(entry);
			entry = next;
		}
	}

	free(db->hash);
	free(db);
}

char *id_to_name(struct db_names *db, int id, char *name)
{
	struct db_entry *entry;

	if (!db)
		return NULL;

	entry = db->hash[id & (db->size - 1)];
	while (entry && entry->id != id)
		entry = entry->next;

	if (entry) {
		strncpy(name, entry->name, IDNAME_MAX);
		return name;
	}

	snprintf(name, IDNAME_MAX, "%d", id);
	return NULL;
}

int name_to_id(struct db_names *db, int *id, const char *name)
{
	struct db_entry *entry;
	int i;

	if (!db)
		return -1;

	if (db->cached && strcmp(db->cached->name, name) == 0) {
		*id = db->cached->id;
		return 0;
	}

	for (i = 0; i < db->size; i++) {
		entry = db->hash[i];
		while (entry && strcmp(entry->name, name))
			entry = entry->next;

		if (entry) {
			db->cached = entry;
			*id = entry->id;
			return 0;
		}
	}

	return -1;
}