/*
 *	$Source$
 *	$Author$
 *	$Header$
 */

#ifndef lint
static char *rcsid_verse_c = "$Header$";
#endif lint

#define BOOKFILE "bible.dat"

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include "bible.h"

#define NUM_OPEN 10

BOOK	booktab[NUMBOOKS];
BOOK	*current_book;

BOOK	*open_books[NUM_OPEN];
long	open_book_time;

char	*strdup();

#define PARSE_SKIP(o, p, c) o=p; while(*p && *p != c && *p !='\n') p++; \
	if (*p) *p++ = '\0'; else goto error;

init_booktab()
{
	int	i;
	FILE	*f;
	char	*cp, *dp;
	char	*line;

	current_book = NULL;
	open_book_time = 0;
	strcpy(tmp, LIBDIR);
	f = fopen(strcat(tmp, BOOKFILE), "r");
	if (!f) {
		perror(tmp);
		fprintf(stderr, "Can't open book table.... aborting...\n");
		exit(1);
	}
	for (i=0; i < NUMBOOKS; i++) {
		booktab[i].current_pos = -1; 	/* Undefined */
		booktab[i].last_used = -1; 	/* Not used */
		cp = fgets(tmp, sizeof(tmp), f);
		tmp[sizeof(tmp)-1] = '\0';
		line = strdup(tmp);
		booktab[i].f = NULL;
	        PARSE_SKIP(dp, cp, ':');
		booktab[i].num = atoi(dp);
		PARSE_SKIP(dp, cp, ':');
		booktab[i].key = strdup(dp);
		PARSE_SKIP(dp, cp, ':');
		booktab[i].filename = strdup(dp);
		PARSE_SKIP(dp, cp, ':');
		booktab[i].numchap = atoi(dp);
		PARSE_SKIP(dp, cp, ':');
		booktab[i].abbrev = strdup(dp);
		PARSE_SKIP(dp, cp, ':');
		booktab[i].name = strdup(dp);
		if (*cp && *cp != '\n') {
			fprintf(stderr, "Extra fields, ignored: \n'%s'\n",
				cp);
		}
		free(line);
		continue;
	error:
		fprintf(stderr, "Parse error in the bible data file:\n");
		fprintf(stderr, "%s\n", line);
		free(line);
	}
	fclose(f);
	for (i = 0; i < NUM_OPEN; i++)
		open_books[i] = NULL;
}

print_verse_cite(v)
	VERSE	*v;
{
	printf("%s %d:%d", v->bk->name, v->c, v->v);
}

free_verse(v)
	VERSE	*v;
{
	if (v->s)
		free(v->s);
	free(v);
}

VERSE *parse_verse(line)
	char	*line;
{
	char	*cp, *dp;
	register char	*book_name;
	int	i, chapter, verse;
	BOOK	*b = NULL;
	VERSE	*v = NULL;
	char	*tmp;
	
	tmp = cp = strdup(line);
	cp = parse_skip((dp = cp), ' ');
	book_name = strdup(dp);
	for (i = 0; i <NUMBOOKS; i++)
		if (!strcasecmp(book_name, booktab[i].name) ||
		    !strcasecmp(book_name, booktab[i].abbrev) ||
		    !strcasecmp(book_name, booktab[i].key))
			b = &booktab[i];
	if (!b)
		return(NULL);
	free(book_name);
	while (cp && *cp == ' ') cp++;
	if (!*cp)
		return(NULL);
	cp = parse_skip((dp = cp), ':');
	chapter = atoi(dp);
	if (!*cp)
		return(NULL);
	verse = atoi(cp);
	if ((v = (struct verse *) malloc(sizeof(VERSE))) == NULL) {
		fflush(stdout);
		fprintf(stderr, "Out of memory in parse_verse!\n");
		abort();
	}
	v->bk = b;
	v->c = chapter;
	v->v = verse;
	v->s = 0;
	free(tmp);
	return(v);
}

open_book(b)
	BOOK	*b;
{
	int	i;
	int	old_slot;	/* Slot to free up */
	long	last_time;	/* Time last used by above */
	
	old_slot = 0;
	last_time = open_book_time;
	for (i=0; i < NUM_OPEN; i++) {
		if (b == open_books[i]) {
			b->last_used = open_book_time++;
			b = current_book;
			return;
		}
		if (!open_books[i]) {
			old_slot = i;
			last_time = 0; /* Use this slot! */
		} else if (open_books[i]->last_used  < last_time) {
			old_slot = i;
			last_time = open_books[i]->last_used;
		}
	}
	/*
	 * OK, old_slot now contains the slot we should flush and use
	 * for the newly open book.
	 */
	strcpy(tmp, LIBDIR);
	if (!(b->f = fopen(strcat(tmp, b->filename), "r"))) {
		perror(tmp);
		fprintf(stderr, "Cannot open book text file!\n");
		exit(1);
	}
	strcpy(tmp, LIBDIR);
	if (!(b->fx = fopen(strcat(tmp, frob_ext(b->filename, ".idx")),
			    "r"))) {
		perror(tmp);
		fprintf(stderr, "Cannot open book index file!\n");
		exit(1);
	}
	
	if (open_books[old_slot]) {
#ifdef notdef
		printf("Flushing: %s\n", open_books[old_slot]->name);
#endif
		fclose(open_books[old_slot]->f);
		fclose(open_books[old_slot]->fx);
		open_books[old_slot]->current_pos = -1;
	}
	current_book = open_books[old_slot] = b;
	b->current_pos = 0;
	open_books[old_slot]->last_used = open_book_time++;
}

/*
 * Try to find a given verse and fill its contents into the verse
 * structure.  If found, that verse becomes the current verse.
 * Otherwise, the current verse remains the same.
 */
lookup_verse(v)
	VERSE	*v;
{
	FILE	*fx,*f;
	int	high, low, mid;
	int	num = 0;
	struct verse_idx vidx;
	
	if (v->s)
		free(v->s);
	if (v->c > v->bk->numchap)
		return;		/* The chapter number is illegal */
	open_book(v->bk);
	fx = v->bk->fx;
	f = v->bk->f;
	high = 0;
	fseek(fx, 0, 2);
	low = ftell(fx) / sizeof(vidx);
	while (1) {
		num++;
		mid = (high+low) / 2;
		fseek(fx, mid *sizeof(vidx), 0);
		fread(&vidx, sizeof(vidx), 1, fx);
		if (vidx.chapter == v->c) {
			if (vidx.verse == v->v)
				break;
			else if (vidx.verse < v->v)
				high = mid+1;
			else
				low = mid-1;
		} else {
			if (vidx.chapter < v->c)
				high = mid+1;
			else
				low = mid-1;
		}
		if (high > low)
			return;
	}
	v->bk->current_pos = mid *sizeof(vidx);
	if (fseek(f, vidx.pos, 0)) {
		perror("lookup_verse: fseek");
		exit(1);
	}
	read_verse(f, v);
}

VERSE *goto_rel_verse(offset)
	int	offset;
{
	register BOOK	*b = current_book;
	FILE	*fx;
	struct verse_idx vidx;
	VERSE	*v = NULL;
	long	new_pos;

	if (!b)
		return(NULL);	/* No current book */
	b->last_used = open_book_time++;
	new_pos = b->current_pos + (offset*sizeof(vidx));
	fx = b->fx;
	if (new_pos < 0 ||
	    fseek(fx, new_pos, 0))
		return(NULL);
	fread(&vidx, sizeof(vidx), 1, fx);
	if (feof(fx)) {
		clearerr(fx);
		return(NULL);
	}
	b->current_pos = new_pos;
	if ((v = (struct verse *) malloc(sizeof(VERSE))) == NULL) {
		fflush(stdout);
		fprintf(stderr, "Out of memory in goto_rel_verse!\n");
		abort();
	}
	v->bk = b;
	v->c = vidx.chapter;
	v->v = vidx.verse;
	v->s = 0;
	fseek(b->f, vidx.pos, 0);
	read_verse(b->f, v);
	return(v);
}


VERSE *read_verse(f, v)
	FILE	*f;
	VERSE	*v;
{
	char	*lines[64];	/* 64 lines for one verse?!? */
	int	i, num = 0, size = 0;
	register char	*cp;
	
	fgets(tmp, sizeof(tmp), f);
	cp = tmp;
	if (*cp != '\037') {
		fprintf(stderr, "Programming error in read_verse...\n");
		fprintf(stderr, "Not positioned correctly;  Contact help!\n");
		abort();
	}
	if (!v) {
		v = parse_verse(tmp+1);
		if (!v)
			problem();
	}
	cp = index(cp, ':');
	if (!cp)
		problem();
	while (*cp && *cp != ' ') cp++;
	while (*cp && *cp == ' ') cp++;
	if (!*cp)
		problem();
	lines[num++] = strdup(cp);
	while (!feof(f)) {
		fgets(tmp, sizeof(tmp), f);
		if (tmp[0] == '\n')
			break;
		lines[num++] = strdup(tmp);
	}
	for (i=0; i < num; i++)
		size += strlen(lines[i]);
	cp = malloc(size+1);
	*cp = '\0';
	for (i=0; i <num; i++) {
		strcat(cp, lines[i]);
		free(lines[i]);
	}
	v->s = cp;
	return(v);
}

problem()
{
	fflush(stdout);
	fprintf(stderr, "There is a problem with the database!!!!\n");
	fprintf(stderr, "Contact help!\n");
	fflush(stderr);
	abort();
}	
	
