// Copyright 1997 Liceo Scientifico Alberti  
// This applet and associated documentation and files may be distributed in 
// its entirety for non-commercial purposes only, provided this copyright 
// notice is included in every copy made.  All other rights reserved by the author. 

// This applet and associated documentation and files are provided "as is" 
// with no warranty of any kind, either expressed or implied, including, but
// not limited to the implied warranties of merchantability, fitness for a
// particular purpose, and non-infringement.

// This applet has been based on the nice NASA applets by Kelly Jo Brown

// Programmer: Roberto De Leo

import java.awt.*;
import java.applet.*;
import java.lang.*;
import java.util.*;

public class Keplero extends Applet implements Runnable{

   private Button startbutton;
   private Button stopbutton;
   Orbita orbita = null;
   Graphics img,oldbackg;
   Vettore vi,Rversd,Fversd;
   UniVettore Rvers,Fvers;
   double RadInSp,AngInSp,Energy,MomAng;
   Satellite startingPoint = null, sun = null;
   Image screen,bar,slider,title,earea,oldback;
   Thread kicker = null;
   int runningFlag = 0;
   int emode=0,j=0;
   int div = 40;
   double k = 800000;
   int px=90,py=0,oldx=90,oldy=0,slide=50;
   double angle=0.0,anglei=.0,Anglei=0.,time=0,delta_time=.1,dist=0.,radius=0.,e=0;
   boolean draw_s=true,draw_t=true;
   static final double RAD_FACT=Math.PI/180;
   long begin_time;
   int pause;
   int posx = 425;
   int posDx= 40;
   int posDX= 80;
   int posy = 85;
   int posz = 35;
   Font TimesRoman = new Font("TimesRoman",Font.BOLD,14);
       
   public void run() 
   {
      double delta_angle;
         
      while(kicker != null) 
      {
 
        // Get current time in miliseconds for constant frame rate
        begin_time=System.currentTimeMillis();

        if( runningFlag == 1 ){
  
            // Calculate Radius

            radius = Traiettoria(vi,dist,e,Rversd,Fversd,angle,anglei,Anglei)/dist;

            // Calculate angle delta
            delta_angle=segno(Fvers.getDx()*vi.getDx() + Fvers.getDy()*vi.getDy())*RAD_FACT*2*Math.PI*Math.sqrt(1-e*e)/(radius*radius)*delta_time;

            // Add deltas

            angle = angle + runningFlag*delta_angle;

            // Calculate Coordinates
            px = (int) (Traiettoria(vi,dist,e,Rversd,Fversd,angle,anglei,Anglei)*Math.cos(angle)) - 5;
            py = (int) (Traiettoria(vi,dist,e,Rversd,Fversd,angle,anglei,Anglei)*Math.sin(angle)) - 5;

        }

         repaint();

         // Calculate pause to give us constant frame rate
         pause= Math.abs(50 - (int)(System.currentTimeMillis() - begin_time));
         try {Thread.sleep(pause);} catch (InterruptedException e){};

      }
   }
	
   public void init()
   {

      int i;

      startbutton = new Button("start");
      startbutton.setForeground(Color.white);
      startbutton.setForeground(Color.red);
      add(startbutton);
      stopbutton = new Button("stop");
      stopbutton.setForeground(Color.white);
      stopbutton.setForeground(Color.red);
      add(stopbutton);
      // Create space for old background
      oldback=createImage(25,25);
      oldbackg=oldback.getGraphics();

   }

   public void start() 
   {

      if(kicker == null) 
      {
        kicker = new Thread(this);
	kicker.start();
      }

   }

   public void stop() 
   {
      if(kicker != null)
      {
        kicker.stop();
        kicker = null;
      }
   }


   public boolean mouseDown(Event evt, int x, int y)
   {

       //set the starting position
       if( x>0 && x<420 && y>40 && y<400){

         if( runningFlag == 1 ){ runningFlag = 0; }
         if(orbita != null){ orbita.Clear(); }

         //Get the angle between planet-sun and x-axis
         angle = Math.atan((double) (y - sun.getY() - 5)/(x - sun.getX() - 5));

         if( (y - sun.getY() - 5)>0 && (x - sun.getX() - 5)<0){
            angle = angle + Math.PI;   
         }
         else if( (y - sun.getY() - 5)<0 && (x - sun.getX() - 5)<0){
            angle = angle - Math.PI;   
         }

         while( angle > 2*Math.PI || angle < 0 ){

             if( angle > 2*Math.PI ) { angle += -2*Math.PI; }
             if( angle < 0 ) { angle += 2*Math.PI; }

         }

         //define and draw reference frame and starting velocity

         if( startingPoint != null ){

             startingPoint.Clear();
             vi.Clear();
             Rversd.Clear();
             Fversd.Clear();

	 }

	 startingPoint = new Satellite(img,Color.blue,x-8,y-8);
	 startingPoint.Draw();
         vi = new Vettore(img,Color.white,x-2,y-2,x+30,y-2);
         vi.Draw();
         Rversd = new Vettore(img,Color.red,x-2,y-2,x-2+(int)(60*startingPoint.getX(sun)/startingPoint.getDist(sun)), y-2+(int)(60*startingPoint.getY(sun)/startingPoint.getDist(sun)));
         Fversd = new Vettore(img,Color.green,x-2,y-2,x-2-(int)(60*startingPoint.getY(sun)/startingPoint.getDist(sun)),y-2+(int)(60*startingPoint.getX(sun)/startingPoint.getDist(sun)));
         Rversd.Draw();
         Fversd.Draw();

	   Rvers = new UniVettore(0,0, startingPoint.getX(sun),startingPoint.getY(sun));
	   Fvers = new UniVettore(0,0,-startingPoint.getY(sun),startingPoint.getX(sun));

	   RadInSp = Rvers.getDx()*vi.getDx() + Rvers.getDy()*vi.getDy();

	   AngInSp = (Fvers.getDx()*vi.getDx() + Fvers.getDy()*vi.getDy())/startingPoint.getDist(sun);

	   MomAng = startingPoint.getDist(sun)*startingPoint.getDist(sun)*AngInSp;

	   Energy = (RadInSp*RadInSp+MomAng*MomAng/(startingPoint.getDist(sun)*startingPoint.getDist(sun)))/2-k/startingPoint.getDist(sun);

	   e    = Math.sqrt(1 + 2*Energy*MomAng*MomAng/(k*k));

//          System.out.println("angolo =" + angle);

           // anglei and Anglei are the two possible angles determined by 
           // the starting position & starting speed
 
	   anglei = angle - Math.abs(Math.acos((startingPoint.getDist(sun)-MomAng*MomAng/k)/(startingPoint.getDist(sun)*e)));

	   Anglei = angle + Math.abs(Math.acos((startingPoint.getDist(sun)-MomAng*MomAng/k)/(startingPoint.getDist(sun)*e)));

          // half length of the bigger ellipse axis
           dist = startingPoint.getDist(sun)*(1-e*Math.cos(angle-anglei))/(1-e*e);

	   orbita = new Orbita(img,Color.green,div,e,dist,anglei,Anglei,vi,angle,Rversd,Fversd,sun);

	   orbita.Draw();
	   startingPoint.Draw();
	   sun.Draw();
	   Fversd.Draw();
	   Rversd.Draw();

           rePrintBackground(img);

           img.drawString("" + lastThreeDigits(e),posx+posDx,posy);
           img.drawString("" + lastThreeDigits(dist),posx+posDx,posy+posz);
           img.drawString("" + lastThreeDigits(dist*(1-e*e)),posx+posDx,posy+2*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDx()),posx+posDx,posy+3*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDy()),posx+posDx,posy+4*posz);

           img.drawString("" + -lastThreeDigits(MomAng),posx+posDx,posy+5*posz);
           img.drawString("" + lastThreeDigits(( RadInSp*RadInSp+MomAng*MomAng/(startingPoint.getDist(sun)*startingPoint.getDist(sun)))/2),posx+posDX,posy+6*posz);
           img.drawString("" + -lastThreeDigits(k/startingPoint.getDist(sun)),posDX+posx,posy+7*posz);
           img.drawString("" + lastThreeDigits(Energy),posx+posDX,posy+8*posz);



     }

      return true;

   }

   public boolean mouseDrag(Event  evt, int  x, int  y){
          
       if( x>0 && x<420 && y>40 && y<400 ){

	   if(orbita != null){ orbita.Clear(); }
	   vi.Clear();
	   vi.setLastCoordinates(x,y);
	   vi.Draw();

	   Rvers = new UniVettore(0,0, startingPoint.getX(sun),startingPoint.getY(sun));
	   Fvers = new UniVettore(0,0,-startingPoint.getY(sun),startingPoint.getX(sun));

	   RadInSp = Rvers.getDx()*vi.getDx() + Rvers.getDy()*vi.getDy();

	   AngInSp = (Fvers.getDx()*vi.getDx() + Fvers.getDy()*vi.getDy())/startingPoint.getDist(sun);

	   MomAng = startingPoint.getDist(sun)*startingPoint.getDist(sun)*AngInSp;

	   Energy = (RadInSp*RadInSp+MomAng*MomAng/(startingPoint.getDist(sun)*startingPoint.getDist(sun)))/2-k/startingPoint.getDist(sun);

	   e    = Math.sqrt(1 + 2*Energy*MomAng*MomAng/(k*k));

//          System.out.println("angolo =" + angle);

           // anglei and Anglei are the two possible angles determined by 
           // the starting position & starting speed
 
	   anglei = angle - Math.abs(Math.acos((startingPoint.getDist(sun)-MomAng*MomAng/k)/(startingPoint.getDist(sun)*e)));

	   Anglei = angle + Math.abs(Math.acos((startingPoint.getDist(sun)-MomAng*MomAng/k)/(startingPoint.getDist(sun)*e)));

          // half length of the bigger ellipse axis
           dist = startingPoint.getDist(sun)*(1-e*Math.cos(angle-anglei))/(1-e*e);

	   orbita = new Orbita(img,Color.green,div,e,dist,anglei,Anglei,vi,angle,Rversd,Fversd,sun);

	   orbita.Draw();
	   startingPoint.Draw();
	   sun.Draw();
	   Fversd.Draw();
	   Rversd.Draw();

           rePrintBackground(img);

           img.drawString("" + lastThreeDigits(e),posx+posDx,posy);
           img.drawString("" + lastThreeDigits(dist),posx+posDx,posy+posz);
           img.drawString("" + lastThreeDigits(dist*(1-e*e)),posx+posDx,posy+2*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDx()),posx+posDx,posy+3*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDy()),posx+posDx,posy+4*posz);

           img.drawString("" + -lastThreeDigits(MomAng),posx+posDx,posy+5*posz);
           img.drawString("" + lastThreeDigits(( RadInSp*RadInSp+MomAng*MomAng/(startingPoint.getDist(sun)*startingPoint.getDist(sun)))/2),posx+posDX,posy+6*posz);
           img.drawString("" + -lastThreeDigits(k/startingPoint.getDist(sun)),posDX+posx,posy+7*posz);
           img.drawString("" + lastThreeDigits(Energy),posx+posDX,posy+8*posz);

           return true;

       }

       return false;

   }

   public final synchronized void update(Graphics g) 
   {
      String str;
      int i;

      // Set up offscreen buffer
      if(screen == null) {
         screen = createImage(580,400);
         img = screen.getGraphics();
      }

      // Only want to draw this once

     if(draw_t){

         // Fill black background
         img.setColor(Color.black);
	 img.fillRect(0,40,419,399);
         // Set the background Font
         img.setFont(TimesRoman);
         img.setColor(Color.gray);
         img.fillRect(0,0,579,39);
         // Fill white background
         img.setColor(Color.white);
         img.fillRect(420,40,579,399);
	 img.setColor(Color.blue);
         img.drawString("e  =",posx,posy);
         img.drawString("a  =",posx,posy+posz);
         img.drawString("b  =",posx,posy+2*posz);
         img.drawString("Vx =",posx,posy+3*posz);
         img.drawString("Vy =",posx,posy+4*posz);
         img.drawString("L  =",posx,posy+5*posz);
         img.drawString("En. Cin. =",posx,posy+6*posz);
         img.drawString("En. Pot. =",posx,posy+7*posz);
         img.drawString("En. Tot. =",posx,posy+8*posz);

         //Draw sun
         sun = new Satellite(img,Color.yellow,210,180);
         sun.Draw();

         // get background for first draw
         oldbackg.drawImage(screen,-oldx-sun.getX()-5,-oldy-sun.getY()-5,null);

         draw_t=false;

     }

     if( runningFlag == 1 ){
    
         // Replace Old background

         img.drawImage(oldback,oldx+sun.getX()+5,oldy+sun.getY()+5,null);

         // get new background

         oldbackg.drawImage(screen,-px-sun.getX()-5,-py-sun.getY()-5,null);

         // Keep track of previous x,y coordinates

         oldx=px;
         oldy=py;

         // Draw Satelite

         px += sun.getX()+5;
         py += sun.getY()+5;
         if(startingPoint==null){startingPoint=new Satellite(img,Color.pink,px,py);}
         startingPoint.setCoordinates(px,py);
         startingPoint.Draw();
         orbita = new Orbita(img,Color.green,div,e,dist,anglei,Anglei,vi,angle,Rversd,Fversd,sun);
         orbita.Draw();
	 rePrintBackground(img);

	 img.drawString("" + lastThreeDigits(e),posx+posDx,posy);
	 img.drawString("" + lastThreeDigits(dist),posx+posDx,posy+posz);
	 img.drawString("" + lastThreeDigits(dist*(1-e*e)),posx+posDx,posy+2*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDx()),posx+posDx,posy+3*posz);
           img.drawString("" + lastThreeDigits((double)vi.getDy()),posx+posDx,posy+4*posz);

           img.drawString("" + -lastThreeDigits(MomAng),posx+posDx,posy+5*posz);
           img.drawString("" + lastThreeDigits(( RadInSp*RadInSp+MomAng*MomAng/(startingPoint.getDist(sun)*startingPoint.getDist(sun)))/2),posx+posDX,posy+6*posz);
           img.drawString("" + -lastThreeDigits(k/startingPoint.getDist(sun)),posDX+posx,posy+7*posz);
           img.drawString("" + lastThreeDigits(Energy),posx+posDX,posy+8*posz);

      }

      g.drawImage(screen,0,0,null);
     
   }

    public boolean action(Event event,Object arg){
      
	if(event.target == startbutton){

            draw_t = true;
	    rePrintBackground(img);
	    startingPoint.Clear();
	    vi.Clear();
	    Rversd.Clear();
	    Fversd.Clear();
	    runningFlag = 1;
	    return true;

	}
	else if (event.target == stopbutton){

	    runningFlag = 0;
	    return true;

	}

	else return action(event,arg);

    }

    public int segno(double pippo){
    
           if(pippo > 0){ return 1; }
           else if(pippo < 0){return -1; }
           else{ return 0; }

    }

    public double Traiettoria(Vettore vi,double dist,double e,Vettore Rversd,Vettore Fversd,double angle,double anglei,double Anglei){

         if( ( angle <= Math.PI/2 ) || ( angle >= 3*Math.PI/2 ) ){

             if( (vi.getDx()*Fversd.getDx()+vi.getDy()*Fversd.getDy())*(vi.getDx()*Rversd.getDx()+vi.getDy()*Rversd.getDy()) < 0 ){
              
                return (dist*(1-e*e)/(1-e*Math.cos(angle-anglei)));

             }
             else{
 
                return (dist*(1-e*e)/(1-e*Math.cos(angle-Anglei)));

             }

           }
           else{

             if( (vi.getDx()*Fversd.getDx()+vi.getDy()*Fversd.getDy())*(vi.getDx()*Rversd.getDx()+vi.getDy()*Rversd.getDy()) < 0 ){
            
                return (dist*(1-e*e)/(1-e*Math.cos(angle-anglei)));
      
             }
             else{
      
             return (dist*(1-e*e)/(1-e*Math.cos(angle - Anglei)));
             
             }
           }
     }

     public double lastThreeDigits(double pippo){

	 return ((double)((int)(pippo*1000)))/1000;

     }

     public void rePrintBackground(Graphics img){

	 img.setColor(Color.gray);
         img.fillRect(0,0,579,39);
         img.setColor(Color.white);
         img.fillRect(420,40,579,399);
         img.setColor(Color.blue);
         img.drawString("e  =",posx,posy);
         img.drawString("a  =",posx,posy+posz);
         img.drawString("b  =",posx,posy+2*posz);
         img.drawString("Vx =",posx,posy+3*posz);
         img.drawString("Vy =",posx,posy+4*posz);
         img.drawString("L  =",posx,posy+5*posz);
         img.drawString("En. Cin. =",posx,posy+6*posz);
         img.drawString("En. Pot. =",posx,posy+7*posz);
         img.drawString("En. Tot. =",posx,posy+8*posz);

     }

}
