// Author      : Josh Grant
// Course      : CIS5900, Computer Graphics, Fall 2000
// Assignment  : hw05
// Date        : October 10th, 2000
// Description : This program randomly samples points on a sphere and outputs
//               the scene in Inventor format.  The class Sphere is called
//               and used to handle the inventor writes.  The center of the
//               sphere, its radius, the number of samples and the radius of
//               the samples can all be initilized with the command line.
//               However, it is not necessary to run the program.  A default
//               value is given to each parameter if not stated on the
//               command line.   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 xcenter = 0.0, ycenter = 0.0, zcenter = 0.0;
double  radius = 0.75;
double sradius = 0.1;
int    samples = 10;

int main(int argc, char **argv) {
  int i;
  double x = 0.0, y = 0.0, z = 0.0;
  Sphere *sphere;
  Vector *center, *samp;
  double scolor[3];

  scolor[0] = 0.0, scolor[1] = 1.0, scolor[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]);

  // write inventor header info.
  writeInventorHeader();

  // Create a Vector for the center of the sphere and then create a new
  // Sphere object.
  center = new Vector(xcenter, ycenter, zcenter);
  sphere = new Sphere(radius, *center);
  sphere->writeInventor();

  // Since sphere has already been written to stdout, we can now modify the
  // object for different spheres.
  sphere->radius = sradius;
  sphere->diffuse = scolor;
  srand48(getpid());
  for (i = 0; i < samples; i++) {
    if (pointOnSphere(x, y, z)) {
      // Vector which holds the point to be sampled on the sphere.
      samp = new Vector(xcenter + x*radius, ycenter + y*radius,
			zcenter + z*radius);

      *(sphere->center) = *samp;
      sphere->writeInventor();

      delete samp;
    }
  }

  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], "xcenter"))
      xcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "ycenter"))
      ycenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "zcenter"))
      zcenter = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "radius"))
      radius = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "samples"))
      samples = atoi(myargv[i + 1]);
    else if (!strcmp(myargv[i], "sradius"))
      sradius = atof(myargv[i + 1]);
    else if (!strcmp(myargv[i], "-help"))
      return 0;
  }
  return 1;
}

void printUsage(char *progname) {
  cout << "Usage -" << endl;
  cout << "  " << progname << " [xcenter <num>] [ycenter <num>] [zcenter <num>]"
  << endl << "  [radius <num>] [samples <num>] [sradius <num>]";
  cout << "\nThis message:\n  " << progname << " -help" << endl;
  exit(1);
}

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