// Author      : Josh Grant
// Course      : CIS5900, Computer Graphics, Fall 2000
// Assignment  : hw05
// Date        : October 10th, 2000
// Description : This program samples random directions around a 3D viewpoint
//               (vx, vy, vz) supplied by the command line.  The scene is
//               written out in Inventor format.  The final scene has several
//               spheres sampled on the viewing sphere which coincide with
//               spheres on the user sphere.  The spheres match up because
//               they because when the viewing sphere casts a ray it
//               intersects at the point marked on the user sphere. The
//               below arguments can be optionally changed when exectued at the
//               command line...
//                  vxcenter   - x center coordinate of viewing sphere
//                  vycenter   - y center coordinate of viewing sphere
//                  vzcenter   - z center coordinate of viewing sphere
//                  uxcenter   - x center coordinate of user sphere
//                  uxcenter   - y center coordinate of user sphere
//                  uxcenter   - z center coordinate of user sphere
//                  vradius    - radius of the viewing sphere
//                  uradius    - radius of the user sphere
//                  samples   - number of samples to attempt.
//               A -help option is also available to print the usage of the
//               program. 

#include <iostream.h>
#include <stdlib.h>
#include <unistd.h>
#include <sphere.h>
#include <vector.h>

int pointOnSphere(double &x, double &y, double &z);
int checkArgs(int myargc, char **myargv);
void printUsage(char *progname);
void writeInventorHeader();

// 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.75, vycenter = -0.75, vzcenter = -0.75;
double uxcenter = 0.75, uycenter = 0.75, uzcenter = 0.75;
double vradius = 0.25, uradius = 0.75;
double lradius = 0.05;
int    samples = 5000;

int main(int argc, char **argv) {
  int i, intersects;
  double x = 0.0, y = 0.0, z = 0.0;
  Sphere *view, *user, *little;
  Vector *dp, *vcenter, *ucenter, q0, q1;
  double lcolor[3], vcolor[3];

  lcolor[0] = 0.0; lcolor[1] = 0.0, lcolor[2] = 1.0;
  vcolor[0] = 1.0; vcolor[1] = 0.0, vcolor[2] = 0.0;

  // 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 = new Vector(vxcenter, vycenter, vzcenter);
  ucenter = new Vector(uxcenter, uycenter, uzcenter);

  *ucenter = *ucenter - *vcenter;

  // Write Inventor Header info
  writeInventorHeader();

  // Write the viewing sphere
  view = new Sphere(vradius, *vcenter);
  view->writeInventor();

  // Write the user sphere
  user = new Sphere(uradius, *ucenter);
  user->writeInventor();

  // Set up the object for all the sampled spheres
  little = new Sphere();
  little->radius = lradius;
  little->diffuse = lcolor;

  srand48(getpid());
  for (i = 0; i < samples; i++) {
    if (pointOnSphere(x, y, z)) {
      //Create Vector for the point on the viewing sphere
      dp = new Vector(x*vradius, y*vradius, z*vradius);
      
      // Does the ray cast from the center of the viewing sphere, to the
      // point dp and beyond, intersect any point on the user sphere?
      intersects = view->intersect(*dp, *ucenter, uradius, q0);

      if (intersects == 1) {
	*(little->center) = *dp + *vcenter;
	little->diffuse = vcolor;
	little->radius = lradius * 0.5;
	little->writeInventor();

	little->diffuse = lcolor;
	little->radius  = lradius;
	*(little->center) = q0;
	little->writeInventor();
      } 

      delete dp;
    }
  }

  return 0;
}

// Function first looks for a point within the unit sphere by randomly
// picking a value for x, y, z and then scaling it between -1 and 1.  If one
// is found then it is normalized.
int pointOnSphere(double &x, double &y, double &z) {
  float length;
  int maxAttempts = 10;

  do {
    x = 2.0 * (drand48() - 0.5);
    y = 2.0 * (drand48() - 0.5);
    z = 2.0 * (drand48() - 0.5);
    length = sqrt(x*x + y*y + z*z);
  } while ( ((length > 1.0) || (length == 0.0)) && (--maxAttempts) );

  if ( (length <= 1.0) && (length > 0.0) ) {     // Normalize.
    x /= length;
    y /= length;
    z /= length;
  } else {                                       // throw exception
    return 0;
  }
  return 1;
}


// 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], "uxview"))
      uxcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "uyview"))
      uycenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "uzview"))
      uzcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "uradius"))
      uradius = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "samples"))
      samples = atoi(myargv[i + 1]);
    else 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], "vradius"))
      vradius = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "-help"))
      return 0;
  }
  return 1;
}

void printUsage(char *progname) {
  cout << "Usage - " << endl;
  cout << "  " << progname << " [vxcenter <num>] [vycenter <num>] " << endl;
  cout << "  [vzcenter <num>] [uxcenter <num>] [uycenter <num>] "<< endl;
  cout << "  [vucenter <num>] [vradius <num>] [uradius <num>] " << endl;
  cout << "  [samples <num>] " << endl;
  cout << "  " << progname << " -help" << endl;
  exit(1);
}

void writeInventorHeader() {
  cout << "#Inventor V2.1 ascii" << endl;
  cout << endl;
  cout << "Complexity {" << endl;
  cout << "  value         0.3" << endl;
  cout << "}" << endl;
  cout << endl;
}