import greenfoot.*;
import java.lang.Math.*;

/**
 * Gun: shoots (i. e. emits) missiles (like a Factory). Handles shooting timeouts.
 * Can be put into a spaceship.
 * 
 * @author Jannis Andrija Schnitzer, Martin Schend
 * @version 2011-01-22
 */
public class Gun  
{
    /**
     * This variable determines how many turns the gun has to wait before it may emit another bullet.
     * It is counted down; when it reaches 0, we can shoot. Otherwise, the gun will just shoot out
     * thin air (i. e., a null pointer) when asked for a missile.
     * 
     * @see #missile()
     */
    protected int timer;
    
    /**
     * The number to which timer is set after a bullet has been launched. (I. e., the number of turns
     * that will pass between two missiles.
     */
    protected int recovery;
    
    /**
     * Speed the missiles are set to when launched.
     */
    protected Vector speed;
    /**
     * Side the gun (and so the missiles) is on.
     */
    protected int side;
    /**
     * Level of the gun (and thus of the missiles).
     */
    protected int level;
    
    /**
     * X distance of launched bullets from spaceship center.
     */
    protected int dx;
    /**
     * @see #dx
     */
    protected int dy;
    
    /**
     * Absolute value of the speed vector that's set to missiles.
     */
    protected double abs_speed;
    
    /**
     * Angle the missiles are launched with (to y axis, value in radians).
     */
    protected double angle;
    
    /**
     * Direction the gun is facing. Allows for easier dy calculation in subclasses.
     * 
     * @see #dy
     */
    protected int direction;
    
    /**
     * Initial recovery time of this kind of gun. (Without alteration by FasterGuns, that is.)
     */
    public static int initial_recovery;

    public Gun(double the_abs_speed, double the_angle)
    {
        this.setSpeed(the_abs_speed, the_angle);
        init();
    }
    public Gun(Vector the_speed)
    {
        this.setSpeed(the_speed);
        init();
    }
    public Gun()
    {
        this(1.0, 0.0);
    }
    /**
     * Return an exact copy of this gun. Used especially in the MoreGuns powerup.
     * TODO: make this more dynamic.
     * 
     * @see MoreGuns
     */
    public Gun copy_gun() {
        Gun g = new Gun(abs_speed, angle);
        g.setRecovery(recovery);
        g.setLevel(level);
        return g;
    }
    protected void init()
    {
        timer = 0;
        recovery = 10;
        initial_recovery = -1; // *coughs*
        
        dx = 0;
        dy = 0;
        if (abs_speed < 0.0)
            direction = -1;
        else
            direction = 1;
        
        level = 1;
    }
    
    public void setSpeed(Vector the_speed)
    {
        speed = new Vector(the_speed);
        abs_speed = speed.length();
        angle = Math.asin(speed.getX()/abs_speed);
    }
    public void setSpeed(double the_abs_speed, double the_angle)
    {
        abs_speed = the_abs_speed;
        angle = the_angle;
        speed = new Vector(abs_speed * Math.sin(angle), -abs_speed * Math.cos(angle));        
    }

    /**
     * Send a tick to the gun every time act() is called in the containing
     * spaceship. This is used to handle the internal firing timer.
     */
    public void tick()
    {
        timer -= 1;
    }
    
    protected Missile get_missile()
    {
        return new Missile();
    }
    
    /**
     * Emit a new bullet or null if not ready yet.
     * 
     * @return     the bullet or null
     */
    public Missile missile()
    {
        if (timer <= 0) {
            Missile m = this.get_missile();
            m.setSpeed(speed);
            m.setSide(side);
            m.setRotation((int)Math.toDegrees(angle));
            timer = recovery;
            return m;
        }
        else
        {
            return null;
        }
    }
    
    public int get_dx()
    {
        return dx;
    }
    public int get_dy()
    {
        return dy;
    }
    public void set_dx(int new_dx)
    {
        dx = new_dx;
    }
    public void set_dy(int new_dy)
    {
        dy = new_dy;
    }
    
    public int getSide()
    {
        return side;
    }
    public void setSide(int new_side)
    {
        side = new_side;
    }
    
    public int getTimer()
    {
        return timer;
    }
    public void setTimer(int timer_value)
    {
        timer = timer_value;
    }

    public int getLevel()
    {
        return level;
    }
    public void setLevel(int the_level) {
        level = the_level;
    }
    
    public int getRecovery()
    {
        return recovery;
    }
    public void setRecovery(int the_recovery)
    {
        recovery = the_recovery;
    }
    
    public double getAbsSpeed()
    {
        return abs_speed;
    }
    public double getAngle()
    {
        return angle;
    }
    
    public static int getInitialRecovery()
    {
        return initial_recovery;
    }
    public static void setInitialRecovery(int the_recovery)
    {
        initial_recovery = the_recovery;
    }
}
