//////// Planets orbit the sun.  (bam/CST112) ////////

Planet[] p;
int np=0;
float textX=10,textY=10;
int textDY=0;

float GM=10000;

float sunX, sunY;
float sunR;
boolean clockwise=true;

boolean debug, help=true, lines, tails=true, names=true, slow, quit, xyflag;    // (Default values = false.
int back=64;


void setup() {
  size(1000,800);

  sunX=  width/2;
  sunY=  height/2;
  sunR=  4;

  p=  new Planet[200];        // Array of planets.

}
void restart() {
  //// Re-initialize.
  np=0;
  help=  true;
  names=  true;
  tails=  true;
}

void draw() {
  //// Draw the next frame ////
  background ( back ); 
  //  background( 0 );

  fill(255,155,0);
  text( "P L A N E T S", width/2, 20);
  if (np<1) {
    text( "Click for new planets;", width/2, 40);
    text( "or enter p for", width/2, 50);
    text( "Solar System", width/2, 60);
  }
  
  if ( quit ) tryQuit();

  frameRate( slow ? 2 : 30 );
  
  fill(0);
  text( np+" planets", width-100, 20 );
  textDY=0;
  int bugY=0;
  if (help) helpshow( width-200, 50 );
  
  sun();

  // Planets -- display each one.
  for( int i=0; i<np; i++) {
    p[i].move();
    p[i].show();
    if (debug) p[i].info( 100, 100+15*bugY++ );
  }    
}

void mousePressed() {
  nextline( mouseX+", "+mouseY );
  //// Check for click on a planet.    ++++
  //// Drag it to an orbital position and recalculate velocity.  +++???
  fill(0);
  createPlanet( mouseX, mouseY );
}



void tryQuit() {
  // Try to quit.  Check last key pressed -- ask, if first time.
   if (key == 'q'  ||   key == 'Q') {      fill(0); text( "Q U I T   ???", sunX, sunY );   }
   else if (key == 'y')     exit(); 
   else quit = false;
}
void keyPressed() {
  //// Check which key.
  if (key == '?') help=  ! help;      // Toggle help flag.
  if (key == 'b') { back=  back < 1 ? 255 : (back > 64 ? back - 16 : back / 2) ; }      // Darken the background.
  if (key == 'd') debug=  ! debug;    // Toggle debugging flag.
  if (key == 'f') slow=  ! slow;      // Toggle slow flag.
  if (key == 'F') frameRate(.2);       // Freeze!
  if (key == 'l') lines=  ! lines;    // Toggle lines flag.
  if (key == 'n') names=  ! names;    // Toggle names flag.
  if (key == 't') tails=  ! tails;    // Toggle tails flag.
  if (key == 'x') xyflag=  ! xyflag;    // Toggle xyflag flag.

  if (key=='P' || key=='p') solarPlanets();
  if (key=='C' || key=='c') clockwise = ! clockwise;
  if (key=='R' || key=='r') { restart(); }
  if (key=='Q' || key=='q') {
    if (quit) exit();
    else quit =  ! quit;
  }
}
void helpshow( float x, float y ) {
  //// Display help messages
  int dy=0;
  fill(255,155,0);
  text( "?  Help", x, y+15*dy++ );
  text( "b  background darkens.", x, y+15*dy++  );
  text( "c  counterclockwise.", x, y+15*dy++  );
  text( "d  Debugging", x, y+15*dy++ );
  text( "f  frame rate (2 per second)", x, y+15*dy++ );
  text( "F  Freeze (5 seconds)", x, y+15*dy++ );
  text( "l  Lines (from sun to planet)", x, y+15*dy++ );
  text( "n  names (displayed with planet)", x, y+15*dy++ );
  text( "t  tails (from previous position)", x, y+15*dy++ );
  text( "x  xyflag (create planets from (x,y) rather than r,theta)", x, y+15*dy++ );
  text( "----", x, y+15*dy++ );
  text( "P  Populate with solar planets", x, y+15*dy++ );
  text( "R  Restart", x, y+15*dy++ );
  text( "Q  Quit", x, y+15*dy++ );
  //
  text( "", x, y+15*dy++ );
  text( "Click to create a new planet", x, y+15*dy++ );
}


void sun() {
  //// Draw the sun;
  fill( 255,255,0);
  ellipseMode(RADIUS);
  ellipse( sunX, sunY, sunR, sunR );
}
void nextline( String s ) {
  //// Display text on next available line.
  text( s, textX, textY+15*textDY++ );
}



void createPlanet( float x, float y) {
  //// Create a new planet at x,y
  if (np >= p.length) {
    background( 255 );
    String s="TOO MANY PLANETS!  Press R key to reset.";
    text( s, 10,10 );
    text( s, 10, height/4 );
    text( s, 10, height/2 );
    fill(0);    
    text( s, 10,30 );
    text( s, width/2, height/4 );
    text( s, width/2, height/2 );
    return;
  }

  float r=  dist(x,y, sunX,sunY);
  if (xyflag) {
      p[np]=  new Planet( x,y );
  } else {
      float theta=  atan( (y-sunY)  / (x-sunX)  );
      if (x < sunX) theta += PI;
      p[np]=  new Planet( r );
    p[np].theta=  theta;
  }
  // Planet details.
  p[np].c=  np<colorlist.length ? colorlist[np] : color( random(255), random(255), random(255) );
  if (np<namelist.length) p[np].name=  namelist[np];
  p[np].name=  xyflag ? "Planet-XY " : "Unknown ";
  p[np].name += np;
  p[np].pR=  2+random(10);
  np++;
}  
  
  
color[] colorlist= { 
  color(150,150,150), 
  color(50,150,50), 
  color(150,200,250), 
  color(255,200,200), 
  color(255,127,200), 
  color(200,200,150), 
  color(100,100,200), 
  color(127,200,100) 
};
String[] namelist= { "Mercury", "Venus", "Terra", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" };
float[] rplist=  { 1,4,4,2, 15,12,10,10 };
float[] rlist=  { 30,60,90,120, 200,300,400,500 };

void solarPlanets() {  
  //// Create all 8 solar planets.
  for (int i=0; i<8 && np<p.length; i++) {
    createSolar( i, 0.0 );
  }
  p[5].ring=3;
  p[4].ring=0.0001;
}

void createSolar( int k, float theta ){
   //// Create one solar planet
   if (k>rlist.length) return;        // (Already done.)
   p[np]=  new Planet( rlist[k] );    // Radius of orbit.
   p[np].theta=  0;
   //  p[np].theta=  -k * PI / 6;rp
   String name=  k<namelist.length ?  namelist[k] : "Unknown "+np;
   p[np].name=  clockwise ? name : "Anti-"+name;
   p[np].pR=  rplist[k];
   color c=  k<colorlist.length ? colorlist[k] : color( random(255), random(255), random(255) );   
   p[np].c=  clockwise ? c : c-0x404040;
   np++;
}
/*
  nextline( mouseX+", "+mouseY );
  p[np]=  new Planet( r );
  p[np].c=  np<colorlist.length ? colorlist[np] : color( random(255), random(255), random(255) );
  if (np<namelist.length) p[np].name=  namelist[np];
  p[np].pR=  2+random(10);
  np++;
*/


class Planet
{
  //// Planet date and orbit data.
  boolean initialized=false;

  // (DEFAULT VALUES)  

  // ORBIT:
  private float r= 150;      // Radius of the orbit.  (~ 150 M km )
  float d = PI * 2 * r;    // Distance travelled in one period.
  float period = 365*24*60*60;  // Time to complete the orbit  (year)
  //  float v= d / period;          // Velocity of planet.  
  private float v= sqrt(GM/r);          // Velocity of planet.  
  
  // POSITION of the planet:
  float theta=0;    // Angle from sun to planet.
  float dtheta=  v/r;
  //float x=sunX, y=sunY-r;
  private float x= sunX + r;                // Assumes theta=0 (horizontal).
  private float y= sunY;
  float dx,dy;

  // PLANET details:
  String name="Unknown" ;
  float pR=  6;          // Radius of planet.
  color c=  color(200,255,255);
  color cNite=  color(100,100,100);  
  float ring=0; 

  // CONSTRUCTORS //
  Planet() { pDetails(); }      // Use defaults.  }
  Planet( float r) { 
    //// Construct a planet at radius r (theta=0).
    this.r=  r;
//    this.theta=  -np * PI / 12;
    this.theta=  0;
    rCreate();
  }
  Planet( float x, float y) { 
    //// Construct a planet at (x,y).
    this.x=  x;
    this.y=  y;
    xyCreate();
  }
  // INITIALIZE Planet orbit and planetary details. //
  void rCreate() {
    //// Initialize planet orbit from from r,theta
    x=  sunX + r * cos(theta);
    y=  sunY + r * sin(theta);
    v=  sqrt( GM / r );
    v= clockwise ? v : -v ;
    pDetails();
  }
  void xyCreate() {
    //// Initialize planet orbit from (x,y)
    r=  dist(x,y, sunX,sunY );
    v=  sqrt( GM / r );
    v= clockwise ? v : -v ;
    // theta=  atan( (x-sunX) / (y-sunY)  );
    theta=  atan( (y-sunY) / (x-sunX) );
    dtheta=  v/r;
    pDetails();
  }
  void pDetails()
  {
    //// Initialize planetary details (and orbit facts).
    v=  sqrt( GM / r );
    v= clockwise ? v : -v ;
    dtheta=  v/r;
    d=  PI * 2 * r;    // Distance travelled in one period.
//    v=  d / t;          // Velocity of planet (per frame ???).
//    t=  d / v;          // Time to complete the orbit  (year)
    //// Planetary details.
    if (c == 0) c=  color( random(255), random(255), random(255) );
    if (name == "") name= "Unknown #"+np;
    if (pR == 0) pR=  2 + random(30);
  }
  

  float oldx, oldy;

  // METHODS //
  void move() {
    //// Move in circular orbit
    oldx=x; oldy=y;
    theta = theta % (2*PI);

    x=  sunX + r*cos(theta);
    y=  sunY + r*sin(theta);

    dx=  x - oldx;
    dy=  y - oldx;

    theta += dtheta;
    
/*
    dx = v * cos(theta);
    dy = v * sin(theta);
    x += dx;
    y -= dy;
    
    float tan=  (y-sunY) / (x-sunX);
    if (tan >100000000) tan= -tan;
    theta=  atan( tan  );
*/

    
  }
  
  void show() {
    //// Show the planet at new position (x,y)
    fill( c );
    ellipseMode( RADIUS );
    ellipse( x, y, pR, pR );
    if (ring>0) {
      noFill();
      if (ring>0) {
        stroke( ring>1 ? 240 : 200 );
        strokeWeight(ring);
        arc( x, y, 1.5*pR, 0.25*pR+ring, -0.25*PI, 1.25*PI );
        strokeWeight(1);
        noStroke();
      }
    }

    if (names) text( name, x+pR, y-5 );

    if (tails) {
      stroke(255,0,0);
      line( oldx-2*(x-oldx), oldy-2*(y-oldy), x,y );    // Display a "tail" from last (x,y) position.
      text( "...v="+v, oldx-2*(x-oldx), oldy-2*(y-oldy) );    // Display a "tail" from last (x,y) position.
      stroke(0);
    }


    // Debugging:  coords
    if (debug) {
      text( "("+x+", "+y+")", x+pR, y+15);
      text( "r:  "+r+"  theta="+theta, x+pR, y+30 );
      text( "v:  "+v, x+pR, y+45 );
    }
    // Lines
    if (lines) {      line(sunX,sunY, x,y );    }
  }
  void info( float textX, float textY ) {
    //// Display a line of information about this planet.

    text( name, textX+20, textY );
    textX += 70;

    String s="";
    s += "      v="+roundto(v,4);
    s += "      d="+roundto(d,4);
    s += "      r="+r;
    s += "      theta="+roundto( theta, 4 );
    s += "      "
        +"("+roundto( x, 4 )
        +","+roundto( y, 4 )
        +")"; 
    if (ring > 0)     s += "       ring="+ring;

    float rr=  r - sqrt( sq(x - sunX) + sq(y - sunY) );
    if (abs(rr) > r*0.0001 )     s += "       rr="+rr;
        
    text( s, textX, textY );
    
  }
  float roundto(float f, int places) {
    //// round to specified number of places.
    float p=  pow(10,places);
    return round( f * p ) / p;
  }


}

