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

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.visibility;

import gecco.server.actionprocessor.ActionProcessor;
import gecco.server.automaton.AutomatonMap;
import gecco.server.core.*;
import gecco.server.qmanager.QManager;
import gecco.server.unitmanager.UnitManager;

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

/**
 *  Description of the Class
 *
 * @author     Karl Hasselström, Henrik Hägerström
 * @created    June 27, 2001
 */
public class VisibilityManager {
    /**
     *  Description of the Field
     */
    private String[] roles;

    /**
     *  Description of the Field
     */
    private AutomatonMap am;
    /**
     *  Description of the Field
     */
    private Client ci;
    /**
     *  Description of the Field
     */
    private UnitManager um;


    /**
     *  Constructor for the VisibilityManager object
     *
     * @param  am  Description of Parameter
     * @param  ci  Description of Parameter
     * @param  um  Description of Parameter
     */
    public VisibilityManager(AutomatonMap am, Client ci, UnitManager um) {
        this.am = am;
        this.ci = ci;
        this.um = um;
        roles = um.getRoles();

    }


    /**
     *  Description of the Method
     *
     * @param  role  Description of Parameter
     * @param  x     Description of Parameter
     * @param  y     Description of Parameter
     * @return       Description of the Returned Value
     */
    public boolean canSeePosition(String role, double x, double y) {
        /*
         * God's Eye can see everything
         * if(um.isGodseye(role))
         * return true;
         */

        Iterator it = um.getObservingUnits(role).iterator();
        while (it.hasNext()) {
            Unit u = (Unit) it.next();
            UnitType uT = u.getUnitType();
            double dx = u.getX() - x;
            double dy = u.getY() - y;
            double distSq = dx * dx + dy * dy;
            if (distSq <= uT.getRangeSquare()) {
                return true;
            }
        }
        return false;
    }


    /**
     *  Description of the Method
     *
     * @param  x  Description of Parameter
     * @param  y  Description of Parameter
     * @param  c  Description of Parameter
     */
    public void updateAutomatonState(int x, int y, Color c) {
        for (int i = 0; i < roles.length; i++) {
            if (um.isGodseye(roles[i]) || canSeeAutomaton(roles[i], x, y)) {
                ci.automatonHasState(roles[i], x, y, c);
            }
        }
    }


    /**
     *  Description of the Method
     *
     * @param  role         Description of Parameter
     * @param  x            Description of Parameter
     * @param  y            Description of Parameter
     * @param  excludeUnit  Description of Parameter
     * @return              Description of the Returned Value
     */
    public boolean canSeeAutomaton(String role, int x, int y, Unit excludeUnit) {
        // God's Eye can see everything
        /*
         * if(um.isGodseye(role))
         * return true;
         */

        Iterator it = um.getObservingUnits(role).iterator();
        while (it.hasNext()) {
            Unit u = (Unit) it.next();
            if (u != excludeUnit && u.canSeeAutomatonAtPosition(x, y)) {
                return true;
            }
        }
        return false;
    }


    /**
     *  Description of the Method
     *
     * @param  role  Description of Parameter
     * @param  x     Description of Parameter
     * @param  y     Description of Parameter
     * @return       Description of the Returned Value
     */
    public boolean canSeeAutomaton(String role, int x, int y) {
        return canSeeAutomaton(role, x, y, null);
    }


    /**
     *  Description of the Method
     *
     * @param  role         Description of Parameter
     * @param  unit         Description of Parameter
     * @param  excludeUnit  Description of Parameter
     * @return              Description of the Returned Value
     */
    public boolean canSeeUnit(String role, Unit unit, Unit excludeUnit) {
        /*
         * God's Eye can see everything
         * if(um.isGodseye(role))
         * return true;
         */
        double x = unit.getX();
        double y = unit.getY();
        Iterator it = um.getObservingUnits(role).iterator();
        while (it.hasNext()) {
            Unit u = (Unit) it.next();
            if (u != excludeUnit) {
                UnitType uT = u.getUnitType();
                double dx = u.getX() - x;
                double dy = u.getY() - y;
                double distSq = dx * dx + dy * dy;
                if (distSq <= uT.getRangeSquare()) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     *  Description of the Method
     *
     * @param  unit  Description of Parameter
     * @param  oldX  Description of Parameter
     * @param  oldY  Description of Parameter
     * @param  newX  Description of Parameter
     * @param  newY  Description of Parameter
     */
    public void updateUnitPosition(Unit unit, double oldX, double oldY,
            double newX, double newY) {
        int handle = unit.getHandle();
        int range = um.getRange(unit);
        double rangeSq = range * range;
        String[] observingRoles = um.getRolesObservingUnit(unit);
        String[] alienRoles = um.getRolesAlienToUnit(unit);

        // Move unit for all godseye roles!
        Iterator godsEyeIt = um.getGodsEyeRoles().iterator();
        while (godsEyeIt.hasNext()) {
            String curRole = (String) godsEyeIt.next();
            ci.unitMoved(curRole, handle, newX, newY);
        }

        // Send unit updates
        for (int i = 0; i < observingRoles.length; i++) {
            // First of all, send unit moved message since this role
            // is an observer, i.e. it should always be informed of
            // movements for this unit!
            String currentRole = observingRoles[i];
            ci.unitMoved(currentRole, handle, newX, newY);

            // Fetch iterator for all alien units for this role!
            Iterator it = um.getAlienUnits(currentRole).iterator();
            while (it.hasNext()) {
                Unit alienUnit = (Unit) it.next();
                double x = (newX - alienUnit.getX());
                double y = (newY - alienUnit.getY());
                double distSq = x * x + y * y;
                if (distSq <= rangeSq) {
                    // Visible!
                    if (um.activateUnit(currentRole, alienUnit)) {
                        // The now visible unit wasn't visible before!
                        // Present new alien unit for role!
                        ci.unitVisible(currentRole,
                                alienUnit.getHandle(),
                                alienUnit.getName(),
                                alienUnit.getUnitType().getName(),
                                alienUnit.getX(),
                                alienUnit.getY(),
                                null,
                                null,
                                null);
                    }
                } else {
                    // Not Visible!
                    if (!canSeeUnit(currentRole, alienUnit, unit) &&
                            um.deactivateUnit(currentRole, alienUnit)) {
                        // The now invisible unit was visible before,
                        // and this role cannot se it!
                        // Invisibilise alien unit for role!
                        ci.unitInvisible(currentRole,
                                alienUnit.getHandle());
                    }
                }
            }
        }

        for (int i = 0; i < alienRoles.length; i++) {
            // Here we check if the moved unit is spotted
            // by the alien roles for this unit, i.e. the roles
            // that aren't observers or commander for the moved unit.
            String currentRole = alienRoles[i];

            if (canSeeUnit(currentRole, unit, null)) {
                if (um.activateUnit(currentRole, unit)) {
                    // The now visible unit wasn't visible before!
                    // Present the moved unit as an alien unit for the role!
                    ci.unitVisible(currentRole,
                            unit.getHandle(),
                            unit.getName(),
                            unit.getUnitType().getName(),
                            unit.getX(),
                            unit.getY(),
                            null,
                            null,
                            null);
                } else {
                    // The moved unit was visible before! Just inform the role
                    // that it has moved!
                    ci.unitMoved(currentRole, unit.getHandle(), newX, newY);
                }
            } else {
                if (um.deactivateUnit(currentRole, unit)) {
                    // The now invisible unit was visible before!
                    // Therefore, invisibilise the moved unit for role!
                    ci.unitInvisible(currentRole,
                            unit.getHandle());
                }
            }
        }

        // Check for new visible/invisible automatons
        // Java's operation when casting a double to an int is to
        // floor the double, i.e. oldX = 1.94 -> oldIntX = 1
        int oldIntX = (int) oldX;
        int oldIntY = (int) oldY;
        int newIntX = (int) newX;
        int newIntY = (int) newY;
        UnitType uT = unit.getUnitType();
        int[] visionStart = uT.getVisionStart();
        int[] visionStop = uT.getVisionStop();
        int diff = oldIntY - newIntY;
        Iterator rolesIterator = unit.getObservers().iterator();
        while (rolesIterator.hasNext()) {
            String currentRole = (String) rolesIterator.next();
            HashSet units = new HashSet(um.getObservingUnits(currentRole));
            units.remove(unit);
            // Remove current unit from the set, since we always
            // want to check if any _other_ unit can see a specific
            // point or not.
            Unit[] unitsArray = (Unit[]) units.toArray(new Unit[0]);
            int curOldIndex = 0;
            int curNewIndex = 0;
            int curY;
            LinkedList visiblePoints = new LinkedList();
            LinkedList invisiblePoints = new LinkedList();
            if (diff < 0) {
                // Old circle on top
                curY = oldIntY - range;
                for (int i = 0; i < (-diff); i++, curY++, curOldIndex++) {
                    if (curY < 0) {
                        continue;
                    }
                    if (curY >= am.getYBound()) {
                        break;
                    }
                    // Iterate over all points in the interval, check if they are
                    // visible through some other unit. If not, add to invisible set.
                    int startX = visionStart[curOldIndex] + oldIntX;
                    int stopX = visionStop[curOldIndex] + oldIntX;
                    if (startX < 0) {
                        startX = 0;
                    }
                    if (stopX >= am.getXBound()) {
                        stopX = am.getXBound() - 1;
                    }
                    for (int k = startX; k <= stopX; k++) {
                        boolean found = false;
                        for (int l = 0; l < unitsArray.length; l++) {
                            if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            invisiblePoints.add(new Point(k, curY));
                        }
                    }
                }
            } else {
                // New circle on top
                curY = newIntY - range;
                for (int i = 0; i < diff; i++, curY++, curNewIndex++) {
                    if (curY < 0) {
                        continue;
                    }
                    if (curY >= am.getYBound()) {
                        break;
                    }
                    // Iterate over all points in the interval, check if they are
                    // visible through some other unit. If not, add to visible set.
                    int startX = visionStart[curNewIndex] + newIntX;
                    int stopX = visionStop[curNewIndex] + newIntX;
                    if (startX < 0) {
                        startX = 0;
                    }
                    if (stopX >= am.getXBound()) {
                        stopX = am.getXBound() - 1;
                    }
                    for (int k = startX; k <= stopX; k++) {
                        boolean found = false;
                        for (int l = 0; l < unitsArray.length; l++) {
                            if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            visiblePoints.add(new Point(k, curY));
                        }
                    }
                }
            }

            // INVARIANT: Both circles 'active' for next row (it's the first row for which
            // both circles are active).

            int checkValue = (2 * range + 1 - Math.abs(diff));
            for (int i = 0; i < checkValue; i++, curY++, curOldIndex++, curNewIndex++) {
                if (curY < 0) {
                    continue;
                }
                if (curY >= am.getYBound()) {
                    break;
                }

                // Kolla skärning mellan cirklarna, kontrollera om punkter är synliga
                // pga andra enheters synfält, och lägg till i någon mängd om så behövs.
                int oldStartX = visionStart[curOldIndex] + oldIntX;
                int oldStopX = visionStop[curOldIndex] + oldIntX;
                int newStartX = visionStart[curNewIndex] + newIntX;
                int newStopX = visionStop[curNewIndex] + newIntX;
                if (oldStartX < 0) {
                    oldStartX = 0;
                }
                if (oldStopX >= am.getXBound()) {
                    oldStopX = am.getXBound() - 1;
                }
                if (newStartX < 0) {
                    newStartX = 0;
                }
                if (newStopX >= am.getXBound()) {
                    newStopX = am.getXBound() - 1;
                }

                // We have six cases (swap x and y). See below.

// 1)           xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//                               yyyyyyyyyyyyyyyyyyyyyyy

// 2)           xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//                    yyyyyyyyyyyyyyyyyyyyyyy

// 3)           xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//                                                      yyyyyyyyyyyyyyyyyyyyyyy

// The algorithm 'sorts' the four x-indeces, and sets the variables
// to the correct values.

                // Lefthand side interval
                LinkedList leftHandSideSet;
                LinkedList rightHandSideSet;
                int startLeft;
                int stopLeft;
                int startRight;
                int stopRight;

                // Spaghetti code...
                if (newStartX < oldStartX) {
                    // Sorted order so far: newStartX < oldStartX

                    if (oldStartX < newStopX) {
                        // Sorted order so far: newStartX < oldStartX < newStopX
                        leftHandSideSet = visiblePoints;
                        startLeft = newStartX;
                        stopLeft = oldStartX - 1;

                        if (newStopX < oldStopX) {

                            // Sorted order so far: newStartX < oldStartX < newStopX < oldStopX
                            // CASE 1 - new is 'x', old is 'y'
                            rightHandSideSet = invisiblePoints;
                            startRight = newStopX + 1;
                            stopRight = oldStopX;
                        } else {

                            // Sorted order so far: newStartX < oldStartX < oldStopX <= newStopX
                            // CASE 2 - new is 'x', old is 'y'
                            rightHandSideSet = visiblePoints;
                            startRight = oldStopX + 1;
                            stopRight = newStopX;
                        }
                    } else {
                        // Sorted order so far: newStartX <= newStopX <= oldStartX <= oldStopX
                        // CASE 3 - new is 'x', old is 'y'
                        leftHandSideSet = visiblePoints;
                        startLeft = newStartX;
                        stopLeft = newStopX;
                        rightHandSideSet = invisiblePoints;
                        startRight = oldStartX;
                        stopRight = oldStopX;
                        // If these two points coincide, we have to increase
                        // startRight by 1, and decrease stopLeft by 1
                        if (newStopX == oldStartX) {
                            stopLeft--;
                            startRight++;
                        }
                    }
                } else {
                    // Sorted order so far: oldStartX <= newStartX

                    if (newStartX < oldStopX) {
                        // Sorted order so far: oldStartX <= newStartX < oldStopX
                        leftHandSideSet = invisiblePoints;
                        startLeft = oldStartX;
                        stopLeft = newStartX - 1;

                        if (oldStopX < newStopX) {
                            // Sorted order so far: oldStartX <= newStartX < oldStopX < newStopX
                            // CASE 1 - old is 'x', new is 'y'
                            rightHandSideSet = visiblePoints;
                            startRight = oldStopX + 1;
                            stopRight = newStopX;

                        } else {
                            // Sorted order so far: oldStartX <= newStartX <= newStopX <= oldStopX
                            // CASE 2 - old is 'x', new is 'y'
                            rightHandSideSet = invisiblePoints;
                            startRight = newStopX + 1;
                            stopRight = oldStopX;
                        }
                    } else {
                        // Sorted order so far: oldStartX <= oldStopX <= newStartX <= newStopX
                        // CASE 3 - old is 'x', new is 'y'
                        leftHandSideSet = invisiblePoints;
                        startLeft = oldStartX;
                        stopLeft = oldStopX;
                        rightHandSideSet = visiblePoints;
                        startRight = newStartX;
                        stopRight = newStopX;
                        // If these two points coincide, we have to increase
                        // startRight by 1, and decrease stopLeft by 1
                        if (oldStopX == newStartX) {
                            stopLeft--;
                            startRight++;
                        }
                    }
                }

                for (int k = startLeft; k <= stopLeft; k++) {
                    boolean found = false;
                    for (int l = 0; l < unitsArray.length; l++) {
                        if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        leftHandSideSet.add(new Point(k, curY));
                    }
                }

                // Righthand side interval
                for (int k = startRight; k <= stopRight; k++) {
                    boolean found = false;
                    for (int l = 0; l < unitsArray.length; l++) {
                        if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        rightHandSideSet.add(new Point(k, curY));
                    }
                }

            }

            if (diff < 0) {
                // New circle below
                for (int i = 0; i < (-diff); i++, curY++, curNewIndex++) {
                    if (curY < 0) {
                        continue;
                    }
                    if (curY >= am.getYBound()) {
                        break;
                    }
                    // Iterate over all points in the interval, check if they are
                    // visible through some other unit. If not, add to visible set.
                    int startX = visionStart[curNewIndex] + newIntX;
                    int stopX = visionStop[curNewIndex] + newIntX;
                    if (startX < 0) {
                        startX = 0;
                    }
                    if (stopX >= am.getXBound()) {
                        stopX = am.getXBound() - 1;
                    }
                    for (int k = startX; k <= stopX; k++) {
                        boolean found = false;
                        for (int l = 0; l < unitsArray.length; l++) {
                            if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            visiblePoints.add(new Point(k, curY));
                        }
                    }
                }
            } else {
                // Old circle below
                for (int i = 0; i < diff; i++, curOldIndex++, curY++) {
                    if (curY < 0) {
                        continue;
                    }
                    if (curY >= am.getYBound()) {
                        break;
                    }
                    // Iterate over all points in the interval, check if they are
                    // visible through some other unit. If not, add to invisible set.
                    int startX = visionStart[curOldIndex] + oldIntX;
                    int stopX = visionStop[curOldIndex] + oldIntX;
                    if (startX < 0) {
                        startX = 0;
                    }
                    if (stopX >= am.getXBound()) {
                        stopX = am.getXBound() - 1;
                    }
                    for (int k = startX; k <= stopX; k++) {
                        boolean found = false;
                        for (int l = 0; l < unitsArray.length; l++) {
                            if (unitsArray[l].canSeeAutomatonAtPosition(k, curY)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            invisiblePoints.add(new Point(k, curY));
                        }
                    }
                }
            }

            // Skicka meddelanden för de två mängderna med punkter...

            ci.automatonsInvisible(currentRole, invisiblePoints);

            LinkedList visibleColors = new LinkedList();
            Iterator vis = visiblePoints.iterator();
            while (vis.hasNext()) {
                Point p = (Point) vis.next();
                visibleColors.add(am.getAutomaton(p.x, p.y).getCurrentColor());
            }
            ci.automatonsHasState(currentRole,
                    visiblePoints,
                    visibleColors);
        }

    }


    /**
     *  Description of the Method
     *
     * @param  unit        Description of Parameter
     * @param  lastKnownX  Description of Parameter
     * @param  lastKnownY  Description of Parameter
     */
    public void deleteUnit(Unit unit, double lastKnownX, double lastKnownY) {
        /*
         * For all observers of the unit, remove all automatons that aren't visible through
         * other observer-units.
         * Also remove all units that are visible through the unit.
         * Then report to all observer roles and all roles that has this unit as active
         * that it has been destroyed.
         *
         * You could think of this procedure as if the unit suddenly moves a long long
         * way out of our coordinate system.
         */

        int handle = unit.getHandle();
        int range = um.getRange(unit);
        double rangeSq = range * range;
        String[] observingRoles = um.getRolesObservingUnit(unit);
        String[] alienRoles = um.getRolesAlienToUnit(unit);

        // Delete unit for all godseye roles!
        Iterator godsEyeIt = um.getGodsEyeRoles().iterator();
        while (godsEyeIt.hasNext()) {
            String curRole = (String) godsEyeIt.next();
            ci.unitDeleted(curRole, handle);
        }

        // Send unit updates
        for (int i = 0; i < observingRoles.length; i++) {
            // First of all, send unit destroyed message since this role
            // is an observer, i.e. it should always be informed of
            // movements for this unit!
            String currentRole = observingRoles[i];
            ci.unitDeleted(currentRole, handle);

            LinkedList points = new LinkedList();

            // Fetch iterator for all alien units for this role!
            Iterator it = um.getAlienUnits(currentRole).iterator();
            while (it.hasNext()) {
                Unit alienUnit = (Unit) it.next();
                // Check if the role can't see the alien unit after
                // the argument unit has been destroyed
                if (!canSeeUnit(currentRole, alienUnit, unit) &&
                        um.deactivateUnit(currentRole, alienUnit)) {
                    // The now invisible unit was visible before,
                    // and this role cannot se it!
                    // Invisibilise alien unit for role!
                    ci.unitInvisible(currentRole,
                            alienUnit.getHandle());
                }
            }

            // Now, iterate over all the automatons this unit could see
            // and store all the points that now has become invisible to the role.
            int x = (int) lastKnownX;
            int y = (int) lastKnownY;
            int[] xstart = unit.getUnitType().getVisionStart();
            int[] xstop = unit.getUnitType().getVisionStop();
            int curY = y - range;
            for (int k = 0; k < 2 * range + 1; k++, curY++) {
                if (curY < 0) {
                    continue;
                }
                if (curY >= am.getYBound()) {
                    break;
                }
                int left = xstart[k] + x;
                int right = xstop[k] + x;
                if (left < 0) {
                    left = 0;
                } else if (left >= am.getXBound()) {
                    continue;
                }
                if (right < 0) {
                    continue;
                } else if (right >= am.getXBound()) {
                    right = am.getXBound() - 1;
                }
                for (int j = left; j <= right; j++) {
                    if (!canSeeAutomaton(currentRole, j, curY, unit)) {
                        points.add(new Point(j, curY));
                    }
                }
            }
            // Invisibilise the automatons for this role!
            if (!points.isEmpty()) {
                ci.automatonsInvisible(currentRole, points);
            }
        }

        // Lastly, send unit destroyed message to all roles that
        // are alien to the unit but saw it before it was destroyed!

        for (int i = 0; i < alienRoles.length; i++) {
            // Iterate over all alien roles and see if
            // the destroyed unit was active for any one of them.
            // If so, send a unit destroyed message.
            if (um.deactivateUnit(alienRoles[i], unit)) {
                ci.unitDeleted(alienRoles[i], handle);
            }
        }
    }


    /**
     *  Description of the Method
     *
     * @param  role  Description of Parameter
     */
    public void startGame(String role) {
        // This is just a refresh of the current situation for a role!

        // SYNCHRONIZE ON THE QMANAGER QUEUES AND THE ACTIONPROCESSOR QUEUE,
        // OTHERWISE WE MIGHT HAVE SOME INTEGRITY PROBLEMS!
        LinkedList points = new LinkedList();
        LinkedList colors = new LinkedList();
        QManager qM = ReferenceHolder.getQManager();
        ActionProcessor aP = ReferenceHolder.getActionProcessor();
        synchronized (qM.getAutomatonQueue()) {
            synchronized (qM.getAutomatonEventQueue()) {
                synchronized (aP.getActionQueue()) {
                    // Ok, we've frozen all action going on in the game!
                    // Let's rock!

                    // If this role is a gods eye role, call startGodsEyeGame...
                    if (um.isGodseye(role)) {
                        startGodsEyeGame(role);
                        return;
                    }

                    // First, present all observer units and the automatons
                    // they can see!
                    Iterator it = um.getObservingUnits(role).iterator();
                    while (it.hasNext()) {
                        // Present the unit! Check if the role is the commander or just
                        // an observer!
                        Unit unit = (Unit) it.next();
                        UnitType unitType = unit.getUnitType();
                        if (role.equals(unit.getCommander())) {
                            Set unblockedActionDefinitions = new HashSet(unitType.getActionDefinitions());
                            Set blockedActionDefinitions = new HashSet();
                            Set blockedActionNames = unit.getBlockedActions();
                            Iterator actionIt = blockedActionNames.iterator();
                            while (actionIt.hasNext()) {
                                String name = (String) actionIt.next();
                                ActionDefinition actDef = unitType.getActionDefinition(name);
                                blockedActionDefinitions.add(actDef);
                                unblockedActionDefinitions.remove(actDef);
                            }
                            um.activateUnit(role, unit);
                            ci.unitVisible(role,
                                    unit.getHandle(),
                                    unit.getName(),
                                    unit.getUnitType().getName(),
                                    unit.getX(),
                                    unit.getY(),
                                    unblockedActionDefinitions,
                                    blockedActionDefinitions,
                                    unit.getProperties());
                        } else {
                            // Role is an observer for the unit!
                            um.activateUnit(role, unit);
                            ci.unitVisible(role,
                                    unit.getHandle(),
                                    unit.getName(),
                                    unit.getUnitType().getName(),
                                    unit.getX(),
                                    unit.getY(),
                                    null,
                                    null,
                                    unit.getProperties());
                        }

                        // Ok, now present all automatons this unit can see!
                        int x = unit.getIntX();
                        int y = unit.getIntY();
                        int range = unitType.getRange();
                        int[] xstart = unitType.getVisionStart();
                        int[] xstop = unitType.getVisionStop();
                        int curY = y - range;
                        for (int i = 0; i < 2 * range + 1; i++, curY++) {
                            if (curY < 0) {
                                continue;
                            }
                            if (curY >= am.getYBound()) {
                                break;
                            }
                            int left = xstart[i] + x;
                            int right = xstop[i] + x;
                            if (left < 0) {
                                left = 0;
                            } else if (left >= am.getXBound()) {
                                continue;
                            }
                            if (right < 0) {
                                continue;
                            } else if (right >= am.getXBound()) {
                                right = am.getXBound() - 1;
                            }
                            for (int j = left; j <= right; j++) {
                                points.add(new Point(j, curY));
                                colors.add(am.getAutomaton(j, curY).getCurrentColor());
                            }
                        }
                    }

                    // Present all automatons for the role!
                    ci.automatonsHasState(role, points, colors);

                    // Ok, so do the shit and present all units that
                    // are alien to this role, but is in withing the range
                    // of atleast one of the role's observer units.
                    it = um.getAlienUnits(role).iterator();
                    while (it.hasNext()) {
                        Unit alienUnit = (Unit) it.next();
                        Iterator obsIt = um.getObservingUnits(role).iterator();
                        while (obsIt.hasNext()) {
                            Unit obsUnit = (Unit) obsIt.next();
                            UnitType uT = obsUnit.getUnitType();
                            double rangeSq = uT.getRange() * uT.getRange();
                            double dx = (alienUnit.getX() - obsUnit.getX());
                            double dy = (alienUnit.getY() - obsUnit.getY());
                            double dist = dx * dx + dy * dy;
                            if (dist <= rangeSq) {
                                // The observing unit can see the alien unit!
                                // Present alien unit to role!
                                um.activateUnit(role, alienUnit);
                                ci.unitVisible(role,
                                        alienUnit.getHandle(),
                                        alienUnit.getName(),
                                        alienUnit.getUnitType().getName(),
                                        alienUnit.getX(),
                                        alienUnit.getY(),
                                        null,
                                        null,
                                        null);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     *  Description of the Method
     *
     * @param  godsEyeRole  Description of Parameter
     */
    private void startGodsEyeGame(String godsEyeRole) {
        // We're already synchronized on all queues, just rock!

        // Make all units visible for the role!
        Iterator it = um.getAllUnits().iterator();
        while (it.hasNext()) {
            Unit unit = (Unit) it.next();
            // Present unit as if the gods eye role was an observer!
            if (!unit.isDestroyed()) {
                um.activateUnit(godsEyeRole, unit);
                ci.unitVisible(godsEyeRole,
                        unit.getHandle(),
                        unit.getName(),
                        unit.getUnitType().getName(),
                        unit.getX(),
                        unit.getY(),
                        null,
                        null,
                        unit.getProperties());
            }
        }

        // Present all automatons!
        LinkedList points = new LinkedList();
        LinkedList colors = new LinkedList();
        for (int y = 0; y < am.getYBound(); y++) {
            for (int x = 0; x < am.getXBound(); x++) {
                points.add(new Point(x, y));
                colors.add(am.getAutomaton(x, y).getCurrentColor());
            }
        }
        ci.automatonsHasState(godsEyeRole, points, colors);
    }


    /**
     *  The main program for the VisibilityManager class
     *
     * @param  args  The command line arguments
     */
    public static void main(String[] args) {
        // Test if there is any performance to gain by using
        // a table to square integers.

        // Result: well, there is a gain in performance.
        // When doing 401*1000000 multiplications, the
        // time was 9.1 secs for array indexing, and
        // 13.1 when letting java multiplicate the int
        // with itself.

        int maxSquare = 200;
        int[] sqArray = new int[2 * maxSquare + 1];
        int curIndex = 0;
        for (int i = -maxSquare; i < maxSquare; i++, curIndex++) {
            sqArray[curIndex] = i * i;
        }

        // Do kmax * (2 * maxSquare + 1) multiplications
        int kmax = Integer.parseInt(args[0]);
        for (int k = 0; k < kmax; k++) {
            for (int i = -maxSquare; i <= maxSquare; i++) {
//          int theSquare = sqArray[i + maxSquare];
                int theSquare = i * i;
            }
        }
    }

}
