/*
 * @(#)Worm.java	1.4 04/01/27
 *
 * Copyright (c) 2000-2004 Sun Microsystems, Inc. All rights reserved. 
 * PROPRIETARY/CONFIDENTIAL
 * Use is subject to license terms
 */

/*
 * Worm.java
 *
 * Created on March 30, 2001, 16:15
 * @version 1.4
 */

package example.wormgame;

import java.util.Vector;
import javax.microedition.lcdui.Graphics;

/**
 * A Worm is a combination of one or more WormLinks. The first
 * WormLink is the physical tail of the worm. The last WormLink
 * is the physical head of the worm. As the worm moves, it adds
 * one space to the head and then removes one from the tail. If the
 * worm eats a piece of food, the head adds one space but the tail
 * will not remove one. In this manner, the worm will grow outward
 * from its head.
 */
public class Worm {

    /* Possible directions for the worm */
    public final static byte DOWN  = 2;
    public final static byte LEFT  = 4;
    public final static byte RIGHT = 6;
    public final static byte UP    = 8;


    // The direction of the worm's head
    private byte currentDirection;

    // The individual worm links that form the worm
    private Vector worm = new Vector(5, 2);

    private boolean needUpdate;
    private boolean moveOnNextUpdate;
    private boolean hasEaten;

    private final static int INIT_X = 3;
    private final static int INIT_Y = 8;
    private final static int INIT_LEN = 8;
    private final static byte INIT_DIR = RIGHT;

    public Worm(WormPit pit) {
    regenerate();
    }

    /**
     * Regenerate the worm in its initial position. This is used
     * to restart the game after the worm is killed.
     */
    public void regenerate() {
    synchronized (worm) {
        worm.removeAllElements();
        worm.addElement(new WormLink(INIT_X, INIT_Y,
                       INIT_LEN, INIT_DIR));

        // Reset class variables
        currentDirection = INIT_DIR;
        needUpdate = false;
        hasEaten = false;
        moveOnNextUpdate = false;
    }
    }

    /**
     * Change the direction of the worm. It will only change
     * directions if you set a direction that is 90 degrees
     * different from the current direction of the worm.
     */
    public void setDirection(byte direction) {
    synchronized (worm) {
        if ((direction != currentDirection) && !needUpdate) {
        WormLink sl = (WormLink)worm.lastElement();
        int x = sl.getEndX();
        int y = sl.getEndY();
        switch (direction) {
        case UP:
            if (currentDirection != DOWN) {
            y--; needUpdate = true;
            }
            break;
        case DOWN:
            if (currentDirection != UP) {
            y++; needUpdate = true;
            }
            break;
        case LEFT:
            if (currentDirection != RIGHT) {
            x--; needUpdate = true;
            }
            break;
        case RIGHT:
            if (currentDirection != LEFT)  {
            x++; needUpdate = true;
            }
            break;
        }
        if (needUpdate == true) {
            worm.addElement(new WormLink(x, y, 0, direction));
            currentDirection = direction;
        }
        }
    }
    }


    /**
     * Schedule the worm for growth. The next time the update()
     * method is invoked, the worm will move forward by one cell.
     */
    public void moveOnUpdate() {
    synchronized (worm) {
        moveOnNextUpdate = true;
    }
    }

    /**
     * Update the worm. If the worm is scheduled to grow, this
     * method will grow the worm head by one cell and potentially
     * shrink the tail by one cell. If the worm has eaten a piece
     * of food, the tail will not shrink.
     *
     * If the worm is not scheduled to grow, this method will
     * do nothing.
     */
    public void update(Graphics g) throws WormException {
    WormLink head, sl;
    int headX, headY;

    if (!moveOnNextUpdate) {
        return;
    }

    synchronized (worm) {

        head = (WormLink)worm.lastElement();          // the worm 'head'
        head.increaseLength();

        if (!hasEaten) {
        WormLink tail;
        tail = (WormLink)worm.firstElement();     // the worm 'tail'
        int tailX = tail.getX();
        int tailY = tail.getY();

        tail.decreaseLength();
        if (tail.getLength() == 0) {
            worm.removeElement(tail);
        }

        // Clear last block of the tail
        g.setColor(WormPit.ERASE_COLOUR);
        drawLink(g, tailX, tailY, tailX, tailY, 1);

        } else {
        hasEaten = false;
        }

        needUpdate = false;

        // Make sure we're still in bounds
        if (!WormPit.isInBounds(head.getEndX(), head.getEndY())) {
        throw new WormException("over the edge");  // You're dead Jim
        }

        headX = (byte)head.getEndX();
        headY = (byte)head.getEndY();

        // Draw the head
        g.setColor(WormPit.DRAW_COLOUR);
        drawLink(g, headX, headY, headX, headY, 1);

        // See if we ate ourself
        for (int i = 0; i < worm.size()-1; i++) {
        sl = (WormLink)worm.elementAt(i);
        if (sl.contains(headX, headY)) {
            throw new WormException("you ate yourself");
        }
        }

    }
    }

    /**
     * Draw a worm link.
     */
    public void drawLink(Graphics g, int x1, int y1, int x2, int y2, int len) {
    // Convert coordinates to the drawing area
    len *= WormPit.CELL_SIZE;
    if (x1 == x2) {
        x1 *= WormPit.CELL_SIZE;
        if (y2 < y1) {
        y1 = y2 * WormPit.CELL_SIZE;
        } else {
        y1 *= WormPit.CELL_SIZE;
        }
        g.fillRect(x1, y1, WormPit.CELL_SIZE, len);
    } else {
        y1 *= WormPit.CELL_SIZE;
        if (x2 < x1) {
        x1 = x2 * WormPit.CELL_SIZE;
        } else {
        x1 *= WormPit.CELL_SIZE;
        }
        g.fillRect(x1, y1, len, WormPit.CELL_SIZE);
    }
    }

    /**
     * Draw the entire worm. Logically, the worm is composed of
     * one or more links. A worm is usually composed of a few
     * straight segments. This allows us to paint a full worm
     * with a minimum of draw calls.
     */
    public void paint(Graphics g) {
    WormLink sl;
    int x1, x2, y1, y2;
    int len;

    for (int i = 0; i < worm.size(); i++) {
        sl = (WormLink)worm.elementAt(i);
        x1 = sl.getX();    x2 = sl.getEndX();
        y1 = sl.getY();    y2 = sl.getEndY();
        len = sl.getLength();
        drawLink(g, x1, y1, x2, y2, len);
    }
    }

    /**
     * Signals the worm to eat a food item
     */
    public void eat() {
    hasEaten = true;
    }

    /**
     * Get the X coordinate of the cell that contains the worm's head.
     */
    public int getX() {
    synchronized (worm) {
        return ((WormLink)worm.lastElement()).getEndX();
    }
    }

    /**
     * Get the Y coordinate of the cell that contains the worm's head.
     */
    public int getY() {
    synchronized (worm) {
        return ((WormLink)worm.lastElement()).getEndY();
    }
    }

    /**
     * Returns true if the worm contains the given cell coordinate.
     */
    public boolean contains(int x, int y) {
    WormLink sl;
    synchronized (worm) {
        for (int i = 0; i < worm.size(); i++) {
        sl = (WormLink)worm.elementAt(i);
        if (sl.contains(x, y)) {
            return true;
        }
        }
    }
    return false;
    }

}

