/*
15-462 Computer Graphics Spring 2003
Assignment 7: Ray Tracer starter code
Original by Michael Henson, Fall 2002
Modified by Sriram Vaidhyanathan, Spring 2003
*/

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include <pic.h>

#define MAX_TRIANGLES 10
#define MAX_SPHERES 10
#define MAX_LIGHTS 10

char *filename=0;

/* different display modes */
#define MODE_DISPLAY 1
#define MODE_JPEG 2
int mode=MODE_DISPLAY;

/* While debugging, make these smaller */
#define WIDTH 640
#define HEIGHT 480

unsigned char buffer[HEIGHT*WIDTH*3];

/* Vertex properties */
struct Vertex
{
  float position[3];
  float color_diffuse[3];
  float color_specular[3];
  float normal[3];
  float shininess;
  float s,t;
};

/* Triangle definiton */
typedef struct _Triangle
{
  char texture_name[40];
  struct Vertex v[3];
} Triangle;

/* Sphere definition */
typedef struct _Sphere
{
  char texture_name[40];
  float position[3];
  float color_diffuse[3];
  float color_specular[3];
  float shininess;
  float radius;
} Sphere;

/* Light definition */
typedef struct _Light
{
  float position[3];
  float color[3];
} Light;

/* Globals */
Triangle triangles[MAX_TRIANGLES];
Sphere spheres[MAX_SPHERES];
Light lights[MAX_LIGHTS];
float ambient_light[3];

int num_triangles=0;
int num_spheres=0;
int num_lights=0;

void plot_pixel_display(int x,int y,unsigned char r,unsigned char g,unsigned char b);
void plot_pixel_jpeg(int x,int y,unsigned char r,unsigned char g,unsigned char b);
void plot_pixel(int x,int y,unsigned char r,unsigned char g,unsigned char b);

/* 
   This is where your scene will get drawn. 
   Modify this function. 
*/
void draw_scene()
{
  unsigned int x,y;
  //simple output
  for(x=0;x < WIDTH;x++)
    for(y=0;y < HEIGHT;y++)
      plot_pixel(x,y,x%256,y%256,(x+y)%256);

}

/* Write out pixels to display window */
void plot_pixel_display(int x,int y,unsigned char r,unsigned char g,unsigned char b)
{
  glBegin(GL_POINTS);
  glColor3f(((float)r)/256.f,((float)g)/256.f,((float)b)/256.f);
  glVertex2i(x,y);
  glEnd();
  glFlush();
}

/* Write out pixels to jpeg buffer */
void plot_pixel_jpeg(int x,int y,unsigned char r,unsigned char g,unsigned char b)
{
  buffer[(HEIGHT-y-1)*3*WIDTH + x*3]=r;
  buffer[(HEIGHT-y-1)*3*WIDTH + x*3 + 1]=g;
  buffer[(HEIGHT-y-1)*3*WIDTH + x*3 + 2]=b;
}

void plot_pixel(int x,int y,unsigned char r,unsigned char g, unsigned char b)
{
  plot_pixel_display(x,y,r,g,b);
  if(mode == MODE_JPEG)
    plot_pixel_jpeg(x,y,r,g,b);
}

/* Write jpeg buffer to file */
void save_jpg()
{
  Pic *in = NULL;

  in = pic_alloc(640, 480, 3, NULL);
  printf("Saving JPEG file: %s\n", filename);

  memcpy(in->pix,buffer,3*WIDTH*HEIGHT);
  if (jpeg_write(filename, in))
    printf("File saved Successfully\n");
  else
    printf("Error in Saving\n");

  pic_free(in);      

}

/****************************************************/
/* Parsing Functions for Scene description format   */
/****************************************************/

void parse_check(char *expected,char *found)
{
  if(strcasecmp(expected,found))
    {
      char error[100];
      printf("Expected '%s ' found '%s '\n",expected,found);
      printf("Parse error, abnormal abortion\n");
      exit(0);
    }

}

void parse_floats(FILE*file,char *check,float p[3])
{
  char str[100];
  fscanf(file,"%s",str);
  parse_check(check,str);
  fscanf(file,"%f %f %f",&p[0],&p[1],&p[2]);
  printf("%s %f %f %f\n",check,p[0],p[1],p[2]);
}

void parse_tex(FILE*file,char name[40])
{
  char tex[100];
  fscanf(file,"%s",tex);
  parse_check("tex:",tex);
  fscanf(file,"%s",name);
  printf("tex: %s\n",name);
}

void parse_rad(FILE*file,float *r)
{
  char str[100];
  fscanf(file,"%s",str);
  parse_check("rad:",str);
  fscanf(file,"%f",r);
  printf("rad: %f\n",*r);
}

void parse_tcd(FILE*file,float *s,float *t)
{

  char str[100];
  fscanf(file,"%s",str);
  parse_check("tcd:",str);
  fscanf(file,"%f %f",s,t);
  printf("tcd: %f %f\n",*s,*t);

}

void parse_shi(FILE*file,float *shi)
{
  char s[100];
  fscanf(file,"%s",s);
  parse_check("shi:",s);
  fscanf(file,"%f",shi);
  printf("shi: %f\n",*shi);
}

int loadScene(char *argv)
{
  FILE *file = fopen(argv,"r");
  int number_of_objects;
  char type[50];
  int i;
  Triangle t;
  Sphere s;
  Light l;
  fscanf(file,"%i",&number_of_objects);

  printf("number of objects: %i\n",number_of_objects);
  char str[200];

  parse_floats(file,"amb:",ambient_light);

  for(i=0;i < number_of_objects;i++)
    {
      fscanf(file,"%s\n",type);
      printf("%s\n",type);
      if(strcasecmp(type,"triangle")==0)
	{

	  printf("found triangle\n");
	  parse_tex(file,t.texture_name);
	  int j;

	  for(j=0;j < 3;j++)
	    {
	      parse_floats(file,"pos:",t.v[j].position);
	      parse_floats(file,"dif:",t.v[j].color_diffuse);
	      parse_floats(file,"spe:",t.v[j].color_specular);
	      parse_floats(file,"nor:",t.v[j].normal);
	      parse_shi(file,&t.v[j].shininess);
	      parse_tcd(file,&t.v[j].s,&t.v[j].t);
	    }


	  if(num_triangles == MAX_TRIANGLES)
	    {
	      printf("too many triangles, you should increase MAX_TRIANGLES!\n");
	      exit(0);
	    }
	  triangles[num_triangles++] = t;
	}
      else if(strcasecmp(type,"sphere")==0)
	{
	  printf("found sphere\n");

	  parse_tex(file,s.texture_name);
	  parse_floats(file,"pos:",s.position);
	  parse_floats(file,"dif:",s.color_diffuse);
	  parse_floats(file,"spe:",s.color_specular);
	  parse_shi(file,&s.shininess);
	  parse_rad(file,&s.radius);


	  if(num_spheres == MAX_SPHERES)
	    {
	      printf("too many spheres, you should increase MAX_SPHERES!\n");
	      exit(0);
	    }
	  spheres[num_spheres++] = s;
	}
      else if(strcasecmp(type,"light")==0)
	{
	  printf("found light\n");
	  parse_floats(file,"pos:",l.position);
	  parse_floats(file,"col:",l.color);

	  if(num_lights == MAX_LIGHTS)
	    {
	      printf("too many lights, you should increase MAX_LIGHTS!\n");
	      exit(0);
	    }
	  lights[num_lights++] = l;
	}
      else
	{
	  printf("unknown type in scene description:\n%s\n",type);
	  exit(0);
	}
    }
  return 0;
}

/****************************************************/

void display()
{

}

void init()
{
  glMatrixMode(GL_PROJECTION);
  glOrtho(0,WIDTH,0,HEIGHT,1,-1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glClearColor(0,0,0,0);
  glClear(GL_COLOR_BUFFER_BIT);
}

void idle()
{
  /* Make sure this gets drawn only once */
  static int once=0;
  if(!once)
    {
      /* modify this to turn off the display */
      draw_scene();
      if(mode == MODE_JPEG)
	save_jpg();
    }
  once=1;
}

int main (int argc, char ** argv)
{

  if (argc<2 || argc > 3)
    {  
      printf ("usage: %s <scenefile> [jpegname]\n", argv[0]);
      exit(0);
    }
  if(argc == 3)
    {
      mode = MODE_JPEG;
      filename = argv[2];
    }
  else if(argc == 2)
    mode = MODE_DISPLAY;

  glutInit(&argc,argv);
  loadScene(argv[1]);

  glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);
  glutInitWindowPosition(0,0);
  glutInitWindowSize(WIDTH,HEIGHT);
  int window = glutCreateWindow("Assignment 7: Ray Tracer");
  glutDisplayFunc(display);
  glutIdleFunc(idle);
  init();
  draw_scene();
  glutMainLoop();

}
