/Users/petercappello/NetBeansProjects/56-2014/56-2014-3-Zapem/src/Game.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.List;
import static java.lang.Math.*;
import java.util.ArrayList;

/**
 * Zapem game model
 *
 * @author Peter Cappello
 */
public class Game 
{

    /**
     * The edge length, in pixels, of the displayed Critter field.
     */
    public final static int IMAGE_SIZE = 600;
    private final static int MAX_DELTA = (int) sqrt( IMAGE_SIZE );
    private final static int MIN_DELTA = (int) sqrt( sqrt( IMAGE_SIZE ) );
    private final static int MAX_DELTA_SIZE = MAX_DELTA - MIN_DELTA;
    private final static int N_CRITTERS = 3;

    /**
     * The edge length, in pixels, of each Critter object.
     */
    protected final int EDGE_LENGTH = 60;
    
    private final Image image;
    private final Graphics graphics;
    private final Critter critterFactory;
    
    private List<Critter> critterList;
    private boolean isOver;
        
    Game() 
    {         
        image = new BufferedImage( IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_RGB );
        graphics = image.getGraphics();
        graphics.setColor( Color.white );
        graphics.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
        critterFactory = new SquareCritter();
        isOver = true;
    }
    
    /**
     * Gets a reference to the Image field.
     * @return a reference to the Image field.
     */
    public Image getImage() { return image; }
    
    /**
     * Starts a new game.
     */
    public void start()
    {
        isOver = false;
        createCritters();
    }
    
    private void createCritters()
    {
        critterList = new ArrayList<>();
        for ( int i = 0; i < N_CRITTERS; i++ )
        {
            critterList.add( critterFactory.makeCritter() );
        }
    }
        
    /**
     * Draws each Critter in its current position after blanking the image.
     */
    public void draw()
    {       
        graphics.setColor( Color.white );
        graphics.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
        for ( Critter critter : critterList )
        {
            critter.move();
            critter.draw( graphics );
        }
    }
    
    /**
     * Is the game over?
     * The definition of "game over" is that the Critter List is empty.
     * @return true if and only if the game is over.
     */
    public boolean isOver() { return isOver; }
    
    /**
     * Processes a mouse click.
     * Remove the 1st Critter for which the coordinates of the mouse click 
     * are within the Critter's boundary.
     * @param x horizontal coordinate of mouse click.
     * @param y vertical coordinate of mouse click.
     */
    public void processClick( int x, int y )
    {
        if ( isOver )
        {
            return;
        }
        for ( int i = 0; i < critterList.size(); i++ )
        {
            if ( critterList.get( i ).isIn( x, y ) )
            {
                critterList.remove( i );
                isOver =  critterList.isEmpty();
                break;
            }
        }
    }
    
    abstract private class Critter
    {
        protected int deltaX;
        protected int deltaY;
        protected Color color;        
        protected int x;
        protected int y;
        
        Critter makeCritter() 
        {
            switch ( (int) ( N_CRITTERS * random() ) )
            {
                case 0:
                    return new SquareCritter();
                    
                case 1:
                    return new RoundCritter();
                    
                case 2:
                    return new WowCritter();
                
                default:
                    return null;
            }
        }
        
        private Critter()
        {
            x = getRandomCoordinate();
            y = getRandomCoordinate();
            deltaX = getRandomDelta();
            deltaY = getRandomDelta();
            color  = getRandomColor();
        }
        
        private int getRandomCoordinate()
        {
            return (int) ( IMAGE_SIZE * random() );
        }
        
        private int getRandomDelta()   
        {
            int delta = (int) ( MAX_DELTA_SIZE * random() ) + MIN_DELTA;
            return random() < 0.5 ? delta : -delta;
        }
        
        protected final Color getRandomColor() 
        { 
            return new Color( (float) random(), (float) random(), (float) random() );
        }

        abstract void draw( Graphics graphics );
        
        void move()
        {
            int xNew =  x + deltaX;
            x = xNew > 0 ? xNew % IMAGE_SIZE : IMAGE_SIZE - xNew;
            int yNew =  y + deltaY;
            y = yNew > 0 ? yNew % IMAGE_SIZE : IMAGE_SIZE - yNew;
        }
        
        /**
         * Is the mouse (x, y) within this Critter's (x, y)?
         * @param x horizontal mouse coordinate
         * @param y vertical mouse coordinate
         * @return true if and only if 
         *         x is in [this.x, this.x + EDGE_LENGTH]
         * &amp; 
         *         y is in [this.y, this.y + EDGE_LENGTH]
         */
        boolean isIn( int x, int y )
        {
            return this.x <= x && x <= this.x + EDGE_LENGTH
                && this.y <= y && y <= this.y + EDGE_LENGTH;
        }
    }
    
    private class SquareCritter extends Critter
    {
        @Override
        void draw( Graphics graphics ) 
        {
            graphics.setColor( color );
            graphics.fillRect( x, y, EDGE_LENGTH, EDGE_LENGTH );
        }
    }
    
    private class RoundCritter extends Critter
    {
        @Override
        void draw( Graphics graphics ) 
        {
            graphics.setColor( color );
            graphics.fillOval( x, y, EDGE_LENGTH, EDGE_LENGTH );
        }
    }
    
    private class WowCritter extends Critter
    {
        @Override
        void draw( Graphics graphics ) 
        {
            color = getRandomColor();
            graphics.setColor( color );
            graphics.fillOval( x, y, EDGE_LENGTH, EDGE_LENGTH );
        }
    }
}