/***********************************************************************

GECCO, a platform for command and control gaming

Copyright (C) 2001 Henrik Bäärnhielm, Andreas Enblom, Niklas
Hallenfur, Karl Hasselström, Henrik Hägerström, Oskar Linde, Jing Fu
Zi and Jon Åslund

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

***********************************************************************/

package gecco.client;

import java.awt.*;
import java.util.*;

/**
 *  Represents a piece (unit) on the game board. A Piece has several properties;
 *  a unique identifier (handle), position, name and type. <p>
 *
 *  Every new instance must be given a handle. The name is a descriptive String.
 *  The type determines what symbol to use. <p>
 *
 *  A Piece has several so called Properties. A property contains of a name and
 *  value. Every Piece is assumed to only have one Property of a given name. <p>
 *
 *  The Piece is linked to zero or more Actions; things that this specific piece
 *  can do. A Piece has a queue of action that are to be executed in order, and
 *  at most one action that is currently executed. <p>
 *
 *  A piece contains a number of messages that describe things that has happened
 *  to the piece. <p>
 *
 *  A Piece can be marked as selected by a boolean. Selected piece will be
 *  painted with a red rectangle around it.
 *
 * @author     Andreas Enblom
 * @created    June 27, 2001
 */
public class Piece {

    /**
     *  Description of the Field
     */
    final int handle;
    /**
     *  Description of the Field
     */
    Position position = new Position();
    /**
     *  Description of the Field
     */
    String name;
    /**
     *  Description of the Field
     */
    String type;
    /**
     *  Description of the Field
     */
    boolean selected = false;

    /**
     *  Description of the Field
     */
    Hashtable properties = new Hashtable();
    /**
     *  Description of the Field
     */
    Vector messages = new Vector();
    /**
     *  Description of the Field
     */
    Vector actions = new Vector();
    /**
     *  Description of the Field
     */
    ActionQueue actionQueue = new ActionQueue();
    /**
     *  Description of the Field
     */
    Action currentAction = null;
    /**
     *  The maximum number of messages.
     */
    public final static int MAX_MESSAGES = 40;


    /**
     *  Constructor for the Piece object
     *
     * @param  handle  Description of Parameter
     */
    public Piece(int handle) {
        this(handle, new Position(0, 0), "", "");
    }


    /**
     *  Constructor for the Piece object
     *
     * @param  handle    Description of Parameter
     * @param  position  Description of Parameter
     * @param  name      Description of Parameter
     * @param  type      Description of Parameter
     */
    public Piece(int handle, Position position,
            String name, String type) {
        this.handle = handle;
        setPosition(position);
        setName(name);
        setType(type);
    }


    /**
     *  Sets the Position attribute of the Piece object
     *
     * @param  p  The new Position value
     */
    public void setPosition(Position p) {
        position = new Position(p);
    }


    /**
     *  Sets the Name attribute of the Piece object
     *
     * @param  s  The new Name value
     */
    public void setName(String s) {
        name = new String(s);
    }


    /**
     *  Sets the Type attribute of the Piece object
     *
     * @param  s  The new Type value
     */
    public void setType(String s) {
        type = new String(s);
    }


    /**
     *  Sets the Selected attribute of the Piece object
     *
     * @param  b  The new Selected value
     */
    public void setSelected(boolean b) {
        selected = b;
    }


    /**
     *  Adds the given property to the properties of this object. If a property
     *  with such a name already is linked to this piece, it will be replaced.
     *
     * @param  p  The new Property value
     */
    public void setProperty(Property p) {
        properties.put(p.getName(), p);
    }


    /**
     *  Adds a property with the given name and value to the properties of this
     *  object. If a property with such a name already is linked to this piece,
     *  it will be replaced.
     *
     * @param  name   The new Property value
     * @param  value  The new Property value
     */
    public void setProperty(String name, String value) {
        setProperty(new Property(name, value));
    }


    /**
     *  Gets the Handle attribute of the Piece object
     *
     * @return    The Handle value
     */
    public int getHandle() {
        return handle;
    }


    /**
     *  Gets the Position attribute of the Piece object
     *
     * @return    The Position value
     */
    public Position getPosition() {
        return new Position(position);
    }


    /**
     *  Gets the Symbol attribute of the Piece object
     *
     * @return    The Symbol value
     */
    public Image getSymbol() {
        return Symbols.getPieceSymbol(getType());
    }


    /**
     *  Gets the Name attribute of the Piece object
     *
     * @return    The Name value
     */
    public String getName() {
        return new String(name);
    }


    /**
     *  Gets the Type attribute of the Piece object
     *
     * @return    The Type value
     */
    public String getType() {
        return new String(type);
    }


    /**
     *  Gets the Selected attribute of the Piece object
     *
     * @return    The Selected value
     */
    public boolean isSelected() {
        return selected;
    }


    /**
     *  Returns all actions that this piece can do.
     *
     * @return    The Actions value
     */
    public Enumeration getActions() {
        return actions.elements();
    }


    /**
     *  Returns the property with the given name. If no such property exists,
     *  null is returned.
     *
     * @param  name  Description of Parameter
     * @return       The Property value
     */
    public Property getProperty(String name) {
        Property p = null;
        try {
            p = new Property((Property) properties.get(name));
        } catch (Exception ex) {
        }
        return p;
    }


    /**
     *  Gets the Messages attribute of the Piece object
     *
     * @return    The Messages value
     */
    public Enumeration getMessages() {
        return messages.elements();
    }


    /**
     *  Gets the NumberOfMessages attribute of the Piece object
     *
     * @return    The NumberOfMessages value
     */
    public int getNumberOfMessages() {
        return messages.size();
    }


    /**
     *  Returns an array containing the properties of this Piece. The array is
     *  sorted.
     *
     * @return    The Properties value
     */
    public Property[] getProperties() {
        Property[] props = new Property[properties.size()];
        Enumeration e = properties.elements();
        for (int i = 0; i < props.length; ++i) {
            try {
                props[i] = new Property((Property) e.nextElement());
            } catch (Exception ex) {
                props[i] = null;
            }
        }
        Arrays.sort(props);
        return props;
    }


    /**
     *  Returns all actions that is currently queued for this piece. This does
     *  not include the currently executing action.
     *
     * @return    The AllQueuedActions value
     */
    public Vector getAllQueuedActions() {
        return actionQueue.actions();
    }


    /**
     *  Returns the currently executing action, or null if no action is
     *  executing.
     *
     * @return    The ExecutingAction value
     */
    public Action getExecutingAction() {
        return currentAction;
    }


    /**
     *  Description of the Method
     *
     * @param  o  Description of Parameter
     * @return    Description of the Returned Value
     */
    public boolean equals(Object o) {
        try {
            return (((Piece) o).getHandle() == handle);
        } catch (Exception ex) {
            return false;
        }
    }


    /**
     *  Description of the Method
     *
     * @return    Description of the Returned Value
     */
    public int hashCode() {
        return handle;
    }


    /**
     *  Adds the given actions to the list of available actions of this piece.
     *  It is only the type of the actions that is of interest here.
     *
     * @param  a  The feature to be added to the Action attribute
     */
    public void addAction(Action a) {
        if (!actions.contains(a)) {
            actions.addElement(a);
        }
    }


    /**
     *  Removes any action that has the same type as the given action from the
     *  list of available actions of this piece. If there are such actions
     *  executing, the caller has to make sure that they are removed.
     *
     * @param  a  Description of Parameter
     */
    public void removeAction(Action a) {
        actions.removeElement(a);
    }


    /**
     *  Removes all actions that indicates what this piece can do.
     */
    public void removeAllActions() {
        actions.removeAllElements();
    }


    /**
     *  Removes the property with the given name from this Piece.
     *
     * @param  name  Description of Parameter
     */
    public void removeProperty(String name) {
        properties.remove(name);
    }


    /**
     *  Adds a feature to the Message attribute of the Piece object
     *
     * @param  msg  The feature to be added to the Message attribute
     */
    public void addMessage(String msg) {
        if ((msg == null) || (msg.equals(""))) {
            return;
        }
        while (messages.size() >= MAX_MESSAGES) {
            messages.removeElementAt(0);
        }
        messages.add(msg);
    }


    /**
     *  Queues the given action, allowing it to be executed after the other
     *  actions has finished.
     *
     * @param  a  Description of Parameter
     */
    public void queue(Action a) {
        actionQueue.put(a);
    }


    /**
     *  Description of the Method
     *
     * @param  a  Description of Parameter
     */
    public void abortQueuedAction(Action a) {
        actionQueue.remove(a);
    }


    /**
     *  Removes all executing actions of this piece (both queued and currenlty
     *  executing). There is no connection with the server.
     */
    public void abortAllExecutingActions() {
        actionQueue.empty();
        currentAction = null;
    }


    /**
     *  Lets the next action execute. Returns the Action to execute. If no more
     *  actions are to be executed, null is returned. There is no connection
     *  with the server.
     *
     * @return    Description of the Returned Value
     */
    public Action executeNextAction() {
        Action a = actionQueue.get();
        currentAction = a;
        return a;
    }


    /**
     *  Paints the symbol on the given graphics, within the given bounding
     *  rectangle.
     *
     * @param  g       Description of Parameter
     * @param  bounds  Description of Parameter
     */
    public void paint(Graphics g, Rectangle bounds) {
        Image symbol = getSymbol();
        if (symbol == null) {
            return;
        }
        g.drawImage(symbol, bounds.x,
                bounds.y, bounds.width,
                bounds.height, null);
        if (isSelected()) {
            Color oldColor = g.getColor();
            g.setColor(Color.red);
            g.drawRect(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
            g.drawRect(bounds.x + 1, bounds.y + 1, bounds.width - 3, bounds.height - 3);
            g.setColor(oldColor);
        }
    }
}
