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

#include	"hash.h"

#define	PREFS_FNAME	"preference.gz"
#define	TREE_FNAME	"tree.gz"
#define	RESULTS_FNAME	"result.gz"
#define	PARSE_FNAME	"parse.gz"

struct preference
{
	int	t_version;
	int	n_result_ids;
	char	**result_ids;
};

struct result
{
	char	*result_id;
	char	*derivation;
	int		preferred;
};

struct parse
{
	char	*item_id;
	char	*parse_id;
	struct preference	*prefs;
	int	n_results;
	struct result	**results;
}	**parses;
int	nparses;

struct hash	*parse_id_hash;

struct parse	*find_parse(char	*parse_id, int	addme)
{
	struct parse	*p = hash_find(parse_id_hash, parse_id);
	if(!p)
	{
		if(!addme)return NULL;
		p = calloc(sizeof(*p),1);
		p->parse_id = strdup(parse_id);
		p->prefs = NULL;
		nparses++;
		parses = realloc(parses, sizeof(struct parse*)*nparses);
		parses[nparses-1] = p;
		hash_add(parse_id_hash, p->parse_id, p);
	}
	return p;
}

int	load_one_pref(char	*buffer)
{
	char	parse_id[64];
	int	t_version;
	char	result_id[64];
	if(3 != sscanf(buffer, "%[^@]@%d@%[^@]", parse_id, &t_version, result_id))
		return -1;

	struct parse	*par = find_parse(parse_id, 1);
	struct preference	*p = par->prefs;
	if(!p)
	{
		p = calloc(sizeof(*p),1);
		p->t_version = t_version;
		par->prefs = p;
	}
	if(p->t_version > t_version)return 0;
	if(p->t_version < t_version)
	{
		p->t_version = t_version;
		while(p->n_result_ids > 0)
			free(p->result_ids[--(p->n_result_ids)]);
	}
	if(!strcmp(result_id, "-1"))return 0;
	p->n_result_ids++;
	p->result_ids = realloc(p->result_ids, sizeof(char*) * (p->n_result_ids+1));
	p->result_ids[p->n_result_ids-1] = strdup(result_id);
	return 0;
}

/*load_prefs_table(char	*fname)
{
	FILE	*f;
	f = fopen(fname, "r");
	if(!f) { perror(fname); exit(-1); }
	if(strstr(fname, ".gz"))
	{
		fclose(f);
		char	cmd[1024];
		sprintf(cmd, "zcat %s", fname);
		f = popen(cmd, "r");
	}

	int	line_num = 0;
	while(!feof(f))
	{
		char	buffer[102400];
		line_num++;
		if(0 != load_one_pref(buffer))
			{ fprintf(stderr, "format error in prefs file %s:%d\n", fname, line_num); exit(-1); }
	}
	if(strstr(fname, ".gz"))
		pclose(f);
	else fclose(f);
}*/

unescape(char	*str)
{
	char	*p = str;
	while(*p)
	{
		if(*p=='\\' && p[1])
		{
			p++;
			switch(*p)
			{
				case	's':
					*str++ = '@';
					break;
				case	'n':
					*str++ = '\n';
					break;
				case	'\\':
					*str++ = '\t';
					break;
				default:
					*str++ = *p;
					break;
			}
		}
		else *str++ = *p;
		p++;
	}
	*str = 0;
}

void	parse_add_result(struct parse	*p, char	*result_id, char	*derivation)
{
	//printf("parse id %s result id %s has derivation: %s\n", p->parse_id, result_id, derivation);
	p->n_results++;
	p->results = realloc(p->results, sizeof(struct result*) * p->n_results);
	struct result	*r = calloc(sizeof(struct result),1);
	p->results[p->n_results-1] = r;
	r->result_id = strdup(result_id);
	r->derivation = strdup(derivation);
	unescape(r->derivation);
	r->preferred = 0;
	struct preference	*prefs = p->prefs;
	if(prefs)
	{
		int	i;
		for(i=0;i<prefs->n_result_ids;i++)
			if(!strcmp(prefs->result_ids[i], r->result_id))
				r->preferred = 1;
	}
}

int	load_one_result(char	*buffer)
{
	char	*tmp = NULL;
	char	*parse_id = buffer;
	char	*result_id = strchr(parse_id, '@');
	if(!result_id)return -1;
	*result_id++ = 0;
	int	i;
	char	*skip = result_id;
	for(i=0;i<9;i++)
	{
		skip = strchr(skip, '@');
		if(!skip)return -1;
		*skip++ = 0;
	}
	char	*derivation = skip;
	skip = strchr(skip, '@');
	if(skip)*skip = 0;

	struct parse	*p = find_parse(parse_id, 0);
	if(!p)return 0;
	parse_add_result(p, result_id, derivation);
	return 0;
}

/*load_results_table(char	*fname)
{
	FILE	*f;
	f = fopen(fname, "r");
	if(!f) { perror(fname); exit(-1); }
	if(strstr(fname, ".gz"))
	{
		fclose(f);
		char	cmd[1024];
		sprintf(cmd, "zcat %s", fname);
		f = popen(cmd, "r");
	}

	int	line_num = 0;
	while(!feof(f))
	{
		char	buffer[102400];
		line_num++;
		if(NULL == fgets(buffer, 102399, f))
			{ fprintf(stderr, "format error in results file %s:%d\n", fname, line_num); exit(-1); }
		char	*nl = buffer + strlen(buffer)-1;
		if(nl >= buffer && *nl=='\n')*nl = 0;
		if(0 != load_one_result(buffer))
			{ fprintf(stderr, "unexpected error in results file %s:%d\n", fname, line_num); exit(-1); }
	}

	if(strstr(fname, ".gz"))
		pclose(f);
	else fclose(f);
}*/

load_tsdb_table(char	*home, char	*fname, int	(*loader)(char	*line))
{
	char	full_fname[1024];
	sprintf(full_fname, "%s/%s", home, fname);

	FILE	*f;
	f = fopen(full_fname, "r");
	if(!f) { perror(full_fname); exit(-1); }
	if(strstr(full_fname, ".gz"))
	{
		fclose(f);
		char	cmd[1024];
		sprintf(cmd, "zcat %s", full_fname);
		f = popen(cmd, "r");
	}

	int	line_num = 0;
	while(!feof(f))
	{
		char	buffer[102400];
		line_num++;
		if(NULL == fgets(buffer, 102399, f))
			break;
		char	*nl = buffer + strlen(buffer)-1;
		if(nl >= buffer && *nl=='\n')*nl = 0;
		if(0 != loader(buffer))
			{ fprintf(stderr, "unexpected error in tsdb file %s:%d\n", full_fname, line_num); exit(-1); }
	}

	if(strstr(full_fname, ".gz"))
		pclose(f);
	else fclose(f);
}

int	load_one_parse(char	*buffer)
{
	char	parse_id[128], run_id[128], item_id[128];
	int		readings;
	if(4 != sscanf(buffer, "%[^@]@%[^@]@%[^@]@%d@", parse_id, run_id, item_id, &readings))
		return -1;
	struct parse	*p = find_parse(parse_id, 0);
	if(!p)return 0;
	p->item_id = strdup(item_id);
	if(readings < p->n_results)
	{
		fprintf(stderr, "ERROR: item %s parse table says %d readings, but found %d results\n", p->item_id, readings, p->n_results);
		return -1;
	}
	return 0;
}

int	load_one_tree(char	*buffer)
{
	char	parse_id[128];
	int		t_version, t_active;
	if(3 != sscanf(buffer, "%[^@]@%d@%d@", parse_id, &t_version, &t_active))
		return -1;
	struct parse	*p = find_parse(parse_id, 0);
	if(!p)return 0;
	if(!p->prefs)return 0;
	if(p->prefs->t_version != t_version)return 0;
	if(t_active != p->prefs->n_result_ids)
	{
		fprintf(stderr, "ERROR: item %s tree table says %d active trees, but found %d preferences for version %d\n", p->item_id, t_active, p->prefs->n_result_ids, p->prefs->t_version);
		return -1;
	}
	return 0;
}

main(int	argc, char	*argv[])
{
	if(argc != 4) { fprintf(stderr, "usage: %s tsdb-profile-dir id-prefix output-data-file\n", argv[0]); exit(-1); }
	parse_id_hash = hash_new("parse id hash");
	load_tsdb_table(argv[1], PREFS_FNAME, load_one_pref);
	load_tsdb_table(argv[1], RESULTS_FNAME, load_one_result);
	load_tsdb_table(argv[1], PARSE_FNAME, load_one_parse);
	load_tsdb_table(argv[1], TREE_FNAME, load_one_tree);

	FILE	*f = fopen(argv[3], "w");
	if(!f) { perror(argv[3]);exit(-1); }
	int	i, j;
	int	output_results = 0, output_items = 0;
	for(i=0;i<nparses;i++)
	{
		struct parse	*p = parses[i];
		assert(p->item_id);
		struct preference	*prefs = p->prefs;
		if(!prefs)continue;
		if(p->n_results < 2 || prefs->n_result_ids < 1)continue;
		for(j=0;j<p->n_results;j++)
		{
			struct result	*r = p->results[j];
			fprintf(f, "%s%s@%s@%d@%s\n", argv[2], p->item_id, r->result_id, r->preferred, r->derivation);
			output_results++;
		}
		output_items++;
	}
	fclose(f);
	printf("%s => %s:  %d trees for %d items\n", argv[1], argv[3], output_results, output_items);
}
