#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "../../tools/vector/linear.h"
#include "../../tools/ppm/ppm.h"
#include "types.h"
#include "object.h"
#include "../../tools/list/list.h"

/* Boolean values */
#define FALSE 0
#define TRUE 1

/* The zero vector */
#define ZERO { 0.0, 0.0, 0.0 }

#define PI 3.141592653589
#define THETASTEPS 100
#define PHISTEPS 


/*      *
 * Data *
 *      */


char objnames[OBJECTTYPES][8]=
{
  "spheres", "walls", "tubes", "pipes", "cones"
};


primitive spaceshipbody;
primitive arrowstick;
primitive arrowhead;
primitive arrowball;
primitive lightsourceprim;
primitive wallprim;

struct objdescelem arrowballselems[2]=
{
  {
    TRUE,
    &arrowball,
    { 0.0, 30.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  },
  {
    TRUE,
    &arrowball,
    { 0.0, -30.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  }
};

struct objdesc arrowballs=
{
  2,
  arrowballselems
};

struct objdescelem arrowelems[3]=
{
  {
    TRUE,
    &arrowstick,
    { 0.0, 0.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  },
  {
    TRUE,
    &arrowhead,
    { 50.0, 0.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, -1.0 }
  },
  {
    FALSE,
    &arrowballs,
    { 60.0, 0.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  }
};

struct objdesc arrow=
{
  3,
  arrowelems
};

struct objdescelem spaceshipelems[3]=
{
  {
    TRUE,
    &spaceshipbody,
    { 0.0, 0.0, 0.0 },
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  },
  {
    FALSE,
    &arrow,
    { 0.0, -45.0, 0.0},
    { 0.0, -1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  },
  {
    FALSE,
    &arrow,
    { 0.0, 45.0, 0.0 }, 
    { 0.0, 0.0, 1.0 }, { 0.0, -1.0, 0.0 }
  }
};

struct objdesc spaceship=
{
  3,
  spaceshipelems
};

struct objdescelem lightsourceelems[1]=
{
  {
    TRUE,
    &lightsourceprim,
    { 0.0, 0.0, 0.0 }, 
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  }
};

struct objdesc lightsource=
{
  1,
  lightsourceelems
};

struct objdescelem wallelems[2]=
{
  {
    TRUE,
    &wallprim,
    { 0.0, 0.0, 0.0 }, 
    { 0.0, 0.0, 1.0 }, { 0.0, -1.0, 0.0 }
  },
  {
    TRUE,
    &wallprim,
    { 0.0, 0.0, 0.0 }, 
    { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
  }
};

struct objdesc mywall=
{
  2,
  wallelems
};


/* Object instances */

struct obj light1=
{
  &lightsource,
  { 0.0, 0.0, 0.0 },
  { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
};

struct obj spaceship1=
{
  &spaceship,
  { 0.0, 480.0, 2.0 },
  { 0.0, 1.0, 0.3 }, { 0.0, 0.0, 1.0 }
};

struct obj spaceship2=
{
  &spaceship,
  { 0.0, 480.0, 20.0 },
  { 0.0, 1.0, 1.0 }, { 0.0, -1.0, 1.0 }
};

struct obj wall1=
{
  &mywall,
  { 0.0, 600.0, -50.0 },
  { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }
};



/*           *
 * Functions *
 *           */


/* Unused function, but it must be defined! (see linear.c) */
void drawline(int px, int py, int qx, int qy)
{
}

light lightassign(vector dir, vector color)
{
  light li;
  
  li.dir=dir;
  li.col=color;

  return li;
}

sphere sphereassign(vector c, scalar r, vector color, 
		    scalar amb, scalar diff, scalar fspre, scalar refl)
{
  sphere sph;

  sph.c=c;
  sph.r=r;
  sph.oi.col[0]=color;
  sph.oi.amb=amb;
  sph.oi.diff=diff;
  sph.oi.fspre=fspre;
  sph.oi.refl=refl;

  return sph;
}

wall wallassign(vector pl_n, vector pl_p, vector color,
		scalar amb, scalar diff, scalar fspre, scalar refl)
{
  wall wa;
  
  wa.pl.n=pl_n;
  wa.pl.p=pl_p;
  wa.oi.col[0]=color;
  wa.oi.amb=amb;
  wa.oi.diff=diff;
  wa.oi.fspre=fspre;
  wa.oi.refl=refl;

  return wa;
}

tube tubeassign(vector li_k, vector li_p, scalar r, 
		vector color0, vector color1, vector color2,
		scalar amb, scalar diff, scalar fspre, scalar refl)
{
  tube tu;

  tu.li.k=li_k;
  tu.li.p=li_p;
  tu.r=r;
  tu.oi.col[0]=color0;
  tu.oi.col[1]=color1;
  tu.oi.col[2]=color2;
  tu.oi.amb=amb;
  tu.oi.diff=diff;
  tu.oi.fspre=fspre;
  tu.oi.refl=refl;

  return tu;
}

pipe pipeassign(vector li_k, vector li_p, scalar r, vector color,
		scalar amb, scalar diff, scalar fspre, scalar refl)
{
  pipe pi;

  pi.li.k=li_k;
  pi.li.p=li_p;
  pi.r=r;
  pi.oi.col[0]=color;
  pi.oi.amb=amb;
  pi.oi.diff=diff;
  pi.oi.fspre=fspre;
  pi.oi.refl=refl;

  return pi;
}

cone coneassign(vector li_k, vector li_p, scalar r, 
		vector color0, vector color1,
		scalar amb, scalar diff, scalar fspre, scalar refl)
{
  cone co;

  co.li.k=li_k;
  co.li.p=li_p;
  co.r=r;
  co.oi.col[0]=color0;
  co.oi.col[1]=color1;
  co.oi.amb=amb;
  co.oi.diff=diff;
  co.oi.fspre=fspre;
  co.oi.refl=refl;
  
  return co;
}

void writeprimitive(primitive prim, FILE *fh)
{
  light li;
  sphere sph;
  wall wa;
  tube tu;
  pipe pi;
  cone co;
  
  switch(prim.type)
  {
  case LIGHT:
    li=prim.primitiveu.li;
    fprintf(fh, "\tdirection\t%.4lf %.4lf %.4lf\n",
	    li.dir.x, li.dir.y, li.dir.z);
    fprintf(fh, "\tcolor\t\t%.4lf %.4lf %.4lf\n\n",
	    li.col.x, li.col.y, li.col.z);
    break;
    
  case SPHERE:
    sph=prim.primitiveu.sph;
    fprintf(fh, "\tpos\t\t%.4lf %.4lf %.4lf\n",
	    sph.c.x, sph.c.y, sph.c.z);
    fprintf(fh, "\tradius\t\t%.4lf\n", sph.r);
    fprintf(fh, "\tcolor0\t\t%.4lf %.4lf %.4lf\n", 
	    sph.oi.col[0].x,
	    sph.oi.col[0].y,
	    sph.oi.col[0].z);
    fprintf(fh, "\tambient\t\t%.4lf\n", sph.oi.amb);
    fprintf(fh, "\tdiffusion\t%.4lf\n", sph.oi.diff);
    fprintf(fh, "\tfracspref\t%.4lf\n", sph.oi.fspre);
    fprintf(fh, "\tsprefexp\t%.4lf\n\n", sph.oi.refl);
    break;

  case WALL:
    wa=prim.primitiveu.wa;
    fprintf(fh, "\tplane\t\t%.4lf %.4lf %.4lf, %.4lf %.4lf %.4lf\n",
	    wa.pl.n.x, wa.pl.n.y, wa.pl.n.z,
	    wa.pl.p.x, wa.pl.p.y, wa.pl.p.z);
    fprintf(fh, "\tcolor0\t\t%.4lf %.4lf %.4lf\n", 
	    wa.oi.col[0].x,
	    wa.oi.col[0].y,
	    wa.oi.col[0].z);
    fprintf(fh, "\tambient\t\t%.4lf\n", wa.oi.amb);
    fprintf(fh, "\tdiffusion\t%.4lf\n", wa.oi.diff);
    fprintf(fh, "\tfracspref\t%.4lf\n", wa.oi.fspre);
    fprintf(fh, "\tsprefexp\t%.4lf\n\n", wa.oi.refl);
    break;

  case TUBE:
    tu=prim.primitiveu.tu;
    fprintf(fh, "\tline\t\t%.4lf %.4lf %.4lf, %.4lf %.4lf %.4lf\n", 
	    tu.li.k.x, tu.li.k.y, tu.li.k.z,
	    tu.li.p.x, tu.li.p.y, tu.li.p.z);
    fprintf(fh, "\tradius\t\t%.4lf\n", tu.r);
    fprintf(fh, "\tcolor0\t\t%.4lf %.4lf %.4lf\n", 
	    tu.oi.col[0].x,
	    tu.oi.col[0].y,
	    tu.oi.col[0].z);
    fprintf(fh, "\tcolor1\t\t%.4lf %.4lf %.4lf\n", 
	    tu.oi.col[1].x,
	    tu.oi.col[1].y,
	    tu.oi.col[1].z);
    fprintf(fh, "\tcolor2\t\t%.4lf %.4lf %.4lf\n", 
	    tu.oi.col[2].x,
	    tu.oi.col[2].y,
	    tu.oi.col[2].z);
    fprintf(fh, "\tambient\t\t%.4lf\n", tu.oi.amb);
    fprintf(fh, "\tdiffusion\t%.4lf\n", tu.oi.diff);
    fprintf(fh, "\tfracspref\t%.4lf\n", tu.oi.fspre);
    fprintf(fh, "\tsprefexp\t%.4lf\n\n", tu.oi.refl);
    break;

  case PIPE:
    pi=prim.primitiveu.pi;
    fprintf(fh, "\tline\t\t%.4lf %.4lf %.4lf, %.4lf %.4lf %.4lf\n", 
	    pi.li.k.x, pi.li.k.y, pi.li.k.z,
	    pi.li.p.x, pi.li.p.y, pi.li.p.z);
    fprintf(fh, "\tradius\t\t%.4lf\n", pi.r);
    fprintf(fh, "\tcolor0\t\t%.4lf %.4lf %.4lf\n", 
	    pi.oi.col[0].x,
	    pi.oi.col[0].y,
	    pi.oi.col[0].z);
    fprintf(fh, "\tambient\t\t%.4lf\n", pi.oi.amb);
    fprintf(fh, "\tdiffusion\t%.4lf\n", pi.oi.diff);
    fprintf(fh, "\tfracspref\t%.4lf\n", pi.oi.fspre);
    fprintf(fh, "\tsprefexp\t%.4lf\n\n", pi.oi.refl);
    break;

  case CONE:
    co=prim.primitiveu.co;
    fprintf(fh, "\tline\t\t%.4lf %.4lf %.4lf, %.4lf %.4lf %.4lf\n", 
	    co.li.k.x, co.li.k.y, co.li.k.z,
	    co.li.p.x, co.li.p.y, co.li.p.z);
    fprintf(fh, "\tradius\t\t%.4lf\n", co.r);
    fprintf(fh, "\tcolor0\t\t%.4lf %.4lf %.4lf\n", 
	    co.oi.col[0].x,
	    co.oi.col[0].y,
	    co.oi.col[0].z);
    fprintf(fh, "\tcolor1\t\t%.4lf %.4lf %.4lf\n", 
	    co.oi.col[1].x,
	    co.oi.col[1].y,
	    co.oi.col[1].z);
    fprintf(fh, "\tambient\t\t%.4lf\n", co.oi.amb);
    fprintf(fh, "\tdiffusion\t%.4lf\n", co.oi.diff);
    fprintf(fh, "\tfracspref\t%.4lf\n", co.oi.fspre);
    fprintf(fh, "\tsprefexp\t%.4lf\n\n", co.oi.refl);
    break;

  default:
    fprintf(stderr, "Internal error in writeprimitive()\n");
  }
}


void put_in_list(primitive *pp, vector pos, vector ydir, vector zdir, 
		 list *lp)
{
  elem e;
  vector primpos;
  
  e.prim=*pp;

  switch(e.prim.type)
    {
    case LIGHT:
      break;
      
    case SPHERE:
      primpos=vrotate(e.prim.primitiveu.sph.c, ydir, zdir);
      e.prim.primitiveu.sph.c = vadd(primpos, pos);
      break;
      
    case WALL:
      primpos=vrotate(e.prim.primitiveu.wa.pl.p, ydir, zdir);
      e.prim.primitiveu.wa.pl.p = vadd(primpos, pos);
      e.prim.primitiveu.wa.pl.n = vrotate(e.prim.primitiveu.wa.pl.n, ydir, zdir);
      break;
      
    case TUBE:
      primpos=vrotate(e.prim.primitiveu.tu.li.p, ydir, zdir);
      e.prim.primitiveu.tu.li.p = vadd(primpos, pos);
      e.prim.primitiveu.tu.li.k = vrotate(e.prim.primitiveu.tu.li.k, ydir, zdir);
      break;
      
    case PIPE:
      primpos=vrotate(e.prim.primitiveu.pi.li.p, ydir, zdir);
      e.prim.primitiveu.pi.li.p = vadd(primpos, pos);
      e.prim.primitiveu.pi.li.k = vrotate(e.prim.primitiveu.pi.li.k, ydir, zdir);
      break;
      
    case CONE:
      primpos=vrotate(e.prim.primitiveu.co.li.p, ydir, zdir);
      e.prim.primitiveu.co.li.p = vadd(primpos, pos);
      e.prim.primitiveu.co.li.k = vrotate(e.prim.primitiveu.co.li.k, ydir, zdir);
      break;
    }
  
  *lp=l_insert(e, NIL, *lp);
}

int traverse_tree(int objecttype, struct objdesc *odp, 
		  vector pos, vector ydir, vector zdir, list *lp)
{
  int i, nprimitives=0;
  vector subobjpos;
  
  for (i=0; i<odp->nsubobjs; i++)
    {
      subobjpos=vrotate(odp->objdescelems[i].pos, ydir, zdir);
      
      if (odp->objdescelems[i].isprimitive)
	{
	  if (odp->objdescelems[i].ptr.primitiveptr->type == objecttype)
	    {
	      put_in_list(odp->objdescelems[i].ptr.primitiveptr,
			  vadd(subobjpos, pos),
			  vrotate(odp->objdescelems[i].ydir, ydir, zdir),
			  vrotate(odp->objdescelems[i].zdir, ydir, zdir),
			  lp);
	      nprimitives++;
	    }
	}
      else
	nprimitives += traverse_tree(objecttype, 
				     odp->objdescelems[i].ptr.objptr,
				     vadd(subobjpos, pos),
				     vrotate(odp->objdescelems[i].ydir, 
					     ydir, zdir),
				     vrotate(odp->objdescelems[i].zdir, 
					     ydir, zdir), lp);
    }

  return nprimitives;
}


void writetofile(list l, int *nprimitives, char *filename)
{
  int cnt=0, current_type=0;
  FILE *fh;
  pos p;
  elem e;
  int i;

  /*  for (i=0; i<OBJECTTYPES;i++)
    printf("nprimitives[%d]=%d\n",i,nprimitives[i]);*/

  if (fh=fopen(filename, "w"))
    {
      fprintf(fh, "globalambient\t%.4lf\n\n",0.2);
      fprintf(fh, "lights\t1\n");
      writeprimitive(lightsourceprim, fh);

      while (nprimitives[current_type]==0) current_type++;
      
      fprintf(fh, "%s\t%d\n", objnames[current_type], 
	      nprimitives[current_type]);

      p=l_first(l);
      while(p)
	{
	  e=l_value(p, l);
	  writeprimitive(e.prim, fh);
	  p = l_next(p, l);

	  cnt++;
	  if (cnt == nprimitives[current_type])
	    {
	      current_type++;
	      while (nprimitives[current_type]==0) current_type++;
	      if (current_type < OBJECTTYPES)
		fprintf(fh, "%s\t%d\n", objnames[current_type], 
			nprimitives[current_type]);
	      cnt=0;
	    }	  
	}
      fclose(fh);
    }
  else printf("Fel!\n");
}

vector xyanglerotate(vector v, scalar theta)
{
  vector result;
  
  result.x = v.x*cos(theta) - v.y*sin(theta);
  result.y = v.x*sin(theta) + v.y*cos(theta);

  return result;
}

vector yzanglerotate(vector v, scalar theta)
{
  vector result;
  
  result.y = v.y*cos(theta) - v.z*sin(theta);
  result.z = v.y*sin(theta) + v.z*cos(theta);  
  
  return result;
}

vector zxanglerotate(vector v, scalar theta)
{
  vector result;
  
  result.z = v.z*cos(theta) - v.x*sin(theta);
  result.x = v.z*sin(theta) + v.x*cos(theta);  

  return result;
}

void doit(char *filename)
{
  FILE *fh;
  int nprimitives[OBJECTTYPES], objecttype;
  list primitivelist;
  pos p;

  primitivelist=l_empty();
  
  for (objecttype=0; objecttype<OBJECTTYPES; objecttype++)
    {
      nprimitives[objecttype] = 
	traverse_tree(objecttype, wall1.objdescptr, 
		      wall1.pos, wall1.ydir, wall1.zdir,
		      &primitivelist);

      nprimitives[objecttype] += 
	traverse_tree(objecttype,  spaceship1.objdescptr, 
		      spaceship1.pos, spaceship1.ydir, spaceship1.zdir,
		      &primitivelist);

      
      /*
      nprimitives[objecttype] += 
	traverse_tree(objecttype, spaceship2.objdescptr, 
		      spaceship2.pos, spaceship2.ydir, spaceship2.zdir,
		      &primitivelist);*/
    }
  
  /*  l_show(primitivelist);*/

  writetofile(primitivelist, nprimitives, filename);
  
  p=l_first(primitivelist);
  while(p)
    {
      primitivelist=l_remove(p, primitivelist);
      p=l_next(p, primitivelist);
    }
}


void main()
{
  char filename[80];
  scalar theta;
  int i;

  lightsourceprim.type=LIGHT;
  lightsourceprim.primitiveu.li=lightassign(vassign(0.8, -0.6, 1.0),
					vassign(1.0, 1.0, 1.0));

  arrowstick.type=PIPE;
  arrowstick.primitiveu.pi=pipeassign(vassign(50.0, 0.0, 0.0), 
				      vassign(0.0, 0.0, 0.0), 
				      4.999,
				      vassign(0.2, 0.2, 0.8),
				      1.0,
				      0.7,
				      0.5,
				      10.0);

  arrowhead.type=CONE;
  arrowhead.primitiveu.co=coneassign(vassign(30.0, 0.0, 0.0), 
				     vassign(-30.0, 0.0, 0.0), 
				     20.0,
				     vassign(0.9, 0.0, 0.0),
				     vassign(0.9, 0.9, 0.9),
				     1.0,
				     0.7,
				     0.6,
				     10.0);
  arrowball.type=SPHERE;
  arrowball.primitiveu.sph=sphereassign(vassign(0.0, 0.0, 0.0), 
					10.0,
					vassign(0.9, 0.9, 0.9),
					1.0,
					0.7,
					0.6,
					10.0);
  
  spaceshipbody.type=TUBE;
  spaceshipbody.primitiveu.tu=tubeassign(vassign(0.0, 0.0, 10.0), 
				     vassign(0.0, 0.0, -5.0), 
				     50.0,
				     vassign(0.2, 0.6, 0.0),
				     vassign(0.2, 0.6, 0.0),
				     vassign(0.2, 0.6, 0.0),
				     1.0,
				     0.7,
				     0.5,
				     10.0);

  wallprim.type=WALL;
  wallprim.primitiveu.wa=wallassign(vassign(0.0, 0.0, 1.0), 
				     vassign(0.0, 0.0, 0.0), 
				     vassign(0.7, 0.7, 0.8),
				     1.0,
				     0.5,
				     0.3,
				     5.0);

  for (i=0,theta=0.0; theta<2.0*PI-0.00001; theta+=2.0*PI/THETASTEPS)
    {
      spaceship1.ydir=xyanglerotate(vassign(0.0, 1.0, 0.3), theta);
      spaceship1.zdir=xyanglerotate(vassign(0.0, 0.0, 1.0), theta);
      arrowelems[2].ydir=yzanglerotate(vassign(0.0, 1.0, 0.0), 3.0*theta);
      arrowelems[2].zdir=yzanglerotate(vassign(0.0, 0.0, 1.0), 3.0*theta);
      
      sprintf(filename, "movie/ecp%d.ecp", i++);
      
      doit(filename);
    }  
}

