/*
 * Title:
 *	database.c
 *
 * Authors:
 *	Michael P. Schenck
 *
 * Purpose:
 *	Maintains the storage of all the models and objects used in the 
 *	renderer.  Models can be requested.  Objects although are not
 *	used by the main program directly.  They are allocated when a node
 *	in the displaymap is created which has a model linked to it.  Models
 *	are the schematics of the objects.  Multiple objects can use the same
 * 	model for thier design and this can allow the model to be changed
 *	(only position of verticies) to create animation or to morph one
 *	model to another.  Models can be released, but do not need to
 *	be.  When the system is closed, all models will be released anyway.  
 *	When a model is requested, an id is returned specific to the model.
 *	If a model could not be setup, a NOMODEL value is returned.
 *
 * Copyright Info:
 *	Copyright (C) 1993, 1994 -- by Michael P. Schenck, 
 *	(mps4466@ultb.isc.rit.edu)
 *	
 *	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.
 *
 *	This software is distributed in the hope that it will be useful, but
 *	WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *      For a copy of the GNU General Public License
 *	write to the Free Software Foundation, 675 Mass Ave,
 *	Cambridge, MA  02139, USA.
 *
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "/include/types.h"
#include "/include/errors.h"
#include "/include/displaymap.h"
#include "/include/matrix.h"
#include "/include/database.h"

struct Model *models[MAXNUMMODELS+1];
struct Object *objects[MAXNUMOBJECTS+1];

static UBYTE *linemap = NULL;
static ULONG curlmsize = 0;
static UBYTE modelpath[MAXPATHLEN];
static UBYTE temp[MAXPATHLEN+MAXNAMELEN];
static FILE *fp;

ULONG findemptymodelnode(void);
ULONG findmodel(UBYTE *);
ULONG findemptyobjectnode(void);

	/* Open the database and set the path to the models. */

void opendatabase(UBYTE *path)

{
   cleardisplaymap();	
   setdatabasepath(path);
}

void setdatabasepath(UBYTE *path)

{
   strcpy(modelpath,path);	
}	   

	/* Find an empty model node in the list. */

ULONG findemptymodelnode()

{
   ULONG i;
	
   for(i=0;i<MAXNUMMODELS;i++) {	
      if(models[i]==NULL)
         return(i);
   }
   return(NOMODEL);
}   	 

	/* Looks to see if a model has already been loaded into the database. */

ULONG findmodel(UBYTE *name)

{
   ULONG i;
   
   for(i=0;i<MAXNUMMODELS;i++) {	
      if((models[i]!=NULL)&&(stricmp(models[i]->name,name)==0)&&(models[i]->links!=0))
         return(i);
   }
   return(NOMODEL);
}
	
	/* Requests a model to be loaded into the database. */
	
ULONG requestmodel(UBYTE *name, UBYTE type)

{
   ULONG position,numlinks,numvert,numpoly,j,k,offset,firstvert,curvert,nextvert;
   UBYTE *linemaptmp;
   	
 	
   if(type==SINGLELINK) {
      position=findemptymodelnode();
      numlinks=0;
   }
   else {
      if((position=findmodel(name))!=NOMODEL) {
	 models[position]->links++;
	 numlinks=models[position]->links;
      }
      else {
	 position=findemptymodelnode();
	 numlinks=1;
      }
   } 		
   if((position!=NOMODEL)&&(numlinks<2)) {	   
      strcpy(temp,modelpath);	 
      if((fp=fopen(strcat(temp,name),"r"))!=NULL) {
         fread((void *)&numvert,sizeof(ULONG),1,fp);
	 fread((void *)&numpoly,sizeof(ULONG),1,fp);
	 if((models[position]=(struct Model *)malloc(sizeof(struct Model)))==NULL) {
	    fclose(fp);
	    models[position]=NULL;
	    return(NOMODEL);
	 }
         if((models[position]->verticies=(FLOAT *)malloc(4*numvert*sizeof(FLOAT)))==NULL) {
	    fclose(fp);
	    free((void *)models[position]);
	    models[position]=NULL;
	    return(NOMODEL);
	 }
	 if((models[position]->polygons=(ULONG *)malloc(numpoly*MAXPOLYVERT*sizeof(ULONG)))==NULL) {
	    fclose(fp);
	    free((void *)models[position]->verticies);
	    free((void *)models[position]);
	    models[position]=NULL;
	    return(NOMODEL);
	 }
         models[position]->links=numlinks;
	 models[position]->numvert=numvert;
	 models[position]->numpoly=numpoly;
	 strcpy(models[position]->name,name);
	 fread((void *)models[position]->verticies,sizeof(FLOAT),4*numvert,fp);
         fread((void *)models[position]->polygons,sizeof(ULONG),numpoly*MAXPOLYVERT,fp);
         fclose(fp);
	 if(numvert > curlmsize) {
	    if((linemaptmp = (UBYTE *)realloc((void *)linemap,numvert*numvert*sizeof(UBYTE))) == NULL) {
       	       free((void *)models[position]->polygons);
	       free((void *)models[position]->verticies);
	       free((void *)models[position]);
	       models[position]=NULL;
	       return(NOMODEL);
	    }
	    linemap = linemaptmp;
	    curlmsize = numvert;
	 }
	 
	 for(j=0;j<curlmsize*curlmsize;j++)
	    *(linemap+j) = FALSE;

	 if((models[position]->edgeshare=(UBYTE *)malloc(numpoly*MAXPOLYVERT*sizeof(UBYTE)))==NULL) {
	    free((void *)models[position]->polygons);
	    free((void *)models[position]->verticies);
	    free((void *)models[position]);
	    models[position]=NULL;
	    return(NOMODEL);
	 }
	 
	 for(j=0;j<numpoly*MAXPOLYVERT;j++)
	    *(models[position]->edgeshare+j) = TRUE;
	 	    
	 for(j=0;j<numpoly*MAXPOLYVERT;j+=MAXPOLYVERT) {
	    firstvert = *(models[position]->polygons+j);
	    curvert = firstvert;
	    for(k=1;k<(MAXPOLYVERT-1);k++) {
	       offset = k;
	       if((nextvert = *(models[position]->polygons+j+k))==NOVERT) {
	          k=MAXPOLYVERT-1;
	          nextvert = firstvert;
	       }
	       if(*(linemap+curvert*curlmsize+nextvert) == FALSE) {
	       	  *(linemap+curvert*curlmsize+nextvert) = TRUE;
		  *(linemap+nextvert*curlmsize+curvert) = TRUE;
	       }
	       else
	          *(models[position]->edgeshare+j+offset-1) = FALSE;
	       curvert = nextvert;
	    }
	 }
      }
      else
	 position=NOMODEL;
   }
   return(position);      
}           
 
 	/* Releases the model specified.  If there is more than one link,
	   it decrements the link by one. */
 
void releasemodel(ULONG model)

{
   if(models[model]->links>1)
      models[model]->links--;
   else {
      free((void *)models[model]->edgeshare);
      free((void *)models[model]->verticies);
      free((void *)models[model]->polygons);
      free((void *)models[model]);
      models[model]=NULL;
   }      	   	
}	   

	/* Finds an available object node. */

ULONG findemptyobjectnode()

{
   ULONG i;
	
   for(i=0;i<MAXNUMOBJECTS;i++) {	
      if(objects[i]==NULL)
         return(i);
   }
   return(NOOBJECT);
}   	 

	/* Allocates space for an object and links to an object node. */

ULONG allocateobject(ULONG model) 

{ 
   ULONG i,position;	
	
   if((position=findemptyobjectnode())==NOOBJECT)
      return(NOOBJECT);
   if((objects[position]=(struct Object *)malloc(sizeof(struct Object)))==NULL)
      return(NOOBJECT);
   if((objects[position]->verticies=(FLOAT *)malloc(4*models[model]->numvert*sizeof(FLOAT)))==NULL) {
      free((void *)objects[position]);
      objects[position]=NULL;
      return(NOOBJECT);
   }	
   if((objects[position]->transvert=(FLOAT *)malloc(4*models[model]->numvert*sizeof(FLOAT)))==NULL) {
      free((void *)objects[position]->verticies);
      free((void *)objects[position]);
      objects[position]=NULL;
      return(NOOBJECT);
   }	
   if((objects[position]->vertflag=(UBYTE *)malloc(models[model]->numvert*sizeof(UBYTE)))==NULL) {
      free((void *)objects[position]->verticies);
      free((void *)objects[position]->transvert);
      free((void *)objects[position]);
      objects[position]=NULL;
      return(NOOBJECT);
   }
   if((objects[position]->polygons=(ULONG *)malloc(models[model]->numpoly*MAXPOLYVERT*sizeof(ULONG)))==NULL) {
      free((void *)objects[position]->verticies);
      free((void *)objects[position]->transvert);
      free((void *)objects[position]->vertflag);
      free((void *)objects[position]);
      objects[position]=NULL;
      return(NOOBJECT);
   }
   for(i=0;i<models[model]->numpoly*MAXPOLYVERT;i++)
      *(objects[position]->polygons+i) = *(models[model]->polygons+i);
   objects[position]->numvert = models[model]->numvert;
   objects[position]->numpoly = models[model]->numpoly;
   objects[position]->edgeshare = models[model]->edgeshare;
   return(position);  
}
	
	/* Deallocates an object. */
	
void deallocateobject(ULONG object)

{  
   free((void *)objects[object]->verticies);
   free((void *)objects[object]->transvert);
   free((void *)objects[object]->vertflag);  
   free((void *)objects[object]->polygons);
   free((void *)objects[object]);
   objects[object]=NULL;        
}        
 
 	/* Closes down the database, releasing all models and objects. */
 
void closedatabase()

{
   ULONG i;
	
   cleardisplaymap();
   for(i=0;i<MAXNUMMODELS;i++) {
      if(models[i]!=NULL)
         releasemodel(i);
   }
   for(i=0;i<MAXNUMOBJECTS;i++) {
      if(objects[i]!=NULL)
         deallocateobject(i);
   }
   if(linemap)
      free((void *)linemap);
}
