/**
 * Copyright 2001 Jean-Francois Doue
 *
 * This file is part of Asteroid Zone. Asteroid Zone is free software;
 * you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or (at your option) any later version.
 * Asteroid Zone is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Asteroid Zone; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 */

package asteroids;

import javax.microedition.lcdui.*;
import java.util.*;

/**
 * Class to implement the player's spaceship. The spaceship
 * can rotate, move, shoot and teleport itself.
 * @author Jean-Francois Doue
 * @version 1.4, 2002/10/14
 */
public class Ship extends Mobile {
    /**
     * False if the ship has been shot.
     */
    public boolean isAlive;

    /**
     * The orientation of the ship.
     */
    public byte angle;

    /**
     * The X coordinates of an array of points representing the ship
     * at various orientations.
     */
    public static byte[] xcoords;

    /**
     * The Y coordinates of an array of points representing the ship
     * at various orientations.
     */
    public static byte[] ycoords;

    /**
     * The size of the ship (in pixels).
     */
    public static byte radius;

    /**
     * The ship singleton
     */
    public static Ship ship;


    private byte _explosionFrame;
    private byte _latency;       // To limit the ship's firing power

    static {
        // Instantiate the ship.
        ship = new Ship();

        final byte[] pointx = {5,-3, 0,-3};
        final byte[] pointy = {0, 4, 0,-4};

        radius = (byte)(5 * ratioNum / ratioDenom);
        xcoords = new byte[128];
        ycoords = new byte[128];
        for (int i = 0; i < 32; i++) {
            int offset = i << 2;
            for (int j = 0; j < 4; j++) {
                xcoords[offset + j] = (byte)(((pointx[j] * cos[i] - pointy[j] * sin[i]) * ratioNum / ratioDenom) >> 6);
                ycoords[offset + j] = (byte)(((pointx[j] * sin[i] + pointy[j] * cos[i]) * ratioNum / ratioDenom) >> 6);
            }
        }
    }

    public Ship() {
    }

    /**
     * Positions the ship at its original location, orientation
     * and velocity.
     */
    public final void reset() {
        angle = 0;
        moveTo(Mobile.width >> 1, Mobile.height >> 1);
        vx = 0;
        vy = 0;
        isAlive = true;
        rotate(0);
    }

    /**
     * Called when the ship collides with an asteroid.
     */
    public final void explode() {
        isAlive = false;
        _explosionFrame = 0;
    }

    /**
     * Move the ship to its next position.
     */
    public final void move() {
        if (isAlive) {
           xold = x;
           yold = y;
           _x += vx;
           _y += vy;
           x = _x >> 8;
           y = _y >> 8;

           // If a border has been hit, wrap the trajectory around
           // the screen. The new origin is the projection of the
           // intersection point on the opposite border.
           if (x <= 0) {
               moveTo(width - 2, y);
           } else if (x >= width - 1) {
               moveTo(1, y);
           } else if (y <= 0) {
               moveTo(x, height - 2);
           } else if (y >= height - 1) {
               moveTo(x, 1);
           }

            if (_latency > 0) {
                _latency--;
            }
         } else {
            _explosionFrame++;
            if (_explosionFrame > 20) {
                reset();
            }
        }
    }

    /**
     * Changes the orientation of the ship.
     */
    public final void rotate(int direction) {
        if (isAlive) {
            angle += direction;
            if (angle > 31) {
            angle -= 32;
         } else if (angle < 0) {
            angle += 32;
         }
        }
    }

    /**
     * Adds velocity to the ship.
     */
    public final void burn() {
        if (isAlive) {
         int newvx = vx + (cos[angle] << 1);
         int newvy = vy + (sin[angle] << 1);
            // clamp the ship velocity.
            if (newvx * newvx + newvy * newvy < 262144) {
                vx = newvx;
                vy = newvy;
            }
        }
    }

    /**
     * Shoots a rocket.
     */
    public final Rocket shoot(Pool rockets) {
        if (isAlive) {
            if (_latency == 0) {
                Rocket rocket = (Rocket)rockets.addNewObject();
                if (rocket != null) {
                    int offset = angle << 2;
                    rocket.init(xcoords[offset] + x, ycoords[offset] + y, angle, vx, vy);

                    // Prevent the ship for shooting again for 3 frames
                    _latency = 3;
                }
            }
        }
        return null;
    }

    /**
     * Teleports the ship to another lcoation.
     */
    public final void teleport() {
        if (isAlive) {
            moveTo(Math.abs(Game.random.nextInt()) % width, Math.abs(Game.random.nextInt()) % height);
        }
    }

    /**
     * Draws the ship at the specified location and orientation.
     */
    public static void draw(int orientation, int xpos, int ypos, Graphics g) {
        int offset = orientation << 2;
        g.drawLine(xcoords[offset] + xpos, ycoords[offset] + ypos,
                   xcoords[offset + 1] + xpos, ycoords[offset + 1] + ypos);
        g.drawLine(xcoords[offset + 1] + xpos, ycoords[offset + 1] + ypos,
                   xcoords[offset + 2] + xpos, ycoords[offset + 2] + ypos);
        g.drawLine(xcoords[offset + 2] + xpos, ycoords[offset + 2] + ypos,
                   xcoords[offset + 3] + xpos, ycoords[offset + 3] + ypos);
        g.drawLine(xcoords[offset + 3] + xpos, ycoords[offset + 3] + ypos,
                   xcoords[offset + 0] + xpos, ycoords[offset + 0] + ypos);
    }

    /**
     * Draws the ship in the specified graphic context.
     */
    public final void draw(Graphics g) {
        if (isAlive) {
            draw(angle, x, y, g);
        } else {
            if (_explosionFrame < 6) {
                g.setColor(255, 0, 0);
                int radius = _explosionFrame;
                g.drawArc(x - radius, y - radius, radius << 1, radius << 1, 0, 360);
                radius = _explosionFrame + 2;
                g.drawArc(x - radius, y - radius, radius << 1, radius << 1, 0, 360);
                radius = _explosionFrame + 4;
                g.drawArc(x - radius, y - radius, radius << 1, radius << 1, 0, 360);
            }
        }
    }
}