import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;


public class Game extends Applet implements Runnable, MouseListener, MouseMotionListener {
	
		Graphics backg;
		Image backbuffer;
		Thread animator;
		Image map;
		Image[] sprite = new Image[16];
		Image[] t_sprite = new Image[1000];
		Image[] t_sprite_gun = new Image[1000];
		Graphics2D t_sprite2D;
		Enemy[] e = new Enemy[16];
		Tower[] tower = new Tower[1000];
		Path path;
		int mouse_x;
		int mouse_y;
		int mouse_mx;
		int mouse_my;
		int i = 0;
		int t = 1;
		int radius = 45;
		double attack_id[] = new double[1000];
		int attack_enemy_id[] = new int[1000];
		double[] theta = new double[1000];
		double[] old_theta = new double[1000];
		double[] delta_theta = new double[1000];
	   long fps = 30;
	   long frame_count = 0; // Counts the number of frames
	   int[] attack_count = new int[1000]; // Counts the number of frames between tower shots
	   boolean[] destroyed = new boolean[1000];
	   AffineTransform[] tx;
	   
   public void init() {
	  fps = 1000/fps;
      backbuffer = createImage( 640, 480 );
      t_sprite2D = (Graphics2D) backbuffer.getGraphics();
      backg = (Graphics) t_sprite2D;
      t_sprite2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      map = getImage(getDocumentBase(), "map.png");
      backg.drawImage(map, 0, 0, this);
      path = new Path();
      addMouseListener( this );
      addMouseMotionListener (this);
      tx = new AffineTransform[1000];
   }
   
   public void mouseEntered( MouseEvent e ) { }
   public void mouseExited( MouseEvent e ) { }
   public void mousePressed( MouseEvent e ) { }
   public void mouseReleased( MouseEvent e ) { }
   public void mouseClicked( MouseEvent e ) {
      mouse_x = e.getX();
      mouse_y = e.getY();
      if (mouse_x % 15 != 0){
    	  mouse_x-= mouse_x % 15 + 1;
      }
      else if(mouse_x % 15 == 0){
    	  mouse_x-= 1;
      }
      if (mouse_y % 15 != 0){
    	  mouse_y-= mouse_y % 15 + 1;
      }
      else if(mouse_y % 15 == 0){
    	  mouse_y-= 1;
      }
      boolean alreadyTower = checkForTower(mouse_x, mouse_y);
      boolean blocked = path.checkPath(mouse_x, mouse_y);
      if (!alreadyTower && !blocked){
    	  place_tower("turret", mouse_x, mouse_y);
      }
      else{
    	  System.out.println("Can't do this");
      }
   }
   
   public void mouseMoved( MouseEvent e ) {
	      mouse_mx = e.getX();
	      mouse_my = e.getY();
	      if (mouse_mx % 15 != 0){
	    	  mouse_mx-= mouse_mx % 15 + 1;
	      }
	      else if(mouse_mx % 15 == 0){
	    	  mouse_mx-= 1;
	      }
	      if (mouse_my % 15 != 0){
	    	  mouse_my-= mouse_my % 15 + 1;
	      }
	      else if(mouse_my % 15 == 0){
	    	  mouse_my-= 1;
	      }
	      showStatus( "Mouse at (" + mouse_mx + "," + mouse_my + ")" );
	   }
	   public void mouseDragged( MouseEvent e ) { }


   
   public void start() {
		animator = new Thread(this);
		animator.start();
	    }
   public void run() {
	   while (Thread.currentThread() == animator){
		   primary();
		   repaint();
		   try {
				Thread.sleep(fps);
			    } catch (InterruptedException e) {
				break;
			    };
	   }
   }
   
   public boolean checkForTower(int x, int y){
	   boolean alreadyTower = false;
	   int index = 1;
	   if (t == 1){
		   return alreadyTower;
	   }
	   else{
		   while (!alreadyTower && index < t){
			   if(tower[index].x_pos == x && tower[index].y_pos == y){
				   alreadyTower = true;
			   }
			   index++;
		   }
		   return alreadyTower;
	   } 
   }
   
   public void place_tower(String name, int x, int y){ // Creates a tower object
	   tower[t] = new Tower(name, x, y);
	   tx[t] = new AffineTransform();
	   tx[t].translate(x, y);
	   attack_id[t] = 1000;
	   attack_enemy_id[t] = -1;
	   t++;
   }
   
   public void drawEnemy(){ // Draws an enemy to the screen
	   for(int j = 1; j < i; j++){
		   sprite[j] = getImage(getDocumentBase(), e[j].img);
		   e[j].move();
		   if (e[j].current_hp <= 0 || e[j].x_pos == 427 && e[j].y_pos > 374){
			   
		   }
		   else{
			   backg.setColor(Color.green);
		   backg.fillRect(e[j].x_pos, e[j].y_pos - 5, 15*e[j].current_hp/e[j].max_hp, 3);
		   backg.setColor(Color.black);
		   backg.drawRect(e[j].x_pos, e[j].y_pos - 5, 15, 3);
		   backg.drawImage(sprite[j], e[j].x_pos, e[j].y_pos, this);
		   }
		   
	   }
   }
   public void closestEnemy(int j, double id){ // Calculates which enemy is closest to the tower and returns the id
	   double attack_id_temp;
	   if (destroyed[j] || id > tower[j].range){
		   attack_id[j] = 1000;
		   for(int k = 1; k < i; k++){
			   if(Math.sqrt((double)(tower[j].range*tower[j].range - (tower[j].x_middle - e[k].x_middle)*(tower[j].x_middle - e[k].x_middle))) > 0 && 
				  Math.sqrt((double)(tower[j].range*tower[j].range - (tower[j].y_middle - e[k].y_middle)*(tower[j].y_middle - e[k].y_middle))) > 0 &&
				  e[k].current_hp > 0){
				  attack_id_temp = Math.sqrt((double)((tower[j].x_middle - e[k].x_middle)*(tower[j].x_middle - e[k].x_middle) + (tower[j].y_middle - e[k].y_middle)*(tower[j].y_middle - e[k].y_middle)));
				  if (attack_id[j] >= attack_id_temp){
					  attack_id[j] = attack_id_temp;
					  attack_enemy_id[j] = k;
				  }
			   }
		   }
	   }
	   else{
		   attack_id_temp = Math.sqrt((double)((tower[j].x_middle - e[attack_enemy_id[j]].x_middle)*(tower[j].x_middle - e[attack_enemy_id[j]].x_middle) + (tower[j].y_middle - e[attack_enemy_id[j]].y_middle)*(tower[j].y_middle - e[attack_enemy_id[j]].y_middle)));
		   attack_id[j] = attack_id_temp;
	   }
   }
   
   public double getTheta(int j){ // Returns the value for the angle the gun must rotate
	   if (e[attack_enemy_id[j]].y_middle <= tower[j].y_middle){
		   if (e[attack_enemy_id[j]].x_middle <= tower[j].x_middle){
			   theta[j] = Math.asin((tower[j].x_middle - e[attack_enemy_id[j]].x_middle)/attack_id[j]);
		   }
		   else if(e[attack_enemy_id[j]].x_middle > tower[j].x_middle){
			   theta[j] = -Math.acos((tower[j].y_middle - e[attack_enemy_id[j]].y_middle)/attack_id[j]);
		   }
	   }
	   else if (e[attack_enemy_id[j]].y_middle >= tower[j].y_middle){
		   if (e[attack_enemy_id[j]].x_middle <= tower[j].x_middle){
			   theta[j] = Math.acos((tower[j].y_middle - e[attack_enemy_id[j]].y_middle)/attack_id[j]);
		   }
		   else if(e[attack_enemy_id[j]].x_middle >= tower[j].x_middle){
			   theta[j] = -Math.asin((tower[j].x_middle - e[attack_enemy_id[j]].x_middle)/attack_id[j]) + Math.PI;
		   }
	   }
	   return theta[j];
   }
   
   public void drawTower(){ // Draws the tower and attacks if necessary
	   for(int j = 1; j < t; j++){
		   if (attack_enemy_id[j] != -1){
			   attack_enemy_id[j] = attack_enemy_id[j];
		   }
		   else{
		   		attack_enemy_id[j] = -1;
	   	   }
		   t_sprite[j] = getImage(getDocumentBase(), tower[j].img);
		   t_sprite_gun[j] = getImage(getDocumentBase(), tower[j].img_gun);
		   backg.drawImage(t_sprite[j], tower[j].x_pos, tower[j].y_pos, this);
		   t_sprite2D.drawImage(t_sprite_gun[j], tx[j], this);
		   this.closestEnemy(j, attack_id[j]);
		   if(attack_enemy_id[j] == -1){
			   attack_count[j] = -1;
		   }
		   else if(attack_id[j] < tower[j].range)
		   {			   theta[j] = getTheta(j);
			   delta_theta[j] = old_theta[j] - theta[j];
			   tx[j].rotate(delta_theta[j], tower[j].x_middle - tower[j].x_pos, tower[j].y_middle - tower[j].y_pos);
			   old_theta[j] = theta[j];
		   }
		   if(attack_enemy_id[j] != -1 && attack_count[j] % tower[j].rate_of_fire >= 0 && attack_count[j] % tower[j].rate_of_fire <= 10 && attack_id[j] < tower[j].range){
			   tower[j].attack(e[attack_enemy_id[j]], attack_count[j]);
			   tower[j].attack_animation(e[attack_enemy_id[j]].x_middle , e[attack_enemy_id[j]].y_middle, attack_count[j], backg);
			   if (e[attack_enemy_id[j]].current_hp > 0){
				   destroyed[j] = false;
			   }
			   else{
				   destroyed[j] = true;
			   }
		   }
		   attack_count[j]++;
	   }
   }
   
   public void primary() {
	   frame_count++;
	   if (frame_count % 25 == 0 && i <= 15){
		   e[i] = new Enemy("ship", 250);
		   i++;
	   }
	   backg.drawImage(map, 0, 0, this);
	   this.drawEnemy();
	   this.drawTower();
	   backg.drawImage(getImage(getDocumentBase(), "turret.png"), mouse_mx, mouse_my, this);
	   backg.drawOval(mouse_mx - radius, mouse_my - radius, radius*2 + 15, radius*2 + 15);
   }
   public void stop() {
	        //music.stop();    //Stop the sound loop.
	}

   public void update( Graphics g ) {
	   		g.drawImage( backbuffer, 0, 0, this );
	   }

	   public void paint( Graphics g ) {
	       update( g );
	   }
}
