/* The configuration file for the robot simulator provides the 
   following information:

   (1) for each robot to control:

	(a) its name

	(b) its location (opt)

	(c) its positioning function (opt)

	(d) initial position (opt)

	(e) its end effector

   (2) for each object to introduce:

OK	(a) its name (opt)

OK	(b) its type

OK	(c) its parameters

OK	(d) its location (opt)

OK	(e) its positioning function (opt)

OK	(f) material/color (opt)

   (3) general paramters, such as

OK	(a) the initial viewing window (opt)

OK	(b) the initial viewing location (opt)

OK	(c) screen window size and position

OK	(d) colors

LATER	(e) light sources
*/

#include <stdio.h>
#include <strings.h>

#include "robotsim.h"

#include "../h/cdefs.h"
#include "../h/robotMath.h"
#include "grf4.h"
#include "../pdb/pdb.h"

#include "linkedList.h"

extern RobotDesc *robots;
extern StaticRobotDesc *staticRobots;
extern ObjectDesc *objects;
extern MaterialDesc *materials;
extern LightDesc *lights;

extern LocFxnDesc *getLocfxnByName();

extern void *malloc();

static char *copystr(s)
char *s;
{
	return (strcpy ((char*)malloc(strlen(s)+1), s));
}

/* XXX not sure about getRobotByName and getStaticRobotByName */

RobotDesc *getRobotByName (name)
char *name;
{
	register RobotDesc *rd;
	for (rd=robots; rd; rd=rd->next)
	 { if (rd->name && strcmp(rd->name, name) == 0)
	    { return (rd);
	    }
	 }
	return (0);
}

StaticRobotDesc *getStaticRobotByName (name)
char *name;
{
	register StaticRobotDesc *sd;
	for (sd=staticRobots; sd; sd=sd->next)
	 { if (sd->name && strcmp(sd->name, name) == 0)
	    { return (sd);
	    }
	 }
	return (0);
}

ObjectDesc *getObjectByName (name)
char *name;
{
	register ObjectDesc *obj;
	for (obj=objects; obj; obj=obj->next)
	 { if (obj->name && strcmp(obj->name, name) == 0)
	    { return (obj);
	    }
	 }
	return (0);
}

MaterialDesc *getMaterialByName (name)
char *name;
{
	register MaterialDesc *mat;
	for (mat=materials; mat; mat=mat->next)
	 { if (mat->name && strcmp(mat->name, name) == 0)
	    { return (mat);
	    }
	 }
	return (0);
}

addStaticRobot (sd)
StaticRobotDesc *sd;
{
	sd->next = staticRobots;
	staticRobots = sd;
}

addObject (obj)
ObjectDesc *obj;
{
	obj->next = objects;
	objects = obj;
}

addMaterial (mat)
MaterialDesc *mat;
{
	mat->next = materials;
	materials = mat;
}

addLight (l)
LightDesc *l;
{
	l->next = lights;
	lights = l;
}

ObjectDesc *setObject();
MaterialDesc *setMaterial();
LightDesc *setLight();

/* XXX must retarget these parameter variables */

extern PdbPar *viewWindowConfig;
extern PdbPar *viewTransConfig;
extern PdbPar *windowPosConfig;
extern int orthoProjection;

extern int bgColor;
extern int fgColor;

interpretParamSet(set)
PdbSet *set;
{
	PdbPar *p;

	for (p=set->list; p; p=p->next)
	 { if (!p->name)
	    { errorMsg (0, "line %d: unnamed %s param\n",
			   p->lineno, pdbTypeStr(p->type));
	      return (-1);
	    }
	   else if (p->flags & PDB_DECLARED)
	    { if (strcmp (p->name, "viewWindow") == 0)
	       { if (!IS_DOUBLE_VECTOR(p) || p->val.set->len > 6)
		  { errorMsg (0, 
"'viewWindow' must be a vector of length <= 6\n");
		    return (-1);
		  }
		 viewWindowConfig = p;
	       }
	      else if (strcmp (p->name, "windowPos") == 0)
	       { if (!IS_INTEGER_VECTOR(p) || p->val.set->len > 4)
		  { errorMsg (0, 
"'windowPos' must be an integer vector of length <= 4\n");
		    return (-1);
		  }
		 windowPosConfig = p;
	       }
	      else if (strcmp (p->name, "viewTrans") == 0)
	       { if (!IS_DOUBLE_VECTOR(p) || p->val.set->len > 6)
		  { errorMsg (0, 
"'viewTrans' must be a vector of length <= 6\n");
		    return (-1);
		  }
		 viewTransConfig = p;				
	       }
	      else if (strcmp (p->name, "bgColor") == 0)
	       { if (p->type != PDB_INTEGER)
		  { errorMsg (0, 
"'bgColor' must be an integer\n");
		    return (-1);
		  }
		 bgColor = p->val.i;
	       }
	      else if (strcmp (p->name, "fgColor") == 0)
	       { if (p->type != PDB_INTEGER)
		  { errorMsg (0, 
"'fgColor' must be an integer\n");
		    return (-1);
		  }
		 fgColor = p->val.i;
	       }
	      else if (strcmp (p->name, "ortho") == 0)
	       { if (p->type != PDB_INTEGER ||
		     (p->val.i != 0 && p->val.i != 1))
		  { errorMsg (0, 
"'ortho' must be either 0 or 1\n");
		    return (-1);
		  }
		 orthoProjection = p->val.i;
	       }
	      else
	       { errorMsg (0, 
"line %d: unrecognized param %s\n", p->lineno, p->name);
		 return (-1);
	       }
	    }
	   else /* p->flags & PDB_CONSTRUCTED */
	    { 
	      if (p->type != PDB_SET)
	       { errorMsg (0,
"line %d: constructor %s must be followed by a set\n", p->lineno, p->name);
		 return (-1);
	       }
	      if (strcmp (p->name, "OBJECT") == 0)
	       { ObjectDesc *obj;
		 
		 if ((obj=setObject(p->val.set)) == 0)
		  { return (-1);
		  }
		 addNewDrawable (DRAW_OBJECT, (void*)obj);
	       }
	      else if (strcmp (p->name, "ROBOT") == 0)
	       { if (setRobot(p->val.set) < 0)
	      	  { return (-1);
		  }
	       }
	      else if (strcmp (p->name, "MATERIAL") == 0)
	       { if (!setMaterial(p->val.set))
	      	  { return (-1);
		  }
	       }
	      else if (strcmp (p->name, "LIGHT") == 0)
	       { if (!setLight(p->val.set))
	      	  { return (-1);
		  }
	       }
	      else
	       { errorMsg (0, "line %d: unrecognized constructor %s\n",
			       p->lineno, p->name);
		 return (-1);
	       }
	    }
	 }
	return (1);
}

ObjectDesc *copyObject(ref)
ObjectDesc *ref;
{
	ObjectDesc *obj, *new, *son;
	ObjectDesc *sons_tail;

	obj = (ObjectDesc*)calloc(1,sizeof(ObjectDesc));

	if (ref->loc)
	 { obj->loc = ALLOC(TRSF);
	   *obj->loc = *ref->loc;
	 }
	if (ref->locfxn)
	 { obj->locfxn = ALLOC(LocFxnDesc);
	   *obj->locfxn = *ref->locfxn;
	 }
	if (ref->sons)
	 { sons_tail = 0;
	   for (son=ref->sons; son; son=son->sibs)
	    { new = copyObject(son);
	      if (!sons_tail)
	       { obj->sons = sons_tail = new;
	       }
	      else
	       { sons_tail->next = new;
		 sons_tail = new;
	       }
	    }
	 }
	else
	 { obj->go = ref->go;
	 }	
	return (obj);
}

ObjectDesc *setObject(set)
PdbSet *set;
{
	PdbPar *p;
	ObjectDesc *obj;

	obj = (ObjectDesc*)calloc(1,sizeof(ObjectDesc));

	p = set->list;
	if (p->name)
	 { errorMsg (0,
"line %d: 1st object param should be untagged\n", p->lineno);
	   return (0);
	 }
	if (p->type != PDB_STRING)
	 { errorMsg (0, 
"line %d: 1st object param must be string defining the type\n", p->lineno);
	   return (0);
	 }
	else if (strcmp (p->val.s, "prism") == 0)
	 { if (setPrism (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "box") == 0)
	 { if (setBox (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "disc") == 0)
	 { if (setDisc (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "cone") == 0)
	 { if (setCone (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "sphere") == 0)
	 { if (setSphere (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "polygon") == 0)
	 { if (setPolygon (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "lines") == 0)
	 { if (setLines (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else if (strcmp (p->val.s, "compound") == 0)
	 { if (setCompound (obj, p->next) < 0)
	    { goto cantSet;
	    }
	 }
	else
	 { errorMsg (0, "line %d: unknown object type '%s'\n",
		      p->lineno, p->val.s);
	   return (0);
	 }
	addObject (obj);
	return (obj);
cantSet:
	errorMsg (0, "line %d: error in object parameters\n", p->lineno);
	return (0);
}

LocFxnDesc *setLocfxn(set)
PdbSet *set;
{
	PdbPar *p;
	LocFxnDesc *fxd;

	fxd = (LocFxnDesc*)calloc(1,sizeof(LocFxnDesc));
	p = set->list;
	if (p == 0 || p->type != PDB_STRING)
	 { errorMsg (0,
"First element in 'locfxn' set must a string identifying the function\n");
	   return (0);
	 }
	if (!parseLocfxnName(p->val.s, fxd))
	 { errorMsg (0,
"locfxn '%s' not defined\n", p->val.s);
	   return (0);
	 }
	p = p->next;
	if (p)
	 { if (p->type != PDB_INTEGER)
	    { errorMsg (0,
"Second element in 'locfxn' set must be an integer\n");
	      return (0);
	    }
	   fxd->arg = p->val.i;
	   if (p->next)
	    { errorMsg (0,
"Parameter set for 'locfxn' may have only two elements\n");
	      return (0);
	    }
	 }
	return (fxd);	
}

parseLocfxnName(name, fxd)
char *name;
LocFxnDesc *fxd;
{
	RobotDesc *rd;
	StaticRobotDesc *sd;

	char buf[256];
	LocFxnDesc *rxd;
	char *suffix;
	char *prefix;

	extern TRSF *genLocFxn();
	extern TRSF *robotHandLocFxn();

	/* 1. See if an explicit function name exists. */

	if (rxd = getLocfxnByName(name))
	 { *fxd = *rxd;
	   return (1);
	 }
	
	/* 2. See if the name has a '.' suffix */

	strcpy (buf, name);
	if ((suffix = index(buf, '.')) == 0)
	 { return (0);
	 }
	else
	 { *suffix++ = '\0';
	   prefix = buf;
	 }

	/* 3. See if the suffix matches the name of one of the
	      robs in the system. */

	for (rd=robots; rd; rd=rd->next)
	 { if (strcmp(suffix, rd->name) == 0)
	    { break;
	    }
	 }
	if (!rd)
	 { for (sd=staticRobots; sd; sd=sd->next)
	    { if (strcmp(suffix, sd->name) == 0)
	       { break;
	       }
	    }
	 }
	if (!rd && !sd)
	 { return (0);
	 }

	/* 3. If the prefix is "HandTX", "HandTY", "HandTZ", "HandRX", 
	      "HandRY", or "HandRZ", then set the locfxn to robotHandLocFxn.*/
	
	if (strncmp(prefix,"Hand",4)==0 && prefix[6] == '\0')
	 { if (prefix[4] == 'T' || prefix[4] == 'R')
	    { switch (prefix[5])
	       { case 'X':
		  { fxd->com = 1;
		    break;
		  }
		 case 'Y':
		  { fxd->com = 2;
		    break;
		  }
		 case 'Z':
		  { fxd->com = 3;
		    break;
		  }
	       }
	      if (fxd->com)
	       { fxd->fxn = robotHandLocFxn;
		 if (rd)
		  { fxd->ptr = (void*)&rd->rs.handPos;
		  }
		 else
		  { static int handPos = 1;
		    /* XXX ! */
		    fxd->ptr = (void*)&handPos;
		  }
		 if (prefix[4] == 'R')
		  { fxd->com += 3;
		  }
		 return (1);
	       }
	    }
	 }

	/* 4. If the prefix is T<n>, for N in 0..6, then set the
	      fxn to genLocFxn for the appropriate T<n> location. */

	if (prefix[0] == 'T' && prefix[2] == '\0')
	 { int n;

	   n = prefix[1] - '0';
	   if (0 <= n && n <= 6)
	    { fxd->fxn = genLocFxn;
	      if (rd)
	       { fxd->ptr = (void*)&rd->L[n];
	       }
	      else
	       { fxd->ptr = (void*)&sd->L[n];
	       }
	      return (1);
	    }
	 }
	return (0);
}


matchGenObjectParam(p, obj)
PdbPar *p;
ObjectDesc *obj;
{
	float locvec[6];
	int k;

	if (!p->name || p->flags & PDB_CONSTRUCTED)
	 { errorMsg (0, "Unnamed or constructed param in list\n");
	   return (-1);
	 }
	else if (strcmp (p->name, "loc") == 0)
	 { TRSF loc;
	   if (setLocation (p, &loc) < 0)
	    { return (-1);
	    }
	   obj->loc = ALLOC(TRSF);
	   *obj->loc = loc;
	 }
	else if (strcmp (p->name, "locfxn") == 0)
	 { if (p->type == PDB_SET)
	    { if ((obj->locfxn = setLocfxn(p->val.set)) == 0)
	       { return (-1);
	       }
	    }
	   else
	    { errorMsg (0, "'locfxn' param should be a set\n");
	      return (-1);
	    }
	 }
	else if (strcmp (p->name, "mat") == 0)
	 { 
	   if (p->type == PDB_STRING)
	    { if ((obj->matdef = getMaterialByName(p->val.s)) == NULL)
	       { errorMsg (0, "material %s undefined\n", p->val.s);
		 return (-1);
	       }
	    }
	   else if (p->type == PDB_SET)
	    { if ((obj->matdef = setMaterial(p->val.set)) == NULL)
	       { return (-1);
	       }
	    }
	   else
	    { errorMsg (0, "'mat' param should be an identifier or set\n");
	      return (-1);
	    }
	 }
	else if (strcmp (p->name, "name") == 0)
	 { if (p->type == PDB_STRING)
	    { obj->name = copystr(p->val.s);
	    }
	   else
	    { errorMsg (0, "'name' must be a string\n");
	      return (-1);
	    }
	 }
	else
	 { return (0);
	 }
	return (1);
}

setPrism (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	char *smooth = 0;
	float *topface = 0;
	float *botface = 0;
	float depth = -1.0;
	int nsides;

	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "depth") == 0)
	    { if (!pdbSetFloat (&depth, p) || depth <= 0.0)
	       { errorMsg (0, "'depth' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "topface") == 0)
	    { if (!IS_DOUBLE_VECTOR(p) || 
		   p->val.set->len < 6 || (p->val.set->len%2))
	       { errorMsg (0, 
"'topface' must be a vector with an even number of at least 6 elements\n");
		 return (-1);
	       }
	      if (!botface)
	       { nsides = p->val.set->len/2;
	       }
	      else
	       { if (nsides != p->val.set->len/2)
		  { errorMsg (0,
"'topface' must have same number of elements as 'botface'\n");
		    return (-1);
		  }
	       }
	      topface = pdbBuildVecF(p->val.set);
	    }
	   else if (strcmp (p->name, "botface") == 0)
	    { if (!IS_DOUBLE_VECTOR(p) || 
		   p->val.set->len < 6 || (p->val.set->len%2))
	       { errorMsg (0, 
"'topface' must be a vector with an even number of at least 6 elements\n");
		 return (-1);
	       }
	      if (!topface)
	       { nsides = p->val.set->len/2;
	       }
	      else
	       { if (nsides != p->val.set->len/2)
		  { errorMsg (0,
"'botface' must have same number of elements as 'topface'\n");
		    return (-1);
		  }
	       }
	      botface = pdbBuildVecF(p->val.set);
	    }
	   else if (strcmp (p->name, "smooth") == 0)
	    { char *cp;
	      if (p->type != PDB_STRING)
	       { errorMsg (0, 
"'smooth' must be a string of '1's and '0's\n");
		 return (-1);
	       }
	      smooth = p->val.s;
	      for (cp=smooth; *cp != '\0'; cp++)
	       { if (*cp != '1' && *cp != '0')
		  { errorMsg (0,
"'smooth' must be a string of '1's and '0's\n");
		    return (-1);
		  }
	       }
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (!botface)
	 { errorMsg (0, "'botface' not specified\n");
	   return (-1);
	 }
	if (depth == -1.0)
	 { errorMsg (0, "'depth' not specified\n");
	 }
	obj->go = grf4GenPrism (nsides, botface, topface, smooth, 
				depth, 0);
	return (1);
}

setBox (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	float dimen[3];
	float *face = 0;

	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "dimen") == 0)
	    { if (!IS_DOUBLE_VECTOR(p) || p->val.set->len != 3)
	       { errorMsg (0, "'dimen' must be a vector of length 3\n");
		 return (-1);
	       }
	      dimen[0] = dimen[1] = dimen[2] = 0;
	      pdbSetVecF (p->val.set, dimen);
	      if (dimen[0] <= 0.0 || dimen[1] <= 0.0 || dimen[2] <= 0.0)
	       { errorMsg (0, "Elements of 'dimen' must be greater than 0\n");
		 return (-1);
	       }
	      face = (float*)malloc(sizeof(float)*8);
	      face[0] = 0.0;
	      face[1] = 0.0;

	      face[2] = dimen[0];
	      face[3] = 0.0;

	      face[4] = dimen[0];
	      face[5] = dimen[1];

	      face[6] = 0.0;
	      face[7] = dimen[1];
	    }
	 }
	if (face == 0)
	 { errorMsg (0, "'dimen' not specified\n");
	   return (-1);
	 }
	obj->go = grf4GenPrism (4, face, (float*)0, (char*)0, 
				   dimen[2], 0);
	return (1);
}


setDisc (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	float rad = -1.0;
	float depth = -1.0;
	int nsides = -1;

	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "depth") == 0)
	    { if (!pdbSetFloat (&depth, p) || depth <= 0.0)
	       { errorMsg (0, "'depth' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "rad") == 0)
	    { if (!pdbSetFloat (&rad, p) || rad <= 0.0)
	       { errorMsg (0, "'rad' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "nsides") == 0)
	    { if (p->type != PDB_INTEGER || p->val.i < 3)
	       { errorMsg (0, "'nsides' must be an integer >= 3\n");
		 return (-1);
	       }
	      nsides = p->val.i;
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (rad == -1.0)
	 { errorMsg (0, "'rad' not specified\n");
	   return (-1);
	 }
	if (depth == -1.0)
	 { errorMsg (0, "'depth' not specified\n");
	 }
	if (nsides == -1)
	 { nsides = 12;
	 }
	obj->go = grf4GenDisc (nsides, rad, depth, 0);
	return (1);
}

setCone (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	float rbot = -1.0;
	float rtop = -1.0;
	float depth = -1.0;
	int nsides = -1;

	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "depth") == 0)
	    { if (!pdbSetFloat (&depth, p) || depth <= 0.0)
	       { errorMsg (0, "'depth' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "rtop") == 0)
	    { if (!pdbSetFloat (&rtop, p) || rtop <= 0.0)
	       { errorMsg (0, "'rtop' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "rbot") == 0)
	    { if (!pdbSetFloat (&rbot, p) || rbot <= 0.0)
	       { errorMsg (0, "'rbot' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "nsides") == 0)
	    { if (p->type != PDB_INTEGER || p->val.i < 3)
	       { errorMsg (0, "'nsides' must be an integer >= 3\n");
		 return (-1);
	       }
	      nsides = p->val.i;
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (rbot == -1.0)
	 { errorMsg (0, "'rbot' not specified\n");
	   return (-1);
	 }
	if (rtop == -1.0)
	 { errorMsg (0, "'rtop' not specified\n");
	   return (-1);
	 }
	if (depth == -1.0)
	 { errorMsg (0, "'depth' not specified\n");
	 }
	if (nsides == -1)
	 { nsides = 12;
	 }
	if (rtop == -1.0)
	 { obj->go = grf4GenDisc (nsides, rbot, depth, 0);
	 }
	else
	 { obj->go = grf4GenCone (nsides, rbot, rtop, depth, 0);
	 }
	return (1);
}

setSphere (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	float rad = -1.0;

	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "rad") == 0)
	    { if (!pdbSetFloat (&rad, p) || rad <= 0.0)
	       { errorMsg (0, "'rad' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (rad == -1.0)
	 { errorMsg (0, "'rad' not specified\n");
	   return (-1);
	 }
	obj->go = grf4GenSphere (12, rad, 0);
	return (1);
}

setPolygon (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	float *face = 0;
	int fnum;
	int nsides = -1;
	float size = -1.0;
	
	register PdbPar *p;
	int stat;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "size") == 0)
	    { if (face)
	       { errorMsg (0,
"'size' param cannot be used with 'face' param\n");
		 return (-1);
	       }
	      if (!pdbSetFloat (&size, p) || size <= 0.0)
	       { errorMsg (0, 
"'size' must be a positive number\n");
		 return (-1);
	       }
	    }
	   else if (strcmp (p->name, "nsides") == 0)
	    { if (face)
	       { errorMsg (0,
"'nsides' param cannot be used with 'face' param\n");
		 return (-1);
	       }
	      if (p->type != PDB_INTEGER || p->val.i < 3)
	       { errorMsg (0, 
"'nsides' must be an integer >= 3\n");
		 return (-1);
	       }
	      nsides = p->val.i;
	    }
	   else if (strcmp (p->name, "face") == 0)
	    { if (nsides >= 0 || size >= 0.0)
	       { errorMsg (0,
"'face' param cannot be used with 'nsides' or 'size' params\n");
		 return (-1);
	       }
	      if (!IS_DOUBLE_VECTOR(p) || 
		   p->val.set->len < 6 || (p->val.set->len%2))
	       { errorMsg (0, 
"'face' must be a vector with an even number of at least 6 elements\n");
		 return (-1);
	       }
	      fnum = p->val.set->len/2;
	      face = pdbBuildVecF(p->val.set);
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (face)
	 { obj->go = grf4GenPolygon (fnum, face);
	 }
	else if (nsides >= 0 && size >= 0.0)
	 { obj->go = grf4GenRegPolygon (nsides, size);
	 }
	else
	 { errorMsg (0,
"must specifiy either 'face' param or 'nsides' and 'size' params\n");
	   return (-1);
	 }
	return (1);
}

setLines (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	register PdbPar *p;
	int stat;
	int num;
	float *vtxs = 0;
	float *fp;

	for (p=p0; p; p=p->next)
	 { if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	   else if (strcmp (p->name, "vtxs") == 0)
	    { if (p->type == PDB_SET)
	       { PdbSet *set = p->val.set;
		 PdbPar *v;

		 vtxs = (float*)calloc(4*set->len, sizeof(float));
		 num = set->len;
		 for (v=set->list, fp=vtxs; v; v=v->next)
		  { if (IS_DOUBLE_VECTOR(v) && v->val.set->len == 4)
		     { pdbSetVecF (v->val.set, fp);
		       fp += 4;
		     }
		    else
		     { errorMsg (0, 
"Elements of 'vtxs' must be vectors of length 4\n");
		       return (-1);
		     }
		  }
	       }
	      else
	       { errorMsg (0, 
"'vtxs' must be a set of vectors of length 4\n");
		 return (-1);
	       }
	    }
	   else
	    { errorMsg (0, "Unknown param '%s'\n", p->name);
	      return (-1);
	    }
	 }
	if (!vtxs)
	 { errorMsg (0, "'vtxs' not specified\n");
	   return (-1);
	 }
	obj->go = grf4GenLineSet (num, vtxs, 0);
	free ((char*)vtxs);
	return (1);
}

setCompound (obj, p0)
ObjectDesc *obj;
PdbPar* p0;
{
	ObjectDesc *ref, *new, *sons_tail;
	register PdbPar *p;
	int stat;

	sons_tail = 0;
	for (p=p0; p; p=p->next)
	 { if (!p->name)
	    { if (p->type == PDB_STRING)
	       { if (ref = getObjectByName(p->val.s))
		  { new = copyObject (ref);
		  }
		 else
		  { errorMsg (0,
"Object '%s' not previously defined\n", p->val.s);
		    return (-1);
		  }
	       }
	      else if (p->type == PDB_SET)
	       { if ((new = setObject (p->val.set)) == 0)
		  { errorMsg (0,
"Error creating member of compound object\n");
		    return (-1);
		  }
	       }
	      else
	       { errorMsg (0,
"Compund object: sub-object must be a name string or a definition set\n");
		 return (-1);
	       }
	      if (!sons_tail)
	       { obj->sons = sons_tail = new;
	       }
	      else
	       { sons_tail->sibs = new;
		 sons_tail = new;
	       }
	    }
	   else if (stat = matchGenObjectParam(p, obj))
	    { if (stat < 0)
	       { return (-1);
	       }
	    }
	 }
	if (!sons_tail)
	 { errorMsg (0, 
"Compund object must contain at least one sub-element\n");
	   return (-1);
	 }
	return (1);
}

MaterialDesc *setMaterial(set)
PdbSet *set;
{
	PdbPar *p, *q;
	char *name = 0;
	MaterialDesc *mat;
	int k;

	mat = (MaterialDesc*)calloc(1,sizeof(MaterialDesc));

	for (p=set->list; p; p=p->next)
	 { if (!p->name)
	    { errorMsg (0,
"line %d: unamed material param\n", p->lineno);
	      return (0);
	    }
	   if (strcmp (p->name, "diffuse") == 0)
	    { if (p->type == PDB_INTEGER)
	       { mat->def.diffuse = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: material param 'diffuse' must be an integer\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "ambient") == 0)
	    { if (p->type == PDB_INTEGER)
	       { mat->def.ambient = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: material param 'ambient' must be an integer\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "emission") == 0)
	    { if (p->type == PDB_INTEGER)
	       { mat->def.emission = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: material param 'emission' must be an integer\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "specular") == 0)
	    { if (p->type == PDB_INTEGER)
	       { mat->def.specular = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: material param 'specular' must be an integer\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "shine") == 0)
	    { if (!pdbSetFloat (&mat->def.shine, p))
	       { errorMsg (0,
"line %d: material param 'shine' must be a number\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "alpha") == 0)
	    { if (!pdbSetFloat (&mat->def.alpha, p))
	       { errorMsg (0,
"line %d: material param 'alpha' must be a number\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "name") == 0)
	    { if (p->type != PDB_STRING)
	       { errorMsg (0,
"line %d: material param 'name' must be a string\n", p->lineno);
		 return (0);
	       }
	      name = p->val.s;
	    }
	   else
	    { errorMsg (0,
"line %d: unknown material param '%s'\n",p->lineno, p->name);
	      return (0);
	    }
	 }
	if (name)
	 { mat->name = copystr(name);
	 }
	addMaterial (mat);
	return (mat);
}

LightDesc *setLight(set)
PdbSet *set;
{
	PdbPar *p;
	LightDesc *light;
	float *v;
	int k;

	light = (LightDesc*)calloc(1,sizeof(LightDesc));

	if (lights)
	 { k = lights->num + 1;
	 }
	else
	 { k = 0;
	 }
	if (k >= MAX_LIGHTS)
	 { errorMsg (0,
"line %d: a maximum of %d lights can be defined\n", MAX_LIGHTS);
	   return (0);
	 }

	p=set->list;
	if (!IS_DOUBLE_VECTOR(p) || p->val.set->len != 3)
	 { errorMsg (0,
"line %d: 1st light param must a vector of three floats\n", p->lineno);
	   return (0);
	 }
	v = pdbBuildVecF(p->val.set);

	light->x = v[0];
	light->y = v[1];
	light->z = v[2];
	light->w = 0;

	free ((char*)v);

	for (p=p->next; p; p=p->next)
	 { if (!p->name)
	    { errorMsg (0,
"line %d: unamed light param\n", p->lineno);
	      return (0);
	    }
	   if (strcmp (p->name, "color") == 0)
	    { if (p->type == PDB_INTEGER)
	       { light->color = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: light param 'color' must be an integer\n");
		 return (0);
	       }
	    }
	   else if (strcmp (p->name, "ambient") == 0)
	    { if (p->type == PDB_INTEGER)
	       { light->ambient = p->val.i;
	       }
	      else
	       { errorMsg (0,
"line %d: light param 'ambient' must be an integer\n");
		 return (0);
	       }
	    }
	   else
	    { errorMsg (0,
"line %d: unknown light param '%s'\n",p->lineno, p->name);
	      return (0);
	    }
	 }
	light->num = k;
	addLight (light);
	return (light);
}

struct robotParams {
	TRSF *loc;
	LocFxnDesc *locfxn;
	int hasEndEffector;
	float *angles;
};

setRobot(set)
PdbSet *set;
{
	RobotDesc *rd;
	StaticRobotDesc *sd;
	PdbPar *p;
	
	struct robotParams rp;

	rp.loc = 0;
	rp.locfxn = 0;
	rp.hasEndEffector = 1;
	rp.angles = 0;

	p = set->list;
	if (p->type != PDB_STRING)
	 { errorMsg (0, 
"line %d: 1st robot param must be the robot's name\n", p->lineno);
	   return (-1);
	 }
	if (p->name)
	 { errorMsg (0,
"line %d: 1st robot param should be untagged\n", p->lineno);
	   return (-1);
	 }
	if (rd = getRobotByName(p->val.s))
	 { /* this identifies a running robot */
	   if (rd->configured)
	    { errorMsg (0,
"line %d: robot '%s' already specified\n", p->lineno, p->val.s);
	      return (-1);
	    }
	   for (p=p->next; p; p=p->next)
	    { if (matchRobotParam (p, &rp, rd->jls) <= 0)
	       { errorMsg (0,
"line %d: bad param for robot '%s'\n", p->lineno, rd->name);
		 return (-1);
	       }
	    }
	   rd->loc = rp.loc;
	   rd->locfxn = rp.locfxn;
	   rd->hasEndEffector = rp.hasEndEffector;
	   rd->ss.initAngles = rp.angles;
	   rd->configured = 1;

	   addNewDrawable (DRAW_ROBOT, (void*)rd);
	 }
	else if (sd = getStaticRobotByName(p->val.s))
	 { errorMsg (0,
"line %d: robot '%s' already specified\n", p->lineno, p->val.s);
	   return (-1);
	 }
	else
	 { /* create a new static robot */

	   char *colonp;
	   int varSize;

	   sd = (StaticRobotDesc*)calloc(1,sizeof(StaticRobotDesc));
	   sd->name = copystr(p->val.s);
	   sd->basename = copystr(p->val.s);
	   if (colonp = rindex (sd->basename, ':'))
	    { *colonp = '\0';
	    }
	   if ((sd->rbtID = find_robot(sd->basename)) == 0)
	    { errorMsg (0,
"line %d: can't find robot '%s' in RCI tables\n", p->lineno, sd->basename);
	      return (-1);
	    }
	   if ((sd->jls = getJls(sd->basename)) == NULL)
	    { errorMsg (0,
"line %d: can't get JLS structure for robot '%s'\n", p->lineno, sd->basename);
	      return (-1);
	    }
	   if ((sd->kyn = getKyn(sd->basename, 0)) == NULL)
	    { errorMsg (0,
"line %d: can't get kyn structure for robot '%s'\n", p->lineno, sd->basename);
	      return (-1);
	    }
	   sd->var = malloc(getVarSize(sd->basename));

	   for (p=p->next; p; p=p->next)
	    { if (matchRobotParam (p, &rp, sd->jls) <= 0)
	       { errorMsg (0,
"line %d: param for robot '%s'\n", p->lineno, p->val.s);
		 return (-1);
	       }
	    }
	   if ((sd->loc = rp.loc) == 0)
	    { sd->loc = ALLOC(TRSF);
	      identTrsf (sd->loc);
	    }
	   sd->locfxn = rp.locfxn;
	   sd->hasEndEffector = rp.hasEndEffector;
	   if ((sd->angles = rp.angles) == 0)
	    { int j;
	      sd->angles = (float*)calloc(sd->jls->numJ, sizeof(float));
	      for (j=0; j<sd->jls->numJ; j++)
	       { sd->angles[j] = sd->jls->parkAngles[j];
	       }
	    }
	   updateVar (sd->var, sd->angles, sd->kyn);
	   addStaticRobot (sd);
	   addNewDrawable (DRAW_STATIC_ROBOT, (void*)sd);
	 }
	return (1);
}

matchRobotParam (p, rp, jls)
PdbPar *p;
struct robotParams *rp;
JLS *jls;
{	
	float locvec[6];
	int k;

	if (!p->name || p->flags & PDB_CONSTRUCTED)
	 { errorMsg (0, "Unnamed or constructed param in list\n");
	   return (-1);
	 }
	else if (strcmp (p->name, "loc") == 0)
	 { TRSF loc;
	   if (setLocation (p, &loc) < 0)
	    { return (-1);
	    }
	   rp->loc = ALLOC(TRSF);
	   *rp->loc = loc;
	 }
	else if (strcmp (p->name, "locfxn") == 0)
	 { if (p->type == PDB_SET)
	    { if ((rp->locfxn = setLocfxn(p->val.set)) == 0)
	       { return (-1);
	       }
	    }
	   else
	    { errorMsg (0, "'locfxn' param should be a set\n");
	      return (-1);
	    }
	 }
	else if (strcmp (p->name, "drawEndEffector") == 0)
	 { if (p->type == PDB_INTEGER && (p->val.i == 1 || p->val.i == 0))
	    { rp->hasEndEffector = p->val.i;
	    }
	   else
	    { errorMsg (0, 
"'endEffector' must be set to either '0' or '1'\n");
	      return (-1);
	    }
	 }
	else if (strcmp (p->name, "angles") == 0)
	 { PdbSet *set;
	   PdbPar *par;
	   int k;

	   if (!IS_DOUBLE_VECTOR(p) || p->val.set->len > jls->numJ)
	    { errorMsg (0, 
"'angles' must be a vector of length <= %d\n", jls->numJ);
	      return (-1);
	    }
	   rp->angles = (float*)malloc(jls->numJ*sizeof(float));
	   for (k=0; k<jls->numJ; k++)
	    { rp->angles[k] = jls->parkAngles[k];
	    }
	   set = p->val.set;
	   for (k=0, par=set->list; k<set->len; k++, par=par->next)
	    { if (par->type == PDB_INTEGER)
	       { rp->angles[k] = DEGTORAD*par->val.i;
	       }
	      else if (par->type == PDB_DOUBLE)
	       { rp->angles[k] = DEGTORAD*par->val.d;
	       }
	    }
	 }
	else
	 { errorMsg (0, "Unknown param '%s'\n", p->name);
	   return (-1);
	 }
	return (1);
}

setLocation (par, trsf)
PdbPar *par;
TRSF *trsf;
{
	float roll, pitch, yaw;
	PdbPar *p;
	int rcode = 0;
	int k;

	identTrsf(trsf);
	if (!IS_DOUBLE_VECTOR(par))
	 { errorMsg (0, "location: must be a vector of floats\n");
	   return (-1);
	 }

	/* go through and parse the untagged part */

	roll = pitch = yaw = 0.0;

	p = par->val.set->list;
	for (k=0; k<par->val.set->len && k<6; k++, p=p->next)
	 { if (p->name)
	    { break;
	    }
	   if (p->type == PDB_NONE)
	    { continue;
	    }
	   else
	    { switch (k)
	       { case 0:
		  { pdbSetFloat (&trsf->p.x, p);
		    break;
		  }
		 case 1:
		  { pdbSetFloat (&trsf->p.y, p);
		    break;
		  }
		 case 2:
		  { pdbSetFloat (&trsf->p.z, p);
		    break;
		  }
		 case 3:
		  { pdbSetFloat (&roll, p);
		    break;
		  }
		 case 4:
		  { pdbSetFloat (&pitch, p);
		    break;
		  }
		 case 5:
		  { pdbSetFloat (&yaw, p);
		    break;
		  }
	       }
	    }
	 }
	if (roll != 0.0 || pitch != 0.0 || yaw != 0.0)
	 { rpyToTrsf (trsf, roll, pitch, yaw);
	 }

	/* now parse the untagged part */

	for ( ; k<par->val.set->len; k++, p=p->next)
	 { float val;

	   if (p->type == PDB_NONE)
	    { errorMsg (0,"location: null component in tagged section\n");
	      return (-1);
	    }
	   if (! p->name)
	    { errorMsg (0,"location: untagged component in tagged section\n");
	      return (-1);
	    }
	   if (! (p->flags & PDB_DECLARED) )
	    { errorMsg (0,"location: colon tagged component\n");
	      return (-1);
	    }
	   pdbSetFloat (&val, p);

	   if (strcmp (p->name, "tx") == 0)
	    { multTrsfXyz (trsf, val, 0.0, 0.0);
	    }
	   else if (strcmp (p->name, "ty") == 0)
	    { multTrsfXyz (trsf, 0.0, val, 0.0);
	    }
	   else if (strcmp (p->name, "tz") == 0)
	    { multTrsfXyz (trsf, 0.0, 0.0, val);
	    }
	   else if (strcmp (p->name, "rx") == 0)
	    { multTrsfRpy (trsf, 0.0, 0.0, val);
	    }
	   else if (strcmp (p->name, "ry") == 0)
	    { multTrsfRpy (trsf, 0.0, val, 0.0);
	    }
	   else if (strcmp (p->name, "rz") == 0)
	    { multTrsfRpy (trsf, val, 0.0, 0.0);
	    }
	   else 
	    { errorMsg (0,"location: illegal tag '%s'\n", p->name);
	      return (-1);
	    }
	 }
}
