/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package worldserver3d;

import worldserver3d.thread.ServerThreadList;
import worldserver3d.view.WorldFrame;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Time;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.Creature;
import model.Thing;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import model.*;
import util.Constants;
import worldserver3d.thread.ServerThread;

/**
 * Main class of the server (WorldServer 3D - WS3D). This class defines the
 * protocol (list of commands) upon which server and clients must agree in order
 * to establish communication. A server socket is also implemented. A network
 * socket is opened for each client that requests communication with the server.
 *
 * @author rgudwin
 * @author ecalhau
 */
public class Main {

    private String version = "1.0";

    public static WorldFrame i;

    public static SimulationFrame sf;

    private static ServerSocket ss;

    private static StringBuffer outToClient;

    private static ServerThreadList clients;

    private static boolean simulating;

    public Main() {
        Logger.getLogger("com.jme").setLevel(Level.OFF);
    }

    private void doInitialize() throws IOException {
        i = new WorldFrame();
        sf = new SimulationFrame(this);
        clients = new ServerThreadList();
        ss = new ServerSocket(Constants.PORT);
    }

    private boolean initialize() {
        try {
            doInitialize();
            System.out.println("Initialized!");
            return true;
        } catch (IOException ex) {
            System.out.println("An exception was caught in Main.initialize: " + ex);
        }
        return false;
    }

    public static void shutdown() {
        simulating = false;
        if (null != clients) {
            System.out.println("Disconnecting " + clients.size() + " client(s)...");
            clients.disconnectAll();
            System.out.println("Done!");
        }
        try {
            if (null != ss) {
                ss.close();
            }
        } catch (IOException ex) {
            System.out.println("An exception was caught in Main.shutdown: " + ex);
        }
    }

    private void doSimulate() throws IOException {
        while (simulating) {
            clients.startAndAdd(new SimulationServerThread(ss.accept()));
            System.out.println("Client added! > Active: " + clients.size());
        }
    }

    public void simulate() {
        if (simulating) {
            System.out.println("Already simulating.");
            return;
        }
        if (initialize()) {
            try {
                simulating = true;
                System.out.println("Simulating...");
                doSimulate();
                shutdown();
                System.out.println("Simulation finished!");
            } catch (IOException ex) {
                System.out.println("An exception was caught in Main.simulate: " + ex);
            }
        } else {
            System.out.println("Unable to initialize WorldServer3D.");
        }
    }
    private class SimulationServerThread extends ServerThread {

        private Socket socket;

        private boolean connected;

        public SimulationServerThread(final Socket socket) {
            Objects.requireNonNull(socket, "Trying to create a new SimulationServerThread with a null socket.");
            this.socket = socket;
            this.connected = true;
        }

        @Override
        public void disconnect() {
            connected = false;
        }

        private boolean isConnected() {
            return null != socket && socket.isConnected() && !socket.isClosed() && connected;
        }

        @Override
        public void run() {
            System.out.println("A server thread is running!");
            try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    PrintWriter out = new PrintWriter(socket.getOutputStream(), true);) {
                out.println(processMessage("version"));
                String inputLine, outputLine;
                while (isConnected()) {
                    inputLine = in.readLine();
                    switch (inputLine) {
                        case "shutdown": {
                            out.println("kick");
                            this.disconnect();
                            shutdown();
                        }
                        break;

                        case "quit": {
                            out.println("kick");
                            this.disconnect();
                        }
                        break;

                        case "bye": {
                            this.disconnect();
                        }
                        break;

                        default: {
                            outputLine = processMessage(inputLine);
                            out.println(outputLine);
                        }
                        break;
                    }
                }
                if (null != clients) {
                    clients.remove(this);
                    System.out.println("Client removed! > Active: " + clients.size());
                }
            } catch (Exception ex) {
                System.out.println("An exception was caught in ServerThread.run: " + ex);
            }
        }
    }

    public synchronized StringBuffer getOutBuffer() {
        return this.outToClient;
    }

    /**
     * Definition of the "protocol". The protocol is a list of commands that the
     * server understands and is ready to process. Each command may also has a
     * list of parameters.
     *
     * @param input command
     * @return message if the command was succesfully processed or not.
     */
    public synchronized String processMessage(String input) {
        outToClient = new StringBuffer("");
        if (null == input || input.equalsIgnoreCase("")) {
            return ("Null message received ...");
        }
        StringTokenizer st = new StringTokenizer(input);
        if (!st.hasMoreTokens()) {
            return ("... No recognized command");
        }
        String command = st.nextToken();
        if (command.equalsIgnoreCase("game")) {
            ProcessRequestIfGameRunning();
        } else if (command.equalsIgnoreCase("startGame")) {
            ProcessStartGame();
        } else if (command.equalsIgnoreCase("VERSION")) {
            ProcessVersion();
        } else if (command.equalsIgnoreCase("quit")) {
            return ProcessQuit();
        } else if (command.equalsIgnoreCase("shutdown")) {
            ProcessShutdown();
        } else if (command.equalsIgnoreCase("getclock")) {
            ProcessGetClock();
        } else if (command.equalsIgnoreCase("memory")) {
            ProcessGetMemory();
        } else if (command.equalsIgnoreCase(("print"))) {
            ProcessPrint();
        } else if (command.equalsIgnoreCase(("setenv"))) {
            ProcessInitialEnvironmentParameters(st);
        } else if (command.equalsIgnoreCase(("getenvironment"))) {
            ProcessGetEnvironment();
        } else if (command.equalsIgnoreCase(("getcreatcoords"))) {
            ProcessGetCreatureCoords(st);
        } else if (command.equalsIgnoreCase(("getcreatinfo"))) {
            ProcessGetCreatureInfo(st);
        } else if (command.equalsIgnoreCase(("getvs"))) {
            ProcessGetVisualSensor(st);
        } else if (command.equalsIgnoreCase(("getfullstatus3D"))) {
            ProcessGetFullStatus3D(st);
        } else if (command.equalsIgnoreCase(("camera"))) {
            ProcessActivateCamera(st);
        } else if (command.equalsIgnoreCase(("affordances"))) {
            ProcessGetAffordances(st);
        } else if (command.equalsIgnoreCase(("getsimulpars"))) {
            ProcessGetSimulPars();
        } else if (command.equalsIgnoreCase(("getsimulpars3D"))) {
            ProcessGetSimulPars3D();
        } else if (command.equalsIgnoreCase(("set"))) {
            ProcessSetFullStatus(st);
        } else if (command.equalsIgnoreCase(("setgoTo"))) {
            ProcessSetDifferentialStatusCML(st);
        } else if (command.equalsIgnoreCase(("setAngle"))) {
            ProcessSetDifferentialStatus3D(st);
        } else if (command.equalsIgnoreCase(("sackit"))) {
            ProcessSackIt(st);
        } else if (command.equalsIgnoreCase(("eatit"))) {
            ProcessEatIt(st);
        } else if (command.equalsIgnoreCase(("hideit"))) {
            ProcessHideIt(st);
        } else if (command.equalsIgnoreCase(("unhideit"))) {
            ProcessUnhideIt(st);
        } else if (command.equalsIgnoreCase(("setTurn"))) {
            ProcessSetDifferentialStatus3DT(st);
        } else if (command.equalsIgnoreCase(("leaflet"))) {
            ProcessSetLeaflet(st);
        } else if (command.equalsIgnoreCase(("deliver"))) {
            ProcessDeliverLeaflet(st);
        } else if (command.equalsIgnoreCase("help")) {
            ProcessHelp();
        } else if (command.equalsIgnoreCase("drop")) {
            ProcessDrop(st);
        } else if (command.equalsIgnoreCase("new")) {
            ProcessNewCreature(st);
        } else if (command.equalsIgnoreCase("start")) {
            ProcessStartCreature(st);
        } else if (command.equalsIgnoreCase("stop")) {
            ProcessStopCreature(st);
        } else if (command.equalsIgnoreCase("refuel")) {
            ProcessRefuel(st);
        } else if (command.equalsIgnoreCase("mindName")) {
            ProcessSetThreadName(st);
        } else if (command.equalsIgnoreCase("getworldsituation")) {
            ProcessGetWorldSituation();
        } else if (command.equalsIgnoreCase("check")) {
            ProcessCheckCreature(st);
        } else if (command.equalsIgnoreCase("showworldthings")) {
            ProcessShowWorldThings();
        } else if (command.equalsIgnoreCase("getall")) {
            ProcessGetAllThings();
        } else if (command.equalsIgnoreCase("newwp")) {
            ProcessNewWaypoint(st);
        } else if (command.equalsIgnoreCase("delwp")) {
            ProcessDeleteWaypoint(st);
        } else if (command.equalsIgnoreCase("closest")) {
            ProcessClosest(st);
        } else if (command.equalsIgnoreCase("food")) {
            ProcessNewFood(st);
        } else if (command.equalsIgnoreCase("jewel")) {
            ProcessNewJewel(st);
        } else if (command.equalsIgnoreCase("brick")) {
            ProcessNewBrick(st);
        } else if (command.equalsIgnoreCase("batchoffood")) {
            ProcessNewFoodInBatch(st);
        } else if (command.equalsIgnoreCase("batchofjewels")) {
            ProcessNewJewelsInBatch(st);
        } else if (command.equalsIgnoreCase("cage")) {
            ProcessNewCage(st);
        } else {
            getOutBuffer().append("... Cannot understand ").append(command);
        }
        return getOutBuffer().toString();
    }

    /*
     * Command: "game". Game status requisition (if it has started or not).
     * @return "yes" or "not"
     */
    void ProcessRequestIfGameRunning() {
        if (i.ep.e.gameStarted()) {
            getOutBuffer().append("yes");
        } else {
            getOutBuffer().append("no");
        }
    }

    /*
     * Command: "version" Information about the server version. @return the
     * version
     */
    void ProcessVersion() {
        getOutBuffer().append("WorldServer3D Version ").append(version).append(". Welcome to the game!!!\r\n");
    }
    /*
     * Command: "startGame" The game is started.
     */

    void ProcessStartGame() {
        i.ep.e.startTheGame(true);
        getOutBuffer().append("Game started!!!\r\n");
    }

    /**
     * Command: "quit" Quit the client connection established with the server.
     *
     * @return
     */
    String ProcessQuit() {
        getOutBuffer().append("Quiting the system");
        return ("quit");

    }
    /*
     * Command: "shutdown" The server is shutdown.
     */

    void ProcessShutdown() {
        getOutBuffer().append("WorldServer is shuting down ...");
        shutdown();
    }

    /*
     * Command: "getclock" @return current time in milliseconds.
     */
    void ProcessGetClock() {
        getOutBuffer().append(new Time(System.currentTimeMillis()).toString());
    }

    /**
     * Command: "memory"
     *
     * @return the amount of free memory in the Java Virtual Machine.
     */
    void ProcessGetMemory() {
        getOutBuffer().append(Runtime.getRuntime().freeMemory());
    }

    /*
     * Command: "print" Append a space to the current text message in the socket
     * buffer.
     */
    void ProcessPrint() {
        getOutBuffer().append(" ");
    }

    /**
     * Command: "getenvironment"
     *
     * @return information about the game environment
     */
    void ProcessGetEnvironment() {
        getOutBuffer().append("Environment:" + i.ep.getPreferredSize().width + " " + i.ep.getPreferredSize().height);
    }

    /**
     * Old "start" command. Not in use in this version.
     */
    void ProcessStartSimulation() {
        i.ep.startMoving();
        getOutBuffer().append("Starting the simulation ...");
    }

    /**
     * Old "stop" command. Not in use in this version.
     */
    void ProcessStopSimulation() {
        i.ep.stopMoving();
        getOutBuffer().append("Stopping the simulation ...");

    }

    /**
     * ****************************************************************************
     * NOTE: The following list of commands has a first parameter called
     * "creature index". It refers to the ordinal position in the list of
     * creatures in the game. It changes whenever an older creature (created
     * before and hence with a lower index) is deleted, causing a shift of
     * indexes. Example: creature 0 is deleted. The creature with index 1 now
     * assumes index 0. It must not be confused with the ID attribute of each
     * Thing in the environment which is unique and never changes during the
     * game.
     * ****************************************************************************
     */
    /**
     * Command: "getcreatcoords"
     *
     * @param st the creature index within the environment (NOT the actual ID).
     * Please see comment "NOTE" above.
     */
    void ProcessGetCreatureCoords(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        Creature c = i.ep.e.getCpool().get(creatID);
        getOutBuffer().append("Creature Coordinates:").append(c.getX()).append(" ").append(c.getY()).append(" ").append(c.getPitch());
    }

    /**
     * Command: "getcreatinfo"
     *
     * @param st the creature index within the environment (NOT the actual ID).
     * Please see comment "NOTE" above.
     */
    void ProcessGetCreatureInfo(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        Creature c = i.ep.e.getCpool().get(creatID);

        // wheel is not applicable when the creature is a differential robot
        // and is allways 0 in this case.
        getOutBuffer().append("Creature Info:\nWheel: ").append(c.getWheel()).append("\nSpeed: ").append(c.getSpeed()).append("\nFuel: ").append(c.getFuel());

    }

    /**
     * Command: "closest"
     *
     * @param st the creature index within the environment (NOT the actual ID).
     * Please see comment "NOTE" above.
     */
    void ProcessClosest(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        Creature c = i.ep.e.getCpool().get(creatID);
        Thing o = c.getClosest();
        if (o != null) {
            System.out.println("The closest thing name is: " + o.getMyName());

            StringBuffer vs = new StringBuffer("");

            vs.append(" ");
            vs.append(o.getMyName());
            vs.append(" ");
            vs.append(o.category); //flag to indicate the type of Thing
            vs.append(" ");
            vs.append(o.isOccluded); //indicates if occluded by another Thing
            vs.append(" ");
            vs.append(o.getX1());
            vs.append(" ");
            vs.append(o.getX2());
            vs.append(" ");
            vs.append(o.getY1());
            vs.append(" ");
            vs.append(o.getY2());
            vs.append(" ");
            vs.append(o.getPitch());
            vs.append(" ");
            vs.append(o.getMaterial().getHardness());
            vs.append(" ");
            vs.append(o.getMaterial().getEnergy());
            vs.append(" ");
            vs.append(o.getMaterial().getTaste());
            vs.append(" ");
            vs.append(o.getMaterial().getColorName());

            getOutBuffer().append(vs);
        } else {

            System.out.println("No closest Thing detected.");
            getOutBuffer().append("No closest Thing detected.");
        }
    }

    /**
     * Command: "getvs"
     *
     * @param st the creature index within the environment (NOT the actual ID).
     * Please see comment "NOTE" above.
     */
    void ProcessGetVisualSensor(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        Creature c = i.ep.e.getCpool().get(creatID);
        synchronized (i.ep.e.getCpool().get(creatID).semaphore) {
            c.updateVisualSensor(i.ep.e);
            StringBuffer vs = new StringBuffer("");
            vs = getVisualContent(c);
            getOutBuffer().append(vs);
        }

    }

    private StringBuffer getVisualContent(Creature c) {
        StringBuffer vs = new StringBuffer("");
        vs.append(" ");
        vs.append(c.getThingsInCamera().size()); //number of things in camera

        ListIterator<Thing> iter = c.getThingsInCamera().listIterator();

        Thing o;
        while (iter.hasNext()) {
            o = (Thing) iter.next();
            vs.append(" ");
            vs.append(o.getMyName());
            vs.append(" ");
            vs.append(o.category); //flag to indicate the type of Thing
            vs.append(" ");
            vs.append(o.isOccluded); //indicates if occluded by another Thing
            vs.append(" ");
            vs.append(o.getX1());
            vs.append(" ");
            vs.append(o.getX2());
            vs.append(" ");
            vs.append(o.getY1());
            vs.append(" ");
            vs.append(o.getY2());
            vs.append(" ");
            vs.append(o.getPitch());
            vs.append(" ");
            vs.append(o.getMaterial().getHardness());
            vs.append(" ");
            vs.append(o.getMaterial().getEnergy());
            vs.append(" ");
            vs.append(o.getMaterial().getTaste());
            vs.append(" ");
            vs.append(o.getMaterial().getColorName());
        }

        return vs;

    }

    /**
     * Creature's visual sensor is started.
     *
     * @param st creature ID
     */
    synchronized void ProcessActivateCamera(StringTokenizer st) {
        try {
            int creatID = -1;
            String cID;
            if (st.hasMoreTokens()) {
                cID = st.nextToken();
                creatID = Integer.parseInt(cID);
            }
            if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
                getOutBuffer().append("Creature ID does not exist.");
                return;
            }
            Creature c = i.ep.e.getCpool().get(creatID);
            int c_index = i.ep.e.getCpool().indexOf(c);
            if (c_index % 2 == 0) {
                i.ep.e.setCamera(0, c_index);
                getOutBuffer().append("Even Camera attached to robot ").append(i.ep.e.getCamera(0));
            } else {
                i.ep.e.setCamera(1, c_index);
                getOutBuffer().append("Odd Camera attached to robot ").append(i.ep.e.getCamera(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    synchronized void ProcessGetAffordances(StringTokenizer st) {
        try {
            String eID = "";
            Thing th;

            if (st.hasMoreTokens()) {
                eID = st.nextToken();
            }

            if (!i.ep.e.oMap.containsKey(eID)) {
                getOutBuffer().append("The Thing ID does not exist.");
                return;
            }
            th = i.ep.e.oMap.get(eID);
            getOutBuffer().append(th.getAffordances());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    synchronized void ProcessGetFullStatus3D(StringTokenizer st) {
        try {
            int creatID = -1;
            String cID;

            if (st.hasMoreTokens()) {
                cID = st.nextToken();
                creatID = Integer.parseInt(cID);
            }
            if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
                getOutBuffer().append("Creature ID does not exist.");
                return;
            }
            Creature c = i.ep.e.getCpool().get(creatID);
            synchronized (i.ep.e.getCpool().get(creatID).semaphore) {
                c.updateVisualSensor(i.ep.e);
                c.updateContactSensor(i.ep.e);

                //Leaflet pool:
                StringBuffer lp = new StringBuffer("");
                StringBuffer vs = new StringBuffer("");
                lp.append(" ");
                lp.append(c.getActiveLeaflets().size()); //number of leaflets not delivered yet
                ListIterator<Leaflet> iterator = c.getActiveLeaflets().listIterator();
                Leaflet leaflet;
                while (iterator.hasNext()) {
                    lp.append(" ");
                    leaflet = (Leaflet) iterator.next();
                    lp.append(" ");
                    lp.append(leaflet.getID());
                    lp.append(" ");
                    lp.append(leaflet.getNumberOfItemTypes());
                    lp.append(" ");
                    lp.append(leaflet.toStringFormatted());
                }
                //Visual System:
                vs = getVisualContent(c);

                //Creature info:
                getOutBuffer().append(c.getMyName()).append(" ").append(c.getX()).append(" ").append(c.getY()).append(" ").append(c.getSize()).append(" ").append(c.getPitch()).append(" ").append(c.getMotorSys()).append(" ").append(c.getWheel()).append(" ").append(c.getSpeed()).append(" ").append(c.getFuel()).append(" ").append(c.getMaterial().getColorName()).append(" ").append(c.ifHasActiveLeaflet()).append(" ").append(lp).append(
                        vs);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void ProcessGetSimulPars() {
        StringBuffer MaterialsDef = new StringBuffer("");

        getOutBuffer().append(i.ep.getPreferredSize().width).append(" ").append(i.ep.getPreferredSize().height).append(" ").append(MaterialsDef);

    }

    void ProcessGetSimulPars3D() {

        getOutBuffer().append(i.ep.getPreferredSize().width).append(" ").append(i.ep.getPreferredSize().height).append(" ").append(i.ep.e.deliverySpotLocation[0]).append(" ").append(i.ep.e.deliverySpotLocation[1]);
    }

    void ProcessInitialEnvironmentParameters(StringTokenizer st) {

        int width, height;
        String w, h, pathToFloorTexture;
        if (st.hasMoreTokens()) {
            w = st.nextToken();
            width = Integer.parseInt(w);
        } else {
            getOutBuffer().append("Environment settings: width is missing!");
            return;
        }

        if (st.hasMoreTokens()) {
            h = st.nextToken();
            height = Integer.parseInt(h);
        } else {
            getOutBuffer().append("Environment settings: height is missing!");
            return;
        }
        if (st.hasMoreTokens()) {
            pathToFloorTexture = st.nextToken();
        } else {
            getOutBuffer().append("Environment settings: path to texture is missing!");
            return;
        }

        sf.gameState.updateEnvironment(width, height, pathToFloorTexture);
    }

    void ProcessSetFullStatus(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:").append(creatID).append("\r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                //String speed="",wheel="",vsdist="",vsang="";
                String speed = "", wheel = "";
                if (st.hasMoreTokens()) {
                    speed = st.nextToken();
                } else {
                    getOutBuffer().append("Speed is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    wheel = st.nextToken();
                } else {
                    getOutBuffer().append("Wheel is missing");
                    return;
                }
//                if (st.hasMoreTokens()) {
//                   vsdist = st.nextToken();
//                }
//                else {out.append("VSDist is missing");
//                  return;
//                }
//                if (st.hasMoreTokens()) {
//                   vsang = st.nextToken();
//                }
//                else {out.append("VSAng is missing");
//                  return;
//                }
                c.setSpeed(Double.parseDouble(speed));
                c.setWheel(Double.parseDouble(wheel));
//                c.setVSDistance(Double.parseDouble(vsdist));
//                c.setVSAngle(Double.parseDouble(vsang));
                i.ep.repaint();
                //out.append("status set: speed="+speed+" wheel="+wheel+" vsdist="+vsdist+" vsang="+vsang);
                getOutBuffer().append("status set: speed=").append(speed).append(" wheel=").append(wheel);
            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Currently not in use on the client. Format: setgoTo creatureID Vr Vl
     * Xdirection Ydirection The creature speed is the arithmetic mean of Vr and
     * Vl The angle of the turn is evaluated according to the direction
     * established between the points: robot location and (X,Y) position.
     *
     * @param st
     */
    void ProcessSetDifferentialStatusCML(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:").append(creatID).append("\r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String vr = "", vl = "", xf = "", yf = "";

                if (st.hasMoreTokens()) {
                    vr = st.nextToken();
                } else {
                    getOutBuffer().append("Right wheel velocity is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    vl = st.nextToken();
                } else {
                    getOutBuffer().append("Left wheel velocity is missing");
                    return;
                }


                if (st.hasMoreTokens()) {
                    xf = st.nextToken();
                } else {
                    getOutBuffer().append("X final is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    yf = st.nextToken();
                } else {
                    getOutBuffer().append("Y final is missing");
                    return;
                }

                c.setVright(Double.parseDouble(vr));
                c.setVleft(Double.parseDouble(vl));
                if (c.getVleft() != -c.getVright()) {
                    c.setSpeed((c.getVright() + c.getVleft()) / 2);
                } else {
                    c.setSpeed(Math.abs(c.getVleft()));
                }


                c.setPitch(Math.toDegrees(Math.atan2(Double.parseDouble(yf) - c.getY(), Double.parseDouble(xf) - c.getX())));

                c.setW(Math.toRadians(c.getPitch()));

                i.ep.repaint();

                getOutBuffer().append("status set: speed=").append(c.getSpeed()).append(" " + "pitch= ").append(c.getPitch());
            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * This command is often used on clients that autonomously control the
     * creature.
     *
     * * Format: setAngle creatureID Vr Vl W
     *
     * @param st
     */
    void ProcessSetDifferentialStatus3D(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:").append(creatID).append(" \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String vr = "", vl = "", w = "", xf = "", yf = "";

                if (st.hasMoreTokens()) {
                    vr = st.nextToken();
                } else {
                    getOutBuffer().append("Right wheel velocity is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    vl = st.nextToken();
                } else {
                    getOutBuffer().append("Left wheel velocity is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    w = st.nextToken();
                } else {
                    getOutBuffer().append("W is missing");
                    return;
                }

                c.setVright(Double.parseDouble(vr));
                c.setVleft(Double.parseDouble(vl));
                c.setSpeed((c.getVright() + c.getVleft()) / 2);


                if (Double.parseDouble(w) != Constants.WNULL) {
                    c.setW(Double.parseDouble(w));
                }
                c.setPitch(Math.toDegrees(c.getW()));

                i.ep.repaint();

                getOutBuffer().append("status set: speed=").append(c.getSpeed()).append(" " + "W= ").append(c.getW());


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessSackIt(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String thingName = "";

                if (st.hasMoreTokens()) {
                    thingName = st.nextToken();
                } else {
                    getOutBuffer().append("Thing to grasp is missing");
                    return;
                }

                // System.out.println("...............................thing to grasp: "+ thingName);

                Thing th = i.ep.e.getThingFromName(thingName);
                if (th != null) {
                    //c.graspIt((Jewel)th);
                    c.putInSack(th, i.ep.e);
                    i.ep.repaint();
                }
                getOutBuffer().append("status : Name of thing to grasp: ").append(thingName);


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessEatIt(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:").append(creatID).append(" \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String thingName = "";

                if (st.hasMoreTokens()) {
                    thingName = st.nextToken();
                } else {
                    getOutBuffer().append("Food to be eaten is missing");
                    return;
                }


                //System.out.println("........................... food name: "+ thingName);

                Thing th = i.ep.e.getThingFromName(thingName);
                if (th != null) {
                    c.eatIt((Food) th, i.ep.e);
                    i.ep.repaint();
                }
                getOutBuffer().append("status : Name of thing to eat: " + thingName);


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessHideIt(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String thingName = "";

                if (st.hasMoreTokens()) {
                    thingName = st.nextToken();
                } else {
                    getOutBuffer().append("Thing to be hidden is missing");
                    return;
                }


                //System.out.println("........................... thing name: "+ thingName);

                Thing th = i.ep.e.getThingFromName(thingName);
                if (th != null) {
                    th.hideMe(i.ep.e);
                    i.ep.repaint();
                }
                getOutBuffer().append("status : Name of thing to hide: " + thingName);


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessUnhideIt(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String thingName = "";

                if (st.hasMoreTokens()) {
                    thingName = st.nextToken();
                } else {
                    getOutBuffer().append("Thing to be unhidden is missing");
                    return;
                }


                //System.out.println("........................... thing name: "+ thingName);

                Thing th = i.ep.e.getThingFromName(thingName);
                if (th != null) {
                    th.undoHideMe(i.ep.e);
                    i.ep.repaint();
                }
                getOutBuffer().append("status : Name of thing to unhide: " + thingName);


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessDrop(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String s;
                int category;
                int colorIdx;
                double x, y;
                if (st.hasMoreTokens()) {
                    s = st.nextToken();
                    category = Integer.parseInt(s);
                    if (st.hasMoreTokens()) {
                        s = st.nextToken();
                        colorIdx = Integer.parseInt(s);
                        c.drop(category, colorIdx);
                        getOutBuffer().append("Drop Thing at: x: " + c.getX()
                                + " y: " + c.getY());

                    } else {
                        getOutBuffer().append("... No recognized command");
                    }
                } else {
                    getOutBuffer().append("... No recognized command");
                }

            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Angle of the turn is determined by the wheel speeds Vr (right wheel) and
     * Vl (left wheel).
     *
     * Format: setTurn creatureID speed Vr Vl
     *
     * @param st
     */
    void ProcessSetDifferentialStatus3DT(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        try {
            synchronized (c) {
                String vr = "", vl = "", speed = "";

                if (st.hasMoreTokens()) {
                    speed = st.nextToken();
                } else {
                    getOutBuffer().append("Creature speed is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    vr = st.nextToken();
                } else {
                    getOutBuffer().append("Right wheel velocity is missing");
                    return;
                }
                if (st.hasMoreTokens()) {
                    vl = st.nextToken();
                } else {
                    getOutBuffer().append("Left wheel velocity is missing");
                    return;
                }

                c.setVright(Double.parseDouble(vr));
                c.setVleft(Double.parseDouble(vl));

                if (c.getMotorSys() == 2) {
                    ((RobotCreature) c).setSpeedExternally();
                }
                c.setSpeed(Double.parseDouble(speed));

                double turn = (c.getVleft() - c.getVright()) / c.getSize();
                double currW = c.getW();
                c.setW(turn + currW);
                c.setPitch(Math.toDegrees(c.getW()));

                i.ep.repaint();

                getOutBuffer().append("Angle according to Vr and Vl: " + "deltaW[rad]= " + turn + " in degrees: " + Math.toDegrees(turn)
                        + " \r\n"
                        + "Status set: creature speed=" + c.getSpeed() + " " + "W[rad]= " + c.getW() + " pitch[degrees]= " + c.getPitch());


            } //end syncronized
        } //end try
        catch (Exception e) {
            e.printStackTrace();
        }

    }

    void ProcessSetLeaflet(StringTokenizer st) {
        int jewel1 = 0;
        int jewel2 = 1;
        int jewel3 = 2;
        Leaflet leaflet = null;
        int creatID = -1;
        String cID;
        String s1 = "", s2 = "", s3 = "";
        List<Leaflet> leafletList = new ArrayList<Leaflet>();

        if (!st.hasMoreTokens()) {
            for (int it = 0; it <= (Constants.MAX_NUMBER_OF_LEAFLETS - 1); it++) {
                leaflet = generateRandomLeaflet();
                leafletList.add(leaflet);
                waitAMilli();
            }
            getOutBuffer().append("\r\n");
            i.ep.e.resetLeafletPool();
            for (Leaflet l : leafletList) {
                i.ep.e.addLeaflet(l);
                getOutBuffer().append(l.toStringFormatted());
            }
            i.ep.e.notifyLeafletObservers();
            //   synchronized (i.ep.e.semaphore) {

            for (Creature cr : i.ep.e.getCpool()) {
                synchronized (cr) {
                    cr.setActiveLeaflets(i.ep.e.getLeafletsOfOwner(cr.getID()));
                }
            }
            //  }

        } else {

            if (st.hasMoreTokens()) {
                cID = st.nextToken();
                creatID = Integer.parseInt(cID);
            }
            if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
                getOutBuffer().append("Creature ID does not exist.");
                return;
            }
            getOutBuffer().append("Creature ID:" + creatID + " \r\n");
            Creature c = i.ep.e.getCpool().get(creatID);
            if (!st.hasMoreTokens()) {
                getOutBuffer().append("Leaflet is missing!!!");
                return;
            } else {
                s1 = st.nextToken();
                jewel1 = Integer.parseInt(s1);

                if (Constants.getColorItem(jewel1) == null) {
                    getOutBuffer().append("First type entered is not alowed! Try again.");
                    return;
                }
                //System.out.println("Jewel1: " + Constants.getColorItem(jewel1) + "\r\n");
                if (st.hasMoreTokens()) {
                    s2 = st.nextToken();
                    jewel2 = Integer.parseInt(s2);
                }
                if (Constants.getColorItem(jewel2) == null) {
                    getOutBuffer().append("Second type entered is not alowed! Try again.");
                    return;
                }
                //System.out.println("Jewel2: " + Constants.getColorItem(jewel2) + "\r\n");
                if (st.hasMoreTokens()) {
                    s3 = st.nextToken();
                    jewel3 = Integer.parseInt(s3);
                }
                if (Constants.getColorItem(jewel3) == null) {
                    getOutBuffer().append("Third type entered is not alowed! Try again.");
                    return;
                }
                //System.out.println("Jewel3: " + Constants.getColorItem(jewel3) + "\r\n");
                //ATTENTION: formatted message: (client depends on this format)
                int[] jewels = {jewel1, jewel2, jewel3};
                leaflet = new Leaflet(jewels, c.getID());
                leafletList.add(leaflet);
            }
            synchronized (c) {
                getOutBuffer().append(" \r\n");
                for (Leaflet l : leafletList) {
                    i.ep.e.addLeaflet(l);
                    getOutBuffer().append(l.toStringFormatted());
                }
                i.ep.e.notifyLeafletObservers();
                c.setActiveLeaflets(i.ep.e.getLeafletsOfOwner(c.getID()));

            }
        }

    }

    Leaflet generateRandomLeaflet() {
        LeafletGenerator lg = new LeafletGenerator();

        return lg.getLeaflet();

    }

    Leaflet generateRandomLeaflet(Long owner) {
        LeafletGenerator lg = new LeafletGenerator(owner);

        return lg.getLeaflet();

    }

    void ProcessHelp() {
        System.out.println("====  ProcessHelp() ===");
        getOutBuffer().append("List of available commands :\r\n"
                + "  \r\n"
                + "  -----------Game settings---------------------------------------------------------------------------\r\n"
                + "  help - Show this list\r\n"
                + "  memory - Show available free memory\r\n"
                + "  getclock - Show the server current time\r\n"
                + "  quit - Quit the communication session\r\n"
                + "  startGame - Start the game: creatures start moving.\r\n"
                + "  shutdown - Shutdown the server application\r\n"
                + "  version - Show current version\r\n"
                + "  setenv <environment width> <environment height> <path to texture image of the floor> \r\n"
                + "  getenvironment - Get Environment Geometry\r\n"
                + "  getworldsituation - Get the actual number of creatures and things\r\n"
                + "  -----------Create and delete waypoints-------------------------------------------------------------\r\n"
                + "  newwp <X> <Y> - New waypoint at position (x y) \r\n"
                + "  delwp <X> <Y> - Delete waypoint at position (x y) \r\n"
                + "  -----------Create entities (objects at the environment)-------------------------------------------\r\n"
                + "  brick <type> <x1> <y1> <x2> <y2>- Type: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White with "
                + "         vertexes x1, x2, y1, y2 \r\n"
                + "  deliver <creature ID> - Deliver the leaflet and increase the creature's score. \r\n"
                + "  drop <creature ID> <Thing index> <Thing color (for jewels) or any number for food> - Index: 3-Jewel; "
                + "  21-Perishable food; 22- Non-Perishable food; Color: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White\r\n"
                + "  food <type> <x> <y> - Type: 0-perishable 1-non-perishable at position (x,y) \r\n"
                + "  jewel <type> <x> <y> - Type: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White at position (x,y) \r\n"
                + "  cage <x> <y> - Create a cage at position (x,y) \r\n"
                + "  leaflet <creature ID> <jewel1> <jewel2> <jewel3> - Create a leaflet. Do not enter anything in order\r\n"
                + "    to randomize. Example: >leaflet or >leaflet robotID Red Green Blue\r\n\r\n"
                + "  -----------Creature control-------------------------------------------------------------------------\r\n"
                + "  new <X> <Y> <PITCH> - Insert new creature at positions\r\n"
                + "  camera <creature ID> - Start robot's visual sensor\r\n"
                + "  start <creature ID> - Start creature motor system. \r\n"
                + "  stop <creature ID> - Stop creature motor system.\r\n"
                + "  setgoTo <creature ID> <Vr> <Vl> <Xdirection> <Ydirection> - Creature turns towards the direction \r\n"
                + "   of point (x, y). But does not stop at the point. Linear velocity is the arithmetic mean of Vr \r\n"
                + "   and Vl.\r\n"
                + "  setTurn <creature ID> <speed> <Vr> <Vl> - Set actuation parameters. Angle of the turn derived from \r\n"
                + "   the wheel speed values (Vr-right Vl-left) and Linear velocity according to <speed> parameter.\r\n"
                + "   The final pitch of the robot depends on its original orientation (pitch).\r\n"
                + "   HINTs: +~30deg: Vl= 11 and Vr= 0.6; +~45deg: Vl= 16 and Vr= 0.2; +~60deg: Vl= 22 and Vr= 1;\r\n"
                + "          +~90deg: Vl= 32 and Vr= 0.6; +~180deg: Vl= 63 and Vr= 0.2; +~270deg: Vl= 95 and Vr= 0.8;\r\n"
                + "  setAngle <creature ID> <Vr> <Vl> <w (radians)> - Set the creature's pitch to a specific angle.\r\n"
                + "    Reference is 0 radian that is the X axis (horizontal) and positive angles go clockwise.\r\n"
                + "    This command is often used when autonomously controlling the creature.\r\n"
                + "    Linear velocity is the arithmetic mean of Vr and Vl.\r\n"
                + "  getfullstatus3D <creature ID> - Creature data: X Y size pitch motorSys wheel speed fuel color camera \r\n"
                + "  affordances <Thing ID (Format: Name__Timestamp)> - Get list of affordances of a Thing\r\n"
                + "  refuel <creature ID> - Set full energy for a specific creature.\r\n");


    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        Main main = new Main();
        main.simulate();

        System.runFinalization();
        System.exit(0);
    }

    void ProcessNewCreature(StringTokenizer st) {
        String s, motor;
        double x, y, pitch;
        int motorSys;
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);
                if (st.hasMoreTokens()) {
                    s = st.nextToken();
                    pitch = Double.parseDouble(s);

                    ThingCreator tc = new ThingCreator(i.ep.e);
                    RobotCreature c = tc.createCreature(false, x, y, pitch);

                    this.sf.gameState.ThingsRN.updateRenderState();


                    getOutBuffer().append("New Creature created: x: " + c.getX()
                            + " y: " + c.getY()
                            + " pitch: " + c.getPitch());
                } else {
                    getOutBuffer().append("... No recognized command");
                }
            } else {
                getOutBuffer().append("... No recognized command");
            }
        }

    }

    void ProcessNewWaypoint(StringTokenizer st) {
        String s;
        double x, y;
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);
                //Creature c = new Creature(x, y, pitch, i.ep.e);
                i.ep.e.wpTS = i.ep.display.getRenderer().createTextureState();
                //IconFactory wp = new IconFactory(i.ep.e.wpTS);
                IconFactory wp = new IconFactory(i.ep.e.wpTS, x, y, i.ep.e.width, i.ep.e.height);
                i.ep.e.addWaypointIcon(wp);
                this.sf.gameState.ThingsRN.updateRenderState();
                getOutBuffer().append("New Waypoint created: x: " + wp.x
                        + " y: " + wp.y);

            } else {
                getOutBuffer().append("... No recognized command");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessDeleteWaypoint(StringTokenizer st) {
        String s;
        double x, y;
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);
                //Creature c = new Creature(x, y, pitch, i.ep.e);
                i.ep.e.wpTS = i.ep.display.getRenderer().createTextureState();
                //IconFactory wp = new IconFactory(i.ep.e.wpTS);
                String str = i.ep.e.removeWaypointIcon(x, y);
                this.sf.gameState.ThingsRN.updateRenderState();
                getOutBuffer().append("Try to delete Waypoint at: x: " + x
                        + " y: " + y + " ... ");
                getOutBuffer().append(str);

            } else {
                getOutBuffer().append("... No recognized command");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessNewCage(StringTokenizer st) {
        String s;
        double x, y;
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);

                ThingCreator tc = new ThingCreator(i.ep.e);
                Cage cage;
                cage = (Cage) tc.createThing(Constants.categoryCAGE, x, y);
                this.sf.gameState.ThingsRN.updateRenderState();
                getOutBuffer().append("New cage created at x: " + cage.getX()
                        + " y: " + cage.getY());

            } else {
                getOutBuffer().append("... Must specify where to place the cage (x, y)");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessNewFood(StringTokenizer st) {
        String s;
        int type;
        double x, y;

        if (st.hasMoreTokens()) {
            s = st.nextToken();
            type = Integer.parseInt(s);
        } else {
            getOutBuffer().append("Must specify the type of food: 0-perishable or 1-non-perishable.");
            return;
        }
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);

                ThingCreator tc = new ThingCreator(i.ep.e);
                Food food;
                if (type == 0) {
                    food = (Food) tc.createThing(Constants.categoryPFOOD, x, y);
                } else if (type == 1) {
                    food = (Food) tc.createThing(Constants.categoryNPFOOD, x, y);
                } else {
                    getOutBuffer().append("Invalid type of food: 0-perishable or 1-non-perishable.");
                    return;
                }

                this.sf.gameState.ThingsRN.updateRenderState();
                getOutBuffer().append("New food created at x: " + food.getX()
                        + " y: " + food.getY());

            } else {
                getOutBuffer().append("... No recognized command");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessNewFoodInBatch(StringTokenizer st) {
        String s;
        int type;
        double x, y;
        int number;

        if (st.hasMoreTokens()) {
            s = st.nextToken();
            type = Integer.parseInt(s);
        } else {
            getOutBuffer().append("Must specify the type of food: 0-perishable or 1-non-perishable.");
            return;
        }
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            number = Integer.parseInt(s);
        } else {
            getOutBuffer().append("Number of food is missing.");
            return;
        }
        Food food;

        for (int j = 0; j < number; j++) {
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                x = Double.parseDouble(s);
            } else {
                getOutBuffer().append("x is missing.");
                return;
            }
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);
            } else {
                getOutBuffer().append("y is missing.");
                return;
            }

            ThingCreator tc = new ThingCreator(i.ep.e);

            if (type == 0) {
                food = (Food) tc.createThing(Constants.categoryPFOOD, x, y);
            } else if (type == 1) {
                food = (Food) tc.createThing(Constants.categoryNPFOOD, x, y);
            } else {
                getOutBuffer().append("Invalid type of food: 0-perishable or 1-non-perishable.");
                return;
            }
            getOutBuffer().append("New food created at x: " + food.getX()
                    + " y: " + food.getY());
        }
        this.sf.gameState.ThingsRN.updateRenderState();
    }

    void ProcessNewBrick(StringTokenizer st) {
        String s;
        int type;
        double x1, y1, x2, y2;

        if (st.hasMoreTokens()) {
            s = st.nextToken();
            type = Integer.parseInt(s);
            if ((type < 0) || (type > 5)) {
                getOutBuffer().append("Invalid type of brick! Try: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White ");
                return;
            }
        } else {
            getOutBuffer().append("Specify the type of brick: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White ");
            return;
        }
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x1 = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y1 = Double.parseDouble(s);
                if (st.hasMoreTokens()) {
                    s = st.nextToken();
                    x2 = Double.parseDouble(s);
                    if (st.hasMoreTokens()) {
                        s = st.nextToken();
                        y2 = Double.parseDouble(s);
                        ThingCreator tc = new ThingCreator(i.ep.e);
                        Brick brick = (Brick) tc.createBrick(type, x1, y1, x2, y2);
                        this.sf.gameState.ThingsRN.updateRenderState();
                        getOutBuffer().append("New brick created at x: " + brick.getX()
                                + " y: " + brick.getY());
                    } else {
                        getOutBuffer().append("... No recognized command");
                    }
                } else {
                    getOutBuffer().append("... No recognized command");
                }



            } else {
                getOutBuffer().append("... No recognized command");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessNewJewel(StringTokenizer st) {
        String s;
        int type;
        double x, y;

        if (st.hasMoreTokens()) {
            s = st.nextToken();
            type = Integer.parseInt(s);
            if ((type < 0) || (type > 5)) {
                getOutBuffer().append("Invalid type of jewel! Try: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White ");
                return;
            }
        } else {
            getOutBuffer().append("Specify the type of jewel: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White ");
            return;
        }
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            x = Double.parseDouble(s);
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);

                ThingCreator tc = new ThingCreator(i.ep.e);
                Jewel jewel = (Jewel) tc.createCrystalOfType(type, x, y);
                this.sf.gameState.ThingsRN.updateRenderState();
                getOutBuffer().append("New jewel created at x: " + jewel.getX()
                        + " y: " + jewel.getY());

            } else {
                getOutBuffer().append("... No recognized command");
            }
        } else {
            getOutBuffer().append("... No recognized command");
        }
    }

    void ProcessNewJewelsInBatch(StringTokenizer st) {

        String s;
        int type;
        double x, y;
        int number;

        if (st.hasMoreTokens()) {
            s = st.nextToken();
            type = Integer.parseInt(s);
            if ((type < 0) || (type > 5)) {
                getOutBuffer().append("Invalid type of jewel! Try: 0-Red 1-Green 2-Blue 3-Yellow 4-Magenta 5-White ");
                return;
            }
        } else {
            getOutBuffer().append("Must specify the type of jewel.");
            return;
        }
        if (st.hasMoreTokens()) {
            s = st.nextToken();
            number = Integer.parseInt(s);
        } else {
            getOutBuffer().append("Number of jewels is missing.");
            return;
        }

        for (int j = 0; j < number; j++) {
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                x = Double.parseDouble(s);
            } else {
                getOutBuffer().append("x is missing.");
                return;
            }
            if (st.hasMoreTokens()) {
                s = st.nextToken();
                y = Double.parseDouble(s);
            } else {
                getOutBuffer().append("y is missing.");
                return;
            }

            ThingCreator tc = new ThingCreator(i.ep.e);

            Jewel jewel = (Jewel) tc.createCrystalOfType(type, x, y);
            getOutBuffer().append("New jewel created at x: " + jewel.getX() + " y: " + jewel.getY());
        }
        this.sf.gameState.ThingsRN.updateRenderState();
    }

    /**
     * Creatures starts moving according to the parameters previously configured
     * by setDiff3D.
     *
     * @param st the creature ID.
     */
    void ProcessStartCreature(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        c.hasStarted = true;
        getOutBuffer().append("\r\n Run creature run...\r\n");
    }

    /**
     * Creature delivers the current leaflet.
     *
     * @param st the creature ID.
     */
    void ProcessDeliverLeaflet(StringTokenizer st) {
        int creatID = -1;
        String cID;
        String leafletID = "";
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        if (st.hasMoreTokens()) {
            leafletID = st.nextToken();
        } else {
            getOutBuffer().append("Leaflet ID is missing");
            return;
        }
        Creature c = i.ep.e.getCpool().get(creatID);
        Leaflet l = (Leaflet) i.ep.e.getLeafletPool().get(Long.parseLong(leafletID));
        c.deliver(l);
        getOutBuffer().append("\r\n Leaflet delivered!\r\n");
    }

    /**
     * Creatures stops moving ahead.
     *
     * @param st the creature ID.
     */
    void ProcessStopCreature(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID);
        Creature c = i.ep.e.getCpool().get(creatID);
        c.hasStarted = false;
        getOutBuffer().append(" Creature has stopped!!!");
    }

    synchronized void ProcessSetThreadName(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist. Must specify a creature.");
            return;
        }
        String name = "RobotMind_" + creatID;
        this.clients.get(clients.size() - 1).setName(name);
        getOutBuffer().append("Mind thread name set to " + name);
    }

    void ProcessRefuel(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("Creature ID does not exist. Must specify a creature.");
            return;
        }
        getOutBuffer().append("Creature ID:" + creatID + " \r\n");
        Creature c = i.ep.e.getCpool().get(creatID);
        c.refill();
    }

    void ProcessGetWorldSituation() {
        getOutBuffer().append("World Situation ... " + i.ep.e.getCpool().size() + " creatures and " + i.ep.e.getOpool().size() + " things");

    }

    void ProcessShowWorldThings() {
        int ii = 0;
        getOutBuffer().append("World Situation ... " + i.ep.e.getCpool().size() + " creatures and " + i.ep.e.getOpool().size() + " Things" + "\n");
        for (Creature c : i.ep.e.getCpool()) {
            getOutBuffer().append("Creature " + ii++ + ": " + c.getX() + "," + c.getY() + "->" + c.getPitch() + "\n");
        }
        ii = 0;
        for (Thing o : i.ep.e.getOpool()) {
            getOutBuffer().append("Thing " + ii++ + ": " + o.getX1() + "," + o.getY1() + "," + o.getX2() + "," + o.getY2() + "\n");
        }
    }

    synchronized void ProcessGetAllThings() {
        try {

            StringBuffer all = new StringBuffer("");
            //   synchronized (i.ep.e.semaphore) {

            all.append(" ");
            int number = i.ep.e.getCpool().size() + i.ep.e.getOpool().size();
            all.append(number); //number of things in camera

            List<Thing> allThings = new ArrayList<Thing>();
            for (Thing c : i.ep.e.getCpool()) {
                allThings.add(c);
            }
            for (Thing th : i.ep.e.getOpool()) {
                allThings.add(th);
            }
            ListIterator<Thing> iter = allThings.listIterator();
            Thing o;
            while (iter.hasNext()) {
                o = (Thing) iter.next();
                all.append(" ");
                all.append(o.getMyName());
                all.append(" ");
                all.append(o.category); //flag to indicate the type of Thing
                all.append(" ");
                all.append(o.isOccluded); //indicates if occluded by another Thing
                all.append(" ");
                all.append(o.getX1());
                all.append(" ");
                all.append(o.getX2());
                all.append(" ");
                all.append(o.getY1());
                all.append(" ");
                all.append(o.getY2());
                all.append(" ");
                all.append(o.getMaterial().getColorTypeIndex());
            }
            //    }

            getOutBuffer().append(all);

            return;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void ProcessCheckCreature(StringTokenizer st) {
        int creatID = -1;
        String cID;
        if (st.hasMoreTokens()) {
            cID = st.nextToken();
            creatID = Integer.parseInt(cID);
        }
        if (creatID < 0 || creatID > i.ep.e.getCpool().size() - 1) {
            getOutBuffer().append("no");
            return;
        } else {
            getOutBuffer().append("yes");
            return;
        }
    }

    //Force the system to wait a millisecond
    public void waitAMilli() {
        long t0, t1;
        t0 = System.currentTimeMillis();
        do {
            t1 = System.currentTimeMillis();
        } while (t1 - t0 < 1);
    }
}
