
/**
 * Space.java
 *
 *  the application server
 *  runs the simulation
 * 
 * Created: Fri Apr  7 08:41:03 2000
 *
 * @author root
 * @version
 */

import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.Vector;
import java.util.Enumeration;

public class Space implements Runnable {
  public static final int FrameWidth = 400;
  public static final int FrameHeight = 300;
  public static final int port = 31337;
  public static final int capacity = 10;    // at most 10 players allowed
  public static final int bulletSpeed = 2;  // how fast the bullet goes
  
  protected Vector shipContainer; // holds all our ships
  protected Vector bulletContainer; // holds all our bullets

  public Space() {
    shipContainer = new Vector (capacity);
    bulletContainer = new Vector (capacity);

    addShip (new Ship (50, 50, 20, 20, 0, 2, Color.blue, "player1911"));
    addShip (new Ship (100, 50, 20, 20, 3, 0, Color.green, "player9111"));
    addBullet (new Bullet (50, 50, 1, 1, "bullet1911"));
    addBullet (new Bullet (50, 50, 2, 1, "bullet9111")); 
  }

  public void run() {
    while (true) {
      Ship ship;
      Bullet bullet;
      
      for (int i = 0; i < ships(); i++) {
        ship = getShip (i);
        ship.move();                             // move the ship..
        
        ship.moveTo (mod (ship.x(), FrameWidth),
                     mod (ship.y(), FrameHeight)); // screen wrapping
      }
      
      for (int i = 0; i < bullets(); i++) {
        bullet = getBullet (i);
        bullet.move();
        
        if (bullet.x() < 0 ||
            bullet.y() < 0 ||
            bullet.x() > FrameWidth ||
            bullet.y() > FrameHeight)
          rmBullet (i);                      // bullet off screen --> deleted
      }
      
      collide();                         // check if any ship was shot down
      delay (150);
    }
  }

  private void resolveBullet (String playerID, Ship ship) {
    String bulletID = "bullet" + playerID.substring (6,10);
    for (int i = 0; i < bullets(); i++) {
      Bullet bullet = getBullet (i);
      if (bullet.bulletOwner().equals (bulletID)) 
        return;      // they already have bullet, we are done
    }
    // good it's a new bullet...so figure out bullet direction
    int xmid, ymid, x, y, dxBullet, dyBullet;
    double[][] p = new double[3][3];
    p = ship.dimensions();
    xmid = (int) ( ((p[0][0]) +  (p[2][0])) / 2 );
    ymid = (int) ( ((p[0][1]) +  (p[2][1])) / 2 );
    x =  (int) p[1][0];
    y =  (int) p[1][1];
    dxBullet = (int) ((x - xmid) / bulletSpeed); //  + (int) ship.xMotion();
    dyBullet = (int) ((y - ymid) / bulletSpeed); //  + (int) ship.yMotion();
    addBullet (new Bullet (x + ship.x(), y + ship.y(),
                           dxBullet, dyBullet,
                           bulletID));
  }

  protected void updatePlayer (String playerID,
                               int dx,
                               int dy,
                               int theta,
                               boolean isNewBullet) {
    for (int i = 0; i < ships(); i++ ) {
      Ship ship = getShip (i);
      if (ship.shipOwner().equals (playerID)) {
        ship.setMotion (dx, dy);                // update ship motion
        if (theta != 0)
          ship.rotate ((double) theta);           // change angle
        if (isNewBullet) {
          resolveBullet (playerID, ship);
          return;
        }
        else 
          return;       // player already has bullet, bail out
      }
    }
  }
    
  protected void addPlayer (String playerID, int x, int y, Color color) {
    // this method corresponds to the ADD command, defined in the protocol
    // a player is added by instantiating a ship for the player
    int dx = 0;
    int dy = 0;

    addShip (new Ship (x, y, 20, 20, dx, dy, color, playerID));
  }

  protected void deletePlayer (String playerID) {
    // delete player with playerID
    // also delete their bullet
    for (int i = 0; i < ships(); i++) {
      Ship ship = getShip(i);
      if (ship.shipOwner().equals (playerID))
        rmShip (i);
    }

    String bulletID = "bullet" + playerID.substring (6,10);
    for (int i = 0; i < bullets(); i++) {
      Bullet bullet = getBullet(i);
      if (bullet.bulletOwner().equals (bulletID))
        rmBullet (i);
    }    
  }

  public static void main (String[] args) throws IOException {
    Space space = new Space();
    new Thread (space).start();   // start the graphics stuff

    final int port = 31337;
    ServerSocket serverSocket = null;
    boolean listening = true;
    
    try {
      serverSocket = new ServerSocket (port);
      
      while (listening) 
        new MultiServerThread (serverSocket.accept(), space).start();

      System.out.println ("bye bye");
      serverSocket.close();  
    } catch (IOException e) {
      System.err.println ("Could not listen on port: " + port);
      System.exit(-1);
    }
  }
  
  private void collide() {           // check if bullet collided with ship..
    double[][] p = new double[3][3];
    int i, j;
    int lasti = shipContainer.size();
    int lastj = bulletContainer.size();
    Ship ship, winner;
    Bullet bullet;
    
    for (i = 0; i < lasti; i++ ) {
      Polygon outline = new Polygon();
      ship = getShip (i);  // get a ship
      p = ship.dimensions();    // get ship dimensions

      outline.addPoint ((int) p[0][0] + ship.x(), (int) p[0][1] + ship.y());
      outline.addPoint ((int) p[1][0] + ship.x(), (int) p[1][1] + ship.y());
      outline.addPoint ((int) p[2][0] + ship.x(), (int) p[2][1] + ship.y());

      for (j = 0; j < lastj; j++) {
        bullet = getBullet (j);
        if (outline.contains (bullet.x(), bullet.y())) {
          System.out.println (ship.shipOwner() +
                              "'s ship shot down by " +
                              bullet.bulletOwner() +
                              "'s bullet.");
          // find ship that eminated the bullet and add +1 to their score
          String winnerName = "player" +
            bullet.bulletOwner().substring(6,10);
          winner = getShip (winnerName);
          if (winner != null)
            winner.addScore();

          // erase ship that was shot down
          rmShip (i);

          // erase bullet that shot it down
          rmBullet (j);   
          lasti--;
          lastj--;
          break;
        }
      }
    }
  }

  // returns the # of ships in the container
  protected int ships () { return shipContainer.size(); }

  // return the # of bullets in the container
  protected int bullets () { return bulletContainer.size(); }

  // gets ship at position i, keeping it in the vector
  protected Ship getShip (int i) { return (Ship) shipContainer.elementAt (i); }

  // gets ship based on player name
  protected Ship getShip (String playerID) {
    if (playerID == null)
      return null;
    
    Ship ship;
    for (int i = 0; i < ships(); i++) {
      ship = getShip (i);
      if (ship.shipOwner().equals (playerID))
        return ship;
    }
    return null;
  }
  
  // gets bullet at position i, keeping it in the vector
  protected Bullet getBullet (int i) {
    return (Bullet) bulletContainer.elementAt (i);
  }

  // adds ship into the container
  protected void addShip (Ship ship) { shipContainer.addElement (ship); }

  // adds bullet into the container
  protected void addBullet (Bullet bullet) {
    bulletContainer.addElement (bullet);
  }

  // removes ship from container, at position pos, destroying it
  protected void rmShip (int pos) { shipContainer.removeElementAt (pos); }

  // remove bullet from container, at position pos, destroying it
  protected void rmBullet (int pos) { bulletContainer.removeElementAt (pos); }

  // a fix for the modulo operation to handle negative numbers
  // ex/  -3 % 10 = 7. assume b > 0
  private int mod (int a, int b) {
    if (a >= 0)
      return a % b;
    else 
      return b - (Math.abs (a) % b);
  }

  private void delay (int millis) {
    try { Thread.sleep (millis); } catch (Exception ignored) {}
  }


} // Space


