/*
 * File: main.cpp
 *
 * Jeffrey Ladino - jnl22@ccs.neu.edu
 * 
 * main project file
 * executes the semester project for Computer Graphics
 * draws bouncing balls on the screen
 */

#include "circle2d.h"
#include "character2d.h"
#include "transforms.h"
#include "shape2d.h"
#include "point2d.h"
#include "matrix3d.h"
#include "vector3d.h"
#include "bb.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>

#define WHITE 255
#define BLACK 0
#define NO_COLLISION -1

// GLOBAL VALUES
int SIZE=600; // size of the drawing window
int BALLCOUNT=1; // number of balls
Real RADIUS=30;
Real ANGULAR_VELOCITY=0.1;

// Tells the user how to run the program
void printUsage(){
  printf("\nbounce -n <number_balls> [-r <radius>] [-w <windowSize>] [-h]\n\n");
  printf("Options:\n");
  printf("\t-n sets the number of balls to appear on the screen from 1 to 99\n");
  printf("\t-r sets an integer radius from 1 to 99 (default=30)\n");
  printf("\t-w sets the windowSize from 200 to 1200 (default=600)\n");
  printf("\t-h prints this help message\n\n");
  printf("radius * number_balls MUST be less than windowSize!\n");
  printf("bounce will run forever... it must be terminated with a Control-C\n\n");
  printf("Each ball gets assigned the following characteristics by pseudorandom numbers\n");
  printf("\tposition\n\tvelocity\n\trotation\n\tcolors\n");
  printf("\nbounce is created by Jeffrey Ladino jnl22@ccs.neu.edu\n\n");
}

void printGlobalValues(){
  printf("number of balls = %d\n", BALLCOUNT);
  printf("radius of each ball = %d\n", (int)RADIUS);
  printf("windowSize = %d\n", SIZE);
}

void init_rand(int seed){
  srand(seed);
}

int my_rand(int lbound, int ubound){
  int randy, length;
  length = ubound-lbound;
  randy = rand();
  randy %= length;
  randy += lbound;
  return randy;
}

void detectCollisions(bb* b, int n, int ubound){
  Real collide_sq = 4*RADIUS*RADIUS;
  vector3d reference;
  vector3d distance;

  // detect if this ball has collided with one of
  // the balls in the range from n to ubound
  for(int i=n+1; i<ubound; i++){
    distance = b[n].getcenter() - b[i].getcenter();
    if(distance.mag_sq() < collide_sq){
      b[n].collide_with(b[i], RADIUS);
    }
  }
}


void parseArgs(int argc, char** argv){

  if(argc == 1){
    printUsage();
    exit(0);
  }

  else{
    int currentarg = 1;
    while(currentarg < argc){
      if(argv[currentarg][0] == '-' && argv[currentarg][1] == 'h'){
	printUsage();
	exit(0);
      }
      else if(argv[currentarg][0] == '-' && argv[currentarg][1] == 'n'){
	currentarg++;
	BALLCOUNT = atoi(argv[currentarg]);
	assert(BALLCOUNT > 0);
	assert(BALLCOUNT < 100);
      }
      else if(argv[currentarg][0] == '-' && argv[currentarg][1] == 'r'){
	currentarg++;
	RADIUS = atoi(argv[currentarg]);
	assert(RADIUS > 0);
	assert(RADIUS < 100);
      }
      else if(argv[currentarg][0] == '-' && argv[currentarg][1] == 'w'){
	currentarg++;
	SIZE = atoi(argv[currentarg]);
	assert(SIZE > 199);
	assert(SIZE < 1201);
      }
      else{
	printf("\nERROR: argument %d unrecognized: %s\n", 
	       currentarg, argv[currentarg]);
	printUsage();
	exit(1);
      }
      currentarg++;
    } // end while

    // enforce the rule that RADIUS * BALLCOUNT <= SIZE
    if(BALLCOUNT * RADIUS > SIZE){
      printf("\nWARNING: number_balls * radius > windowSize\n");
      printf("FORCING number_balls to 1\n");
      BALLCOUNT = 1;
    }
    printf("\nbounce: running with the following values\n");
    printGlobalValues();
  }

} // end parseArgs


/* check_overlap
 *
 * calculates if one ball is overlapping any other
 */
int check_overlap(int index, int ubound, Real radius, int* mark, bb* balls){
  //printf("check overlap %d, %d\n", index, ubound);
  vector3d difference;
  Real distance_sq, d_sq;
  
  d_sq = 4*radius*radius;

  for(int i=0;i<ubound;i++){
    if(i != index){
      difference = balls[i].getcenter() - balls[index].getcenter();
      distance_sq = difference.mag_sq();
      if( distance_sq < d_sq){
	// DEBUGGING PRINTOUTS
	/*
	printf("%d overlaps %d - distance_sq is %e, d_sq is %e\n", 
	       index, i, distance_sq, d_sq);
	balls[index].getcenter().print();
	balls[i].getcenter().print();
	difference.print();
	*/
	mark[index] = i;
	return i;

      }
    }
  } // end for

  // no overlap found
  mark[index] = NO_COLLISION;
  return NO_COLLISION;

} // end check_overlap()

/* fix_overlaps
 *
 * WARNING: expensive recursive function!!!!
 * This functions detects balls whose boundaries are overlapping.
 *
 */ 
void fix_overlaps(int lbound, int ubound, Real radius, int* mark, bb* balls){
  vector3d newcenter;
  for(int i=lbound;i<ubound;i++){
    if(mark[i] != NO_COLLISION){
      check_overlap(i, ubound, radius, mark, balls);
      if(mark[i] != NO_COLLISION){
	check_overlap(i, ubound, radius, mark, balls);
	newcenter[Xpt] = (Real)my_rand(-SIZE/2 + (int)radius, 
				       SIZE/2 - (int)radius);
	newcenter[Ypt] = (Real)my_rand(-SIZE/2 + (int)radius, 
				       SIZE/2 - (int)radius);
	// debugging printouts
	/*
	printf("newcenter\n");
	newcenter.print();
	printf("balls %d BEFORE moving\n", i);
	balls[i].getcenter().print();
	printf("balls %d AFTER moving\n", i);
	balls[i].getcenter().print();
	*/
	balls[i].setcenter(newcenter);
	fix_overlaps(i, ubound, radius, mark, balls);
	return;
      }
    }
  }
} // end fix_overlaps()

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

  // create BALLCOUNT bouncing balls
  bb* lotto;
  lotto = new bb[BALLCOUNT];
  
  // initialize random seed
  init_rand(BALLCOUNT);

  // initialize all of the bouncing balls
  for(int i=0;i<BALLCOUNT; i++){
    int lbound = -SIZE/2 + (int)RADIUS;
    int ubound = SIZE/2 - (int)RADIUS;

    lotto[i].init((Real)my_rand(lbound, ubound), // xcenter 
		  (Real)my_rand(lbound, ubound), // ycenter 
		  (Real)my_rand(-7, 7), (Real)my_rand(-7, 7), // velocity
		  RADIUS, // radius
		  ANGULAR_VELOCITY*(Real)my_rand(-3,3), // angular velocity
		  (unsigned) i, // number inside the led
		  (unsigned long)my_rand(1,254), // ball color
		  (unsigned long)my_rand(1,254) // led color
		  ); 
  } // end for

  // fix overlapping balls
  int collisions[BALLCOUNT];
  for(int i=0;i<BALLCOUNT;i++) collisions[i] = 0;
  // recursive function to fix overlapping balls
  fix_overlaps(0, BALLCOUNT, RADIUS, collisions, lotto);
  

  // Viewing Transform
  matrix3d M = Translate(SIZE/2, SIZE/2) * Reflect_xaxis();

  // Create the drawing window
  SimpleDraw SD1(0,0,SIZE,SIZE);

  // animate the bouncing balls
  while(true){
    unsigned long count=0;
    SD1.Clear();
    for(int j=0;j<BALLCOUNT;j++){
      lotto[j].draw_color(SD1, M);
      detectCollisions(lotto, j, BALLCOUNT);
    }
    for(int jj=0;jj<BALLCOUNT;jj++){
      lotto[jj].move(-SIZE/2,-SIZE/2,SIZE/2,SIZE/2);
    }
    usleep(150);
  } // end infinite loop

  // delete the balls
  delete lotto;
  
} // end main



