// Author      : Josh Grant
// Course      : CIS5900, Computer Graphics, Fall 2000
// Assignment  : hw10
// Date        : November 14th, 2000
// Description : This program randomly casts rays out from a view point and  
//               checks for an intersection between x number of spheres
//               within a 3D scene.

#include <stdio.h>
#include <iostream.h>
#include <Sphere.h>
#include <ViewPoint.h>

#define RAYFILE "raycast.1"

int  checkArgs    (int myargc, char **myargv);
void printUsage   (char *progname);
void writeRayFile (FILE *fptr, Vector center, Vector color);
void writeInventor(Vector center, Vector color, double radius);

// I know globals are bad, but this was a quick way to clean up the mess
// caused by parsing through the command line.  Now the function is at the
// bottom of the page instead of right up top.
double vxcenter = 0.0, vycenter = 0.0, vzcenter = 5.0;
int    samples = 5000, sphere_num = 1;

int main(int argc, char **argv) {
  int i, j, intersects, *obj_index, hold;
  Sphere *objects;
  ViewPoint *view;
  Vector s_center, s_color, out_color, ray, q0, q1, normal, vcenter;
  double s_radius = 0.0, x, y, z, r, g, b, vlength;
  FILE *fptr;

  // if one of the command line arguments was not recognized, then we print
  // the usage and quit.
  if (!checkArgs(argc, argv))
    printUsage(argv[0]);
  
  // Set up some Vectors for the center of the viewing point and the user
  // sphere.
  vcenter = Vector(vxcenter, vycenter, vzcenter);
  //vcenter.normalize();
  
  view    = new ViewPoint(vcenter, 0.1);
  objects = new Sphere[sphere_num];
  obj_index = new int[sphere_num];

  cout << "#Inventor V2.1 ascii" << endl;
  cout << endl;
  cout << "Complexity {" << endl;
  cout << "  value         1.0" << endl;
  cout << "}" << endl;
  cout << endl;

  writeInventor(vcenter, Vector(0.5, 0.5, 0.5), 0.1);

  srand48(getpid());
  for (i = 0; i < sphere_num; i++) {
    obj_index[i] = i;
    x = 2.0 * (drand48() - 0.5);
    y = 2.0 * (drand48() - 0.5);
    z = 2.0 * (drand48() - 0.5);

    s_center = Vector(x, y, z);
    s_radius = 0.2;

    r = drand48();
    g = drand48();
    b = drand48();
    s_color  = Vector(r, g, b);

    objects[i] = Sphere(s_radius, s_center, s_color);
    writeInventor(s_center, s_color, s_radius);
  }

  for (i = sphere_num - 1; i >= 0; i--) {
    for (j = 0; j < i; j++) {
      if (objects[obj_index[j]].getCenter().length() >
	  objects[obj_index[j]].getCenter().length()) {
        hold = obj_index[j];
        obj_index[j] = obj_index[j+1];
        obj_index[j+1] = hold;
      }
    }
  }

  fptr = fopen(RAYFILE, "w");
  fprintf(fptr, "# RayCast File, First line is view point\n");
  fprintf(fptr, "#    x        y        z        r        g        b\n");
  fprintf(fptr, "%8.4f %8.4f %8.4f\n", vxcenter, vycenter, vzcenter);

  cout << "Complexity {" << endl;
  cout << "  value         0.2" << endl;
  cout << "}" << endl;
  for (i = 0; i < samples; i++) {
    if (view->castRay(ray)) {
      //cout << ray << endl;
      for (j = 0; j < sphere_num; j++) {
        intersects = view->intersect(ray, objects[obj_index[j]], q0, q1);
        if (intersects == 1) {
	  ray.normalize();
	  normal = q0 - objects[obj_index[j]].getCenter();
	  normal.normalize();
	  out_color = dot(normal, -ray) * objects[obj_index[j]].getColor();
	  writeRayFile(fptr, q0, out_color);
	  writeInventor(q0, out_color, 0.05);
	  break;
        }
      }
    }
  }
  fclose(fptr);

  return 0;
}

void writeRayFile (FILE *fptr, Vector center, Vector color) {
  fprintf(fptr, "%8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
	  center.x(), center.y(), center.z(),
	  color.x(),  color.y(),  color.z());
}

void writeInventor (Vector center, Vector color, double radius) {
  cout << "Separator {" << endl;
  cout << "  Material {" << endl;
  cout << "    emissiveColor "
       << color.x() << " " 
       << color.y() << " " 
       << color.z() << endl;
  cout << "  }" << endl;
  cout << "  Transform {" << endl;
  cout << "    translation   ";
  cout << center.x() << "   " << center.y() << "   " << center.z();
  cout << endl;
  cout << "  }" << endl;
  cout << "  Sphere {" << endl;
  cout << "    radius        " << radius << endl;
  cout << "  }" << endl;
  cout << "}" << endl << endl;
}

// checkArgs() checks the command line arguments and assigns all variables
// not listed on command to zero.
int checkArgs(int myargc, char **myargv) {
  for (int i = 1; i < myargc; i += 2) {
    if (!strcmp(myargv[i], "vxview"))
      vxcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "vyview"))
      vycenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "vzview"))
      vzcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "spheres"))
      sphere_num = atoi(myargv[i + 1]);
    else if (!strcmp(myargv[i], "samples"))
      samples = atoi(myargv[i + 1]);
    else if (!strcmp(myargv[i], "-help"))
      return 0;
  }
  return 1;
}

void printUsage(char *progname) {
  cout << "Usage - " << endl;
  exit(1);
}