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

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.*;
import gecco.server.core.*;
import gecco.server.unitmanager.*;
import java.awt.Color;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 *  Description of the Class
 *
 * @author     Unknown
 * @created    June 27, 2001
 */
public class ServerImpl implements Server {

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


    /**
     *  Returns the size of the map.
     *
     * @return    A Dimension object representing the size of the map.
     */
    public Dimension getMapSize() {
        int x = ReferenceHolder.getAutomatonMap().getXBound();
        int y = ReferenceHolder.getAutomatonMap().getYBound();
        return new Dimension(x, y);
    }


    /**
     *  Returns the map itself, in its original state.
     *
     * @return    An array of ints, representing the colours of the original
     *      map.
     */
    public byte[] getMap() {
        return ReferenceHolder.getAutomatonMap().getInitialMap();
    }


    /**
     *  Gets the UnitTypeIcons attribute of the ServerImpl object
     *
     * @return    The UnitTypeIcons value
     */
    public Map getUnitTypeIcons() {
        Map retMap = new HashMap();
        Iterator it = ReferenceHolder.getUnitManager().getUnitTypes().iterator();
        while (it.hasNext()) {
            UnitType uT = (UnitType) it.next();
            retMap.put(uT.getName(), uT.getImageData());
        }
        return retMap;
    }


    /**
     *  Orders an action that takes no parameters.
     *
     * @param  fromRole    the role that sent the order.
     * @param  actionId    an identifier for this action.
     * @param  unitHandle  the unit being ordered.
     * @param  action      the action to perform.
     */
    public void actionOnVoid(String fromRole, long actionId,
            int unitHandle, String action) {
        Action actInstance = initiateAction(fromRole, unitHandle, action, (int) actionId);
        if (actInstance != null) {
            actInstance.initiate((int) actionId, unitHandle);
            // Check if it is an instantaneous action or if it should be added to the queue!
            if (actInstance.isInstantaneous()) {
                ReferenceHolder.getActionProcessor().executeInstantaneousAction(actInstance);
            } else {
                ReferenceHolder.getActionProcessor().initiateAction(fromRole, actInstance);
            }
        }
    }


    /**
     *  Orders an action that takes a unit as a parameter.
     *
     * @param  fromRole    the role that sent the order.
     * @param  actionId    an identifier for this action.
     * @param  unitHandle  the unit being ordered.
     * @param  action      the action to perform.
     * @param  targetUnit  the unit being targeted.
     */
    public void actionOnUnit(String fromRole, long actionId,
            int unitHandle, String action,
            int targetUnit) {
        Action actInstance = initiateAction(fromRole, unitHandle, action, (int) actionId);
        if (actInstance != null) {
            actInstance.initiate((int) actionId, unitHandle, targetUnit);
            // Check if it is an instantaneous action or if it should be added to the queue!
            if (actInstance.isInstantaneous()) {
                ReferenceHolder.getActionProcessor().executeInstantaneousAction(actInstance);
            } else {
                ReferenceHolder.getActionProcessor().initiateAction(fromRole, actInstance);
            }
        }
    }


    /**
     *  Orders an action that takes a position parameter.
     *
     * @param  fromRole    the role that sent the order.
     * @param  actionId    an identifier for this action.
     * @param  unitHandle  the unit being ordered.
     * @param  action      the action to perform.
     * @param  x           the X coordinate parameter.
     * @param  y           the Y coordinate parameter.
     */
    public void actionOnPosition(String fromRole, long actionId,
            int unitHandle, String action,
            double x, double y) {
        Action actInstance = initiateAction(fromRole, unitHandle, action, (int) actionId);
        if (actInstance != null) {
            actInstance.initiate((int) actionId, unitHandle, x, y);
            // Check if it is an instantaneous action or if it should be added to the queue!
            if (actInstance.isInstantaneous()) {
                ReferenceHolder.getActionProcessor().executeInstantaneousAction(actInstance);
            } else {
                ReferenceHolder.getActionProcessor().initiateAction(fromRole, actInstance);
            }
        }
    }


    /**
     *  Aborts the given action. This causes the action to fail.
     *
     * @param  fromRole  the role that sent the order.
     * @param  actionId  the action to cancel.
     */
    public void abortAction(String fromRole, long actionId) {
        if (ReferenceHolder.getActionProcessor().abortAction(fromRole, (int) actionId)) {
            // Send action error message
            ReferenceHolder.getComClient().actionFailed(fromRole, (int) actionId, "Action aborted.");
        } else {
            // Error...
            log("Couldn't find action with id " + (int) actionId + " from role '" + fromRole + "'! NOT aborted.");
        }
    }


    /**
     *  Description of the Method
     *
     * @param  role  Description of Parameter
     */
    public void startGame(String role) {
        ReferenceHolder.getVisibilityManager().startGame(role);
    }


    /**
     *  Description of the Method
     *
     * @param  role        Description of Parameter
     * @param  unitHandle  Description of Parameter
     * @param  action      Description of Parameter
     * @param  actionId    Description of Parameter
     * @return             Description of the Returned Value
     */
    private Action initiateAction(String role, int unitHandle, String action, int actionId) {
        UnitManager um = ReferenceHolder.getUnitManager();
        Unit unit = um.getUnit(unitHandle);
        UnitType unitType = unit.getUnitType();
        boolean b = unitType.isActionInstantaneous(action);

        if (!unit.getCommander().equals(role)) {
            // TODO - send error, this role is not allowed to control this client
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted. You are not allowed to control this unit.");
            return null;
        }

        if (!b && unit.getActiveAction() != null) {
            // TODO - send error, there is an active action for this unit
            // and the given action is not instantaneous!
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted. This unit is busy carrying out another action.");
            return null;
        }

        if (unit.getBlockedActions().contains(action)) {
            // The action is blocked for this unit...
            // TODO - send error...
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted. This unit is not able to execute this action.");
            return null;
        }
        if (!unitType.getActionNames().contains(action)) {
            // Error, the client has sent an undefined action...
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted. This action is not defined for this unit.");
            return null;
        }

        Class actionClass = unitType.getActionClass(action);
        try {
            Action act = (Action) actionClass.newInstance();
            if (b) {
                act.setInstantaneous(true);
            } else {
                unit.setActiveAction(act);
            }
            return act;
        } catch (IllegalAccessException e) {
            // TODO
            // Not allowed to use the empty constructor in Action class...
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted due to internal failure.");
            log("FATAL! The action class " + actionClass.getName() + " does not have a publicly accessible empty constructor.");
        } catch (InstantiationException e) {
            // TODO
            // No empty constructor in Action class...
            ReferenceHolder.getComClient().actionFailed(role, actionId, "Action aborted due to internal failure.");
            log("FATAL! The action class " + actionClass.getName() + " does not have an empty constructor.");
        }
        return null;
    }


    /**
     *  Description of the Method
     *
     * @param  s  Description of Parameter
     */
    private void log(String s) {
        ReferenceHolder.getLogManager().log("ServerImpl: " + s);
    }
}
