/*
 * DBOT Version 1.0           Author :  Vincent Hayward
 *                                      School of Electrical Engineering
 *                                      Purdue University
 *      Dir     : db
 *      File    : dbot.c
 *      Remarks : All the functions of the library are there.
 *      Usage   : make install
 */

/*LINTLIBRARY*/

#include <stdio.h>
#include <sys/types.h>
#include "../h/rccl.h"
#include "../h/umac.h"

#define BEGIN   0               /* seek options */
#define RELAT   1               /* ....         */
#define ENDOF   3               /* ....         */
#define RW      2               /* open ....... */
#define BUFS    1024            /* best r/w buf */
#define FREE    0               /* entry not allocated  */
#define HOLE    -1              /* entry removed        */
#define MAGICD  0123            /* magic number data b  */
#define LABS    16              /* label size           */

#define MAX     85              /* 85 * sizeof(TRID) == 2040 */

typedef struct {                /* transform identity   */
	char label[LABS];       /* name                 */
	time_t ttime;           /* birth date           */
	long addr;              /* address              */
}TRID;

typedef struct {                /* internal storage represention */
	float px, py, pz, ox, oy, oz, ax, ay, az;
} DBTR, *DBTR_PTR;

/*
 * core image of the file header
 */

static struct head {
	int magic;
	int nb;
	TRID table[MAX];
} hd;

char *ctime();
time_t time();
long lseek();




#define READS(f, p)\
if (read(f, (char *)&p, sizeof(p)) < 0) {\
	fprintf(stderr, "read error on data base file\n");\
	return(-1);}

#define WRITES(f, p)\
if (write(f, (char *)&p, sizeof(p)) < 0) {\
	fprintf(stderr, "write error on data base file\n");\
	return(-1);}

#define SEEK(f, a, n)\
if (lseek(f, a, n) < 0) {\
	fprintf(stderr, "seek error on data base file\n");\
	return(-1);}

#define ISDB(h)\
if (h .magic != MAGICD) {\
	fprintf(stderr, "bad magic number\n");\
	return(-1);}

/*
 * creates a new data base file, which contains at least a table
 * of MAX TRID records.
 * All the records are initialize with a 0 creation or change time.
 *
 * A 'FREE' teach time means free entry in table.
 * Each time an entry is added, all the addr pointers of the 'FREE'
 * entries are set to the size of the file.
 * When an entry is removed, a HOLE is created.
 * The labels are C strings and therefore cannot contain more
 * than LABS -1 chars.
 */

/*
 * create an empty data base
 */

makedb(name) /*::*/
char *name;
{
	int fd, i, j;

	if ((fd = creat(name, 0644)) < 0) {
		fprintf(stderr, "could'nt creat transform data base file\n");
		return(-1);
	}
	if ((fd = open(name, RW)) < 0) {
		fprintf(stderr, "open error on data base file");
		return(-1);
	}
	for (i = 0; i < MAX; ++i) {
		for (j = 0; j < LABS; ++j) {
			hd.table[i].label[j] = '\0';
		}
		hd.table[i].ttime = FREE;
		hd.table[i].addr = sizeof(hd);
	}
	hd.magic = MAGICD;
	hd.nb = 0;
	WRITES(fd, hd);
	return(fd);
}


/*
 * sequential search of the entries in the table
 * return
 *              index in the table,
 *              -2 if not found,
 *              -1 if io error
 */

static search(n, fd) /*##*/
char *n;
int fd;
{
	int i;

	SEEK(fd, 0L, BEGIN);
	READS(fd, hd);
	ISDB(hd);
	for (i = 0; i < MAX; ++i) {
		if (hd.table[i].ttime == FREE || hd.table[i].ttime == HOLE) {
			continue;
		}
		if (strncmp(n, hd.table[i].label, LABS - 1) == 0) {
			return(i);
		}
	}
	return(-2);
}


/*
 * save a transform in the data base
 * 1) look if exits, if yes prompts the user if can change change
 * 2) tries to fill holes
 * 3) extent if no hole
 *
 * return
 *              1  if change aborted by user
 *              0  if store successful
 *              -1 if io error
 */

savetr(t, fd) /*::*/
TRSF_PTR t;
int fd;
{
	int q, i, j;
	DBTR dbt;
	char *m;

	i = search(t->name, fd);
	if (i == -1) {
		fprintf(stderr, "search error\n");
		return(-1);
	}
	if (i >= 0) {
		printf("savetr : %s created : %s",
			hd.table[i].label, ctime(&hd.table[i].ttime));
		printf("change ? ");
		QUERY(q);
		if (q == 'n') {
			return(1);
		}
	 (void) time(&hd.table[i].ttime);
		m = "savetr : %s changed at %s";
	}
	else {
		q = NO;
		for (i = 0; i < MAX; ++i) {
			if (hd.table[i].ttime == HOLE) {
			 (void) time(&hd.table[i].ttime);
				m = "savetr : %s Added at %s";
				q = YES;
				++hd.nb;
				break;
			}
		}
		if (!q) {
			q = NO;
			for (i = 0; i < MAX; ++i) {
				if (hd.table[i].ttime == FREE) {
				 (void) time(&hd.table[i].ttime);
					m = "savetr : %s added at %s";
					q = YES;
					++hd.nb;
					break;
				}
			}

			if (!q) {
				fprintf(stderr, "data base file saturated\n");
				return(-1);
			}
			for (j = 0; j < MAX; ++j) {
				if (hd.table[j].ttime == FREE) {
					hd.table[j].addr += sizeof(DBTR);
				}
			}
		}
	}
	strcpyn(hd.table[i].label, t->name, LABS);
	printf(m, hd.table[i].label, ctime(&hd.table[i].ttime));
	SEEK(fd, hd.table[i].addr, BEGIN);
	cnvtrdb(&dbt, t);
	WRITES(fd, dbt);
	SEEK(fd, 0L, BEGIN);
	WRITES(fd, hd);
	return(0);
}


/*
 * get a transform out of the data base
 *
 * return
 *              0  if found
 *              -2 if not found
 *              -1 if io error
 */


gettr(t, fd) /*::*/
TRSF_PTR t;
int fd;
{
	int i;
	DBTR dbt;

	i = search(t->name, fd);
	if (i == -1) {
		fprintf(stderr, "search error\n");
		return(-1);
	}
	if (i >= 0) {
		SEEK(fd, hd.table[i].addr, BEGIN);
		READS(fd, dbt);
		cnvdbtr(t, &dbt);
		printf("gettr : %s last change : %s",
			hd.table[i].label, ctime(&hd.table[i].ttime));
		return(0);
	}
	else {
		printf("gettr : %s not found\n", t->name);
		return(-2);
	}
}


/*
 * remove a transform out of the data base
 *
 * return
 *              0  is successful
 *              -2 if not found
 *              -1 if io error
 */

remtr(n, fd) /*::*/
char *n;
int fd;
{
	int i;

	i = search(n, fd);
	if (i == -1) {
		fprintf(stderr, "search error\n");
		return(-1);
	}
	if (i >= 0) {
		hd.table[i].ttime = HOLE;
		--hd.nb;
		SEEK(fd, 0L, BEGIN);
		WRITES(fd, hd);
		printf("remtr : %s removed\n", n);
		return(0);
	}
	else {
		printf("remtr : %s not found\n", n);
		return(-2);
	}
}



/*
 * print the content of the data base
 *
 * return
 *              0  if ok
 *              -1 if io error
 */

dumpdb(fd, v) /*::*/
int fd;
bool v;
{
	TRSF tr;
	DBTR dbt;
	int i;

	SEEK(fd, 0L, BEGIN);
	READS(fd, hd);
	ISDB(hd);
	printf("dump : %d entries\n", hd.nb);
	for (i = 0; i < MAX; ++i) {
		if (hd.table[i].ttime != FREE && hd.table[i].ttime != HOLE) {
			printf("%d  ", (hd.table[i].addr - sizeof(hd)) / sizeof(DBTR));
			printf("%s , %s", hd.table[i].label, ctime(&hd.table[i].ttime));
			if(v) {
				SEEK(fd, hd.table[i].addr, BEGIN);
				READS(fd, dbt);
				cnvdbtr(&tr, &dbt);
				printf("%8.3f %8.3f %8.3f %8.3f\n",
					tr.n.x, tr.o.x, tr.a.x, tr.p.x);
				printf("%8.3f %8.3f %8.3f %8.3f\n",
					tr.n.y, tr.o.y, tr.a.y, tr.p.y);
				printf("%8.3f %8.3f %8.3f %8.3f\n",
					tr.n.z, tr.o.z, tr.a.z, tr.p.z);
			}
		}
	}
	return(0);
}


/*
 * compact the data base in place, for that make a copy of it
 *
 * return
 *              0  if ok
 *              -1 if io error
 */


static struct head hdc;

compactdb(name)
char *name;
{
	char temp[40];
	int fd, fdt;
	char bd[BUFS];
	int n;
	DBTR dbt;
	int i, j, inc;
	char *sprintf();

	if ((fd = open(name, RW)) < 0) {
		fprintf(stderr, "open error on data base file %s\n", name);
		return(-1);
	}
 (void) sprintf(temp, "%d.cdb", getpid());
	if ((fdt = creat(temp, 0644)) < 0) {
		fprintf(stderr, "can't creat data base file %s\n", temp);
		return(-1);
	}
	if ((fdt = open(temp, RW)) < 0) {
		fprintf(stderr, "open error on data base file %s\n", temp);
		return(-1);
	}
	SEEK(fd, 0L, BEGIN);
	while ((n = read(fd, bd, BUFS)) > 0) {
		if (write(fdt, bd, n) < 0) {
			fprintf(stderr, "can't duplicate data base file\n");
			return(-1);
		}
	}

	fd = makedb(name);
	SEEK(fdt, 0L, BEGIN);
	READS(fdt, hdc);
	j = 0;
	inc = 0;
	for (i = 0; i < MAX; ++i) {
		if (hdc.table[i].ttime != HOLE && hdc.table[i].ttime != FREE) {
			SEEK(fdt, hdc.table[i].addr, BEGIN);
			READS(fdt, dbt);
			WRITES(fd, dbt);
			hd.table[j].addr += inc;
			inc += sizeof(DBTR);
			hd.table[j].ttime = hdc.table[i].ttime;
			strcpyn(hd.table[j].label, hdc.table[i].label, LABS);
			++hd.nb;
			++j;
		}
	}
	for (;j < MAX; ++j) {
		hd.table[j].addr += inc;
	}
	SEEK(fd, 0L, BEGIN);
	WRITES(fd, hd);
	if (unlink(temp) < 0) {
		fprintf(stderr, "could'nt unlink\n");
		return(-1);
	}
	return(0);
}


/*
 * convert a regular transform into data base format
 */

static cnvtrdb(d, t) /*##*/
DBTR_PTR d;
TRSF_PTR t;
{
	d->px = t->p.x;
	d->py = t->p.y;
	d->pz = t->p.z;
	d->ax = t->a.x;
	d->ay = t->a.y;
	d->az = t->a.z;
	d->ox = t->o.x;
	d->oy = t->o.y;
	d->oz = t->o.z;
}


/*
 * convert a data base format transform into regular
 */

static cnvdbtr(t, d) /*##*/
DBTR_PTR d;
TRSF_PTR t;
{
	t->p.x = d->px;
	t->p.y = d->py;
	t->p.z = d->pz;
	t->a.x = d->ax;
	t->a.y = d->ay;
	t->a.z = d->az;
	t->o.x = d->ox;
	t->o.y = d->oy;
	t->o.z = d->oz;
	t->n.x = t->o.y * t->a.z - t->o.z * t->a.y;
	t->n.y = t->o.z * t->a.x - t->o.x * t->a.z;
	t->n.z = t->o.x * t->a.y - t->o.y * t->a.x;
}


/*
 * copy n chars of a string
 */

static strcpyn(s1, s2, n) /*##*/
char *s1, *s2;
int n;
{
	while (*s1++ = *s2++) {
		if (--n == 0) {
			*--s1 = '\0';
			return;
		}
	}
}
