package gizmoball.game;

import gizmoball.NotImplementedException;
import physics.*;

/**
 * The absorber gizmo.  This represents a rectangular gizmo that can
 * contain and eject a ball gizmo.  The absorber's position is its
 * upper-left corner and it extends to the lower right based on its
 * size.
 *
 * <p>When a ball hits an absorber, the absorber stops the ball and
 * holds it (unmoving) in the bottom right-hand corner of the
 * absorber. The ball's center is .25L from the bottom of the absorber
 * and .25L from the right side of the absorber.
 *
 * <p>If the absorber is holding a ball, then the action of an
 * absorber, when it is triggered, is to shoot the ball straight
 * upwards in the direction of the top of the playing area.  The
 * initial velocity of the ball is be 50L/sec. (With the default
 * gravity and the default values for friction, the value of 50L/sec
 * gives the ball enough energy to lightly collide with the top wall,
 * if the bottom of the absorber is at y=20L.) If the absorber is not
 * holding the ball, or if the previously ejected ball has not yet
 * left the absorber, then the absorber takes no action when it
 * receives a trigger signal.
 *
 * <p>Absorbers cannot be rotated.
 *
 * <p>Currently, absorbers only support containing a single ball.
 *
 * @author <a href="mailto:amdragon@mit.edu">Austin Clements</a>
 * @version $Id: AbsorberGizmo.java,v 1.3 2004/04/27 20:58:51 amthrax Exp $
 */
public class AbsorberGizmo extends AbstractGizmoWithPolygonalGeometry {
    private static Vect EJECT = new Vect(0, -50);
    private static Vect BALL_OFFSET = new Vect(0.25, 0.25);

    private Vect size;

    private BallGizmo myBall = null;
    private boolean ejectNow = false;

    /**
     * Creates a new <code>AbsorberGizmo</code> instance.
     */
    public AbsorberGizmo() {
        super("Absorber", Vect.ZERO);
        setSize(new Vect(20, 2));
    }

    /**
     * Get the value of size
     *
     * @return the value of size
     */
    public Vect getSize() {
        return size;
    }

    /**
     * Set the value of size
     *
     * @param newVelocity Value to assign size
     */
    public void setSize(Vect newSize) {
        this.size = newSize;

        setGeometry(new Vect[] {Vect.ZERO, new Vect(newSize.x(), 0),
                                newSize, new Vect(0, newSize.y())},
                    Vect.ZERO);
        firePropertyObservers("Size", newSize);
    }

    /**
     * Set up the properties of the absorber gizmo
     *
     * @modifies properties
     * @effects adds the Size property
     */
    protected void setUpProperties() {
        super.setUpProperties();
        addProperty("Size");
    }

    /**
     * Does nothing
     */
    public boolean tick(double time) {
        if (myBall != null && false) {
            // Rule with an iron fist
            myBall.setPosition(getPosition().plus(size).minus(BALL_OFFSET));
            myBall.setVelocity(Vect.ZERO);
        }
        
        return false;
    }

    /**
     * Deal with collisions with another ball.  Captures the ball.
     */
    public void collide(AbstractGizmo other) {
        if (ejectNow) {
            // FIXME This is flaky
            if (myBall == null) {
                throw new RuntimeException("Trying to eject nothing");
            }
            
            myBall.setPosition(getPosition().plus(new Vect(size.x(), 0)).
                               minus(BALL_OFFSET));
            myBall.setVelocity(EJECT);
            myBall = null;
            ejectNow = false;
        } else if (other instanceof BallGizmo) {
            myBall = (BallGizmo)other;

            myBall.setPosition(getPosition().plus(size).minus(BALL_OFFSET));
            myBall.setVelocity(Vect.ZERO);
        }
    }

    /**
     * Fire the absorber's trigger.  If there is a ball in the
     * absorber, this causes it to be ejected.
     */
    public void fireTrigger(AbstractTrigger trigger) {
        if (myBall != null) {
            ejectNow = true;
        }
    }
}

