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

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.server.core;

import gecco.server.automaton.*;

/**
 *  This class represents an Action that a unit can execute, and every real
 *  action should be a subclass of this class. There are several kinds of
 *  actions. First, there is normal actions, that are executed one after another
 *  in a queue, only one at a time, and there are instantaneous actions, that
 *  are executed at once, even if there are some other normal action ongoing.
 *  The normal actions typically span some interval in time, like movement of
 *  units, and the instantaneous don't, they are just instantaneous. :) Second,
 *  actions can have different arguments. At the moment, the arguments can be a
 *  unit, a point on the map, or nothing. Normal actions are executed over a
 *  time interval, and at some points in time the action has checkpoints when
 *  the method with the same name is called by the game engine. That method is
 *  where most of the code of the action is placed, for example code to check if
 *  the action is completed, and code to update any status of the unit executing
 *  the action, and so on.
 *
 * @author     Henrik Bäärnhielm, Karl Hasselström, Henrik Hägerström
 * @created    April 30, 2001
 */
public abstract class Action {

    /**
     *  The unique handle of the action.
     */
    private int actionHandle;

    /**
     *  The unit that executes the action.
     */
    private Unit unit = null;

    /**
     *  The unit that is the target of the action, if appropriate.
     */
    private Unit argUnit = null;

    /**
     *  The coordinates of the point that is the target of this action, if
     *  appropriate.
     */
    private double argX = -1;
    /**
     *  Description of the Field
     */
    private double argY = -1;

    /**
     *  Flag to indicate if this action is instantaneous.
     */
    private boolean isInstantaneous = false;


    /**
     *  Constructor for the Action object
     */
    public Action() {
    }


    /**
     *  Sets the Instantaneous flag of the Action object
     *
     * @param  b  The new Instantaneous value
     */
    public void setInstantaneous(boolean b) {
        isInstantaneous = b;
    }


    /**
     *  Gets the Instantaneous flag of the Action object
     *
     * @return    The Instantaneous flag
     */
    public boolean isInstantaneous() {
        return isInstantaneous;
    }


    /**
     *  Gets the handle of the Action object
     *
     * @return    The handle
     */
    public int getActionHandle() {
        return actionHandle;
    }


    /**
     *  Gets the handle of the unit that executes this action
     *
     * @return    The unit handle
     */
    public int getUnitHandle() {
        return unit.getHandle();
    }


    /**
     *  Gets the unit that executes this action
     *
     * @return    The unit
     */
    public Unit getUnit() {
        return unit;
    }


    /**
     *  Gets the unit that is the target of this action.
     *
     * @return    The target unit, or null if there is no target unit.
     */
    public Unit getArgumentUnit() {
        return argUnit;
    }


    /**
     *  Gets the x coordinate of the point that is the target of this action.
     *
     * @return    The x coordinate of the target point, or -1 if there is no
     *      target point.
     */
    public double getArgumentX() {
        return argX;
    }


    /**
     *  Gets the y coordinate of the point that is the target of this action.
     *
     * @return    The y coordinate of the target point, or -1 if there is no
     *      target point.
     */
    public double getArgumentY() {
        return argY;
    }


    /**
     *  Initialise the action without any arguments.
     *
     * @param  _actionHandle  A unique handle that should identify this action.
     * @param  _unitHandle    The handle of the executing unit.
     */
    public void initiate(int _actionHandle, int _unitHandle) {
        actionHandle = _actionHandle;
        unit = ReferenceHolder.getUnitManager().getUnit(_unitHandle);
    }


    /**
     *  Initialise the action with a point as argument.
     *
     * @param  _actionHandle  A unique handle that should identify this action.
     * @param  _unitHandle    The handle of the executing unit.
     * @param  _argX          The x coordinate of the target point.
     * @param  _argY          The y coordinate of the target point.
     */
    public void initiate(int _actionHandle, int _unitHandle, double _argX, double _argY) {
        actionHandle = _actionHandle;
        unit = ReferenceHolder.getUnitManager().getUnit(_unitHandle);
        argX = _argX;
        argY = _argY;
    }


    /**
     *  Initialise the action with a unit as argument.
     *
     * @param  _actionHandle   A unique handle that should identify this action.
     * @param  _unitHandle     The handle of the executing unit.
     * @param  _argUnitHandle  The handle of the target unit.
     */
    public void initiate(int _actionHandle, int _unitHandle, int _argUnitHandle) {
        actionHandle = _actionHandle;
        unit = ReferenceHolder.getUnitManager().getUnit(_unitHandle);
        argUnit = ReferenceHolder.getUnitManager().getUnit(_argUnitHandle);
    }


    /**
     *  This method is called periodically by the game engine.
     *
     * @return    An object of type ActionReturn, which encapsulates data about
     *      the status of the action after the checkpoint. Different instances
     *      of the class ActionReturn should be returned depending on whether
     *      the action is completed, aborted, or if the action should be
     *      requeued and the checkpoint method called again at some time later.
     */
    public abstract ActionReturn checkPoint();


    /**
     *  Gets the state of the given automaton.
     *
     * @param  x  The x coordinate of the automaton.
     * @param  y  The y coordinate of the automaton.
     * @return    The state of the given automaton.
     */
    protected int getAutomatonState(int x, int y) {
        return ReferenceHolder.getAutomatonMap().getAutomaton(x, y).getState();
    }


    /**
     *  Sends an event to a given automaton.
     *
     * @param  x          The x coordinate of the automaton.
     * @param  y          The y coordinate of the automaton.
     * @param  eventName  The name of the event.
     * @param  factor     The factor of the event.
     */
    protected void sendEventToAutomaton(int x, int y, String eventName, int factor) {
        AutomatonEvent aE = new AutomatonEvent(eventName, factor);
        ReferenceHolder.getQManager().addEventToQueue(aE, x, y);
    }


    /**
     *  Sends an event to a given unit.
     *
     * @param  unit       The unit that should recieve the event.
     * @param  eventName  The name of the event.
     * @param  factor     The factor of the event.
     */
    protected void sendEventToUnit(Unit unit, String eventName, int factor) {
        UnitEvent unitEvent = new UnitEvent(eventName, factor);
        ReferenceHolder.getActionProcessor().handleUnitToUnitEvent(unit, unitEvent);
    }


    /**
     *  Determines if the unit that executes the action can see a certain
     *  position, that is, if the position is inside the units visibility range.
     *
     * @param  x  The x coordinate of the position.
     * @param  y  The y coordinate of the position.
     * @return    true if the unit can see the position, false otherwise.
     */
    protected boolean canSeePosition(double x, double y) {
        return ReferenceHolder.getVisibilityManager().canSeePosition(unit.getCommander(), x, y);
    }
}
