package spacewar;

import java.util.Random;

/**
 * Robot is an automatic pilot that now has quite a bit of intelligence.
 * So, beware !
 */
class Robot extends Pilot implements Runnable {

    private static final int FIRE_INTERVAL  = 60;
    private static final int REBIRTH_DELAY  = 900;

    private final Random random = new Random();

    private Thread  runner;
    private boolean runnable = true;

    Robot(Game theGame, int number) {
        super(theGame, number);
    }

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

    void destroy() {
        if (runner != null) {
            runnable = false;
            runner   = null;
        }
    }


    // A Robot tracks User-controlled ships and fires at them
    public void run() {
        Ship target = null;

        while(runnable) {
            // find target ship
            do {
                Ship[] potentials = getGame().getRegistry().getShips();
                if(potentials.length != 0)
                    target = potentials[Math.abs(random.nextInt() % potentials.length)];
                sleepForABit(25);
            } while (target == ship);
            // main loop
            int     currentRotation       = Ship.STOP;
            int     time;
            boolean currentlyAccelerating = false;
            double  dx, dy, angleA, angleB, theta, dtheta, d,
                targetVel, a, b, c, targetXVel, targetYVel;

            while(true) {
                sleepForABit(FIRE_INTERVAL);

                // if my ship is destroyed, give me a new one
                if (!ship.isAlive()) {
                    sleepForABit(REBIRTH_DELAY);
                    getGame().newShip(this);
                }

                // find direction and distance from target to me
                dx = ship.getXPos() - target.getXPos();
                if (dx < - getGame().getWidth() / 2)
                    dx += getGame().getWidth();
                if (dx > getGame().getWidth() / 2)
                    dx -= getGame().getWidth();
                dy = ship.getYPos() - target.getYPos();
                if (dy < - getGame().getHeight() / 2)
                    dy += getGame().getHeight();
                if (dy > getGame().getHeight() / 2)
                    dy -= getGame().getHeight();
                d = Math.sqrt(dx * dx + dy * dy);
                angleA = Math.atan(dy / dx);
                if (dx < 0)
                    angleA += Math.PI;

                // find relative velocity and trajectory of target
                targetXVel = target.getXVel() - ship.getXVel();
                targetYVel = target.getYVel() - ship.getYVel();
                targetVel = Math.sqrt(targetXVel * targetXVel +
                                      targetYVel * targetYVel);
                angleB = Math.atan(targetYVel / targetXVel);
                if (targetXVel < 0)
                    angleB+=Math.PI;

                // find angle between line to target and taget's direction of travel
                theta = (angleA - angleB) % (2 * Math.PI);
                if (theta < -Math.PI)
                    theta += 2 * Math.PI;
                if (theta > Math.PI)
                    theta -= 2 * Math.PI;

                // calculate time to bullet impact using law of cosines
                a = targetVel * targetVel + Ship.BULLET_SPEED * Ship.BULLET_SPEED;
                b = d * targetVel * Math.cos(theta);
                c = - d * d;
                time = (int)((-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a);

                // calculate angle and distance to bullet impact location
                dx = targetXVel * time - dx;
                dy = targetYVel * time - dy;
                theta = Math.atan(dy / dx);
                if(dx < 0)
                    theta += Math.PI;

                // find desired change in rotation
                dtheta = (theta - ship.getOrientation()) % (2 * Math.PI);
                // find the shortest path to the desired orientation;
                if(dtheta < - Math.PI)
                    dtheta += 2 * Math.PI;
                if(dtheta > Math.PI)
                    dtheta -= 2 * Math.PI;

                // turn if nessecary
                if (dtheta > Ship.DEFAULT_ANGULAR_VELOCITY / 2) {
                    if (currentRotation != Ship.CLOCKWISE)
                        ship.rotate(currentRotation = Ship.CLOCKWISE);
                }
                else if (dtheta < -Ship.DEFAULT_ANGULAR_VELOCITY / 2) {
                    if (currentRotation != Ship.COUNTERCLOCKWISE)
                        ship.rotate(currentRotation = Ship.COUNTERCLOCKWISE);
                } // otherwise, fire, maybe even a burst
                else {
                    if(currentRotation != Ship.STOP)
                        ship.rotate(currentRotation = Ship.STOP);
                    if (random.nextInt() % 40 == 0) {
                        ship.fire();
                    }
                }

                // randomly accelerate
                if (currentlyAccelerating && random.nextInt() % 2 == 0)
                    ship.thrust(currentlyAccelerating = false);
                else {
                    if (ship.getXVel() == 0)
                        angleA = 0;
                    else
                        angleA = Math.atan(ship.getYVel() / ship.getXVel());

                    if (ship.getXVel() < 0)
                        angleA+=Math.PI;
                    angleB = (angleA - ship.getOrientation()) % (2 * Math.PI);
                    if (angleB < -Math.PI)
                        angleB += 2 * Math.PI;
                    if (angleB > Math.PI)
                        angleB -= 2 * Math.PI;
                    angleB = Math.abs(angleB);

                    // angleB now represents the angle between the ship's
                    // orientation and velocity vector.  This will be used to
                    // determine the probably that the ship will thrust to
                    // prevent ships from accelerating too much in one direction
                    if (random.nextInt() % (int)(12 * (Math.PI - angleB) + 1) == 0)
                        ship.thrust(currentlyAccelerating = true);
                }

                // switch targets if current one has been destroyed
                if (target.getDamage() == 100)
                    break;

                // randomly switch targets
                if (random.nextInt() % 4000 == 0)
                    break;
            }
        }
    }

    void sleepForABit (int time) {
        try {
            runner.sleep(time);
        }
        catch (InterruptedException e) {}
    }
}
