using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine;
using UnityEngine.Assertions;

public enum PacketCode {Message = 0, ServerAvailable = 1, isServer = 2, tellServer = 3, tellClient = 4, serverEnd = 5, clientEnd = 6, 
	removePath = 100, changePathAndPos = 101, changeTarget = 102, deployUnit = 103, changeGameSpeed = 104, changePause = 105, destroyUnit = 106, logMessage = 107, setHealth = 108, setFiring = 109, 
	setDisabled = 110, setNullTarget = 111, setTargetSpeed = 112, setStore = 113, storeUnit = 114, createDeployed = 115, createStored = 116, createModuleData = 117, addToBeFired = 118, fireMissile = 119, 
	setEMCON = 120, setAntimissileSalvo = 121, setStoredLoadingState = 122, addToMissileControl = 123, removeFromMissileControl = 124, addToIlluminated = 125, removeFromIlluminated = 126, addToTransferts = 127, removeFromTransferts = 128, setCIWSAmmo = 129,
	setLoadout = 130, addBird = 131, removeBird = 132, setMotherShip = 133, setPositionTarget = 134,
	requestMovement = 200, requestChangeTarget = 201, requestOrder = 202, requestChangeGameSpeed = 203, requestChangePause = 204, requestSetDisabled = 205, requestSetNullTarget = 206, requestStop = 207, requestSetTargetSpeed = 208, requestSetEMCON = 209,
	requestSetAntimissileSalvo = 210, requestSetPositionTarget = 211};

public abstract class NetworkManager {
	private static int selfChannelID;
	private static int hostID;

	private static List<int> clientIDs;
	private static int serverID;
	private static int masterServerID;

	private static bool isAServer = true;

	private static bool connectedToMasterServer = false;
	private static bool connectedToServer = false;

	private static bool connectingToMasterServer = false;
	private static bool connectingToServer = false;

	private static List<int> tempConnections = new List<int>();
	private static int playerJoining = 0;

	private static List<Tuple<int, Buffer>> toSend;

	public static void initConnection() {
		GlobalConfig globalConfig = new GlobalConfig();
		globalConfig.ReactorMaximumReceivedMessages = 1000;
		globalConfig.ReactorMaximumSentMessages = 1000;
		globalConfig.ReactorModel = ReactorModel.SelectReactor;

		NetworkTransport.Init(globalConfig);
		ConnectionConfig config = new ConnectionConfig();
		selfChannelID  = config.AddChannel(QosType.ReliableSequenced);
		HostTopology topology = new HostTopology(config, 64);
		//hostID = NetworkTransport.AddHost(topology, 49611);
		hostID = NetworkTransport.AddHost(topology);

		byte error;
		masterServerID = NetworkTransport.Connect(hostID, "81.28.207.55", 49610, 0, out error); //connect to master server
		//masterServerID = NetworkTransport.Connect(hostID, "128.179.187.247", 49610, 0, out error); //connect to epfl master server
		//masterServerID = NetworkTransport.Connect(hostID, "10.100.1.166", 49610, 0, out error); //connect to fake master server

		toSend = new List<Tuple<int, Buffer>>();
	}

	public static void indicateIsServer(bool value) {
		isAServer = value;

		Buffer buffer = new Buffer(PacketCode.isServer);
		buffer.writeBool(value);

		byte error;
		NetworkTransport.Send(hostID, masterServerID, selfChannelID, buffer.getArray(), buffer.getLength(), out error);

		if (value) {
			clientIDs = new List<int>();
		}
	}

	public static void manageNetworkInputs() {
		int recHostId;
		int connectionId;
		int channelId;
		byte[] recBuffer = new byte[1024];
		int bufferSize = 1024;
		int dataSize;
		byte error;

		NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

		if (error != 0 && error != 6) {
			Debug.LogError("Network error : " + error);
		}

		switch (recData) {
			case NetworkEventType.ConnectEvent:
				if (connectionId == masterServerID) {
					Debug.Log("Connected to master server");
					connectedToMasterServer = true;
				}
				else if (connectionId == serverID) {
					Debug.Log("Connected to server");
					connectedToServer = true;
					Immortal.startGame();
				}
				else  if (!tempConnections.Contains(connectionId) && isAServer) {
					Debug.Log("Client is connected");

					clientIDs.Add(connectionId);

					Requests.changePause(true);

					playerJoining ++;

					foreach(DeployedUnit unit in GameController.getDeployedUnits()) {
						Buffer buffer = new Buffer(PacketCode.createDeployed);
						buffer.writeInt(unit.getID());
						buffer.writeInt(unit.getClass().getID());

						toClient(connectionId, buffer);
					}

					/*foreach(StoredUnit unit in GameController.getStoredUnits()) {
						Buffer buffer = new Buffer(PacketCode.createStored);
						buffer.writeInt(unit.getID());
						buffer.writeInt(unit.getClass().getID());

						toClient(connectionId, buffer);
					}*/

					for (int i = 0; i <= GameController.getModuleDatas().Count / Constants.MODULE_DATA_INFO_PER_PACKET; i++) {
						Buffer buffer = new Buffer(PacketCode.createModuleData);
						int size = Mathf.Min(Constants.MODULE_DATA_INFO_PER_PACKET, GameController.getModuleDatas().Count - i * Constants.MODULE_DATA_INFO_PER_PACKET);
						buffer.writeInt(size);
						for (int j = 0; j < size; j++) {
							ModuleData data = GameController.getModuleDatas()[i * Constants.MODULE_DATA_INFO_PER_PACKET + j];
							buffer.writeInt(data.getID());
							buffer.writeInt(data.getParent());

							if (data is AngularModuleData) {
								buffer.writeFloat(((AngularModuleData)data).getAngleOffset());
							}
							else {
								buffer.writeFloat(0.0f);
							}
							buffer.writeVector(data.getPosition());
							buffer.writeInt(data.getTemplate().getID());
						}
						toClient(connectionId, buffer);
					}

					for (int i = 0; i <= GameController.getStoredUnits().Count / Constants.STORED_UNIT_INFO_PER_PACKET; i++) {
						Buffer buffer = new Buffer(PacketCode.createStored);
						int size = Mathf.Min(Constants.STORED_UNIT_INFO_PER_PACKET, GameController.getStoredUnits().Count - i * Constants.STORED_UNIT_INFO_PER_PACKET);
						buffer.writeInt(size);
						for (int j = 0; j < size; j++) {
							StoredUnit stored = GameController.getStoredUnits()[i * Constants.STORED_UNIT_INFO_PER_PACKET + j];
							buffer.writeInt(stored.getID());
							buffer.writeInt(stored.getClass().getID());
							buffer.writeInt(stored.getStore().getID());
						}
						toClient(connectionId, buffer);
					}

					Buffer endBuffer = new Buffer(PacketCode.serverEnd);
					toClient(connectionId, endBuffer);

					/*Buffer moduleDatasBuffer = new Buffer(PacketCode.createModuleData, GameController.getModuleDatas().Count * 4 * 8 + 4);
					moduleDatasBuffer.writeInt(GameController.getModuleDatas().Count);
					for (int i = 0; i < GameController.getModuleDatas().Count; i++) {
						ModuleData data = GameController.getModuleDatas()[i];
						moduleDatasBuffer.writeInt(data.getID());
						moduleDatasBuffer.writeInt(data.getParent());

						if (data is AngularModuleData) {
							moduleDatasBuffer.writeFloat(((AngularModuleData)data).getAngleOffset());
						}
						else {
							moduleDatasBuffer.writeFloat(0.0f);
						}
						moduleDatasBuffer.writeVector(data.getPosition());

						moduleDatasBuffer.writeInt(data.getTemplate().getID());
					}
					toClient(connectionId, moduleDatasBuffer);*/


					foreach(DeployedUnit unit in GameController.getDeployedUnits()) {
						Requests.updatePath(unit);
					}
					/*foreach(StoredUnit unit in GameController.getStoredUnits()) {
						Requests.setStore(unit, unit.getStore()); //TODO : change (too many packets)
					}
					foreach(ModuleData data in GameController.getModuleDatas()) {
						Requests.setHealth(data, data.getHealth()); //TODO : change (too many packets)
					}*/
				}
			break;
			case NetworkEventType.DataEvent:
				Buffer received = new Buffer(recBuffer);

				decode(received, dataSize);
			break;
			case NetworkEventType.DisconnectEvent:
				if (connectionId == masterServerID) {
					Debug.Log("Disconnected from master server");
				}
				else if (connectionId == serverID) {
					Debug.Log("Disconnected from server");
					connectingToServer = false;
					Application.Quit(); //Temporary, avoids desynch
				}
				else if (clientIDs != null && clientIDs.Contains(connectionId)) {
					Debug.Log("Client is disconnected");
					clientIDs.Remove(connectionId);
				}
			break;
		}
	}

	private static void decode(Buffer buffer, int temp) {
		byte error;
		int unitID;

		int code = buffer.readInt();

		if (Buffer.getSize((PacketCode)code) != temp && Buffer.getSize((PacketCode)code) != 1024) {
			Debug.LogError("Code : " + code + " Expected size : " + Buffer.getSize((PacketCode)code) + " ; Real size : " + temp);
		}

		switch((PacketCode)code) {
			default:
				Debug.LogError("Unknown message format");
			break;
			case PacketCode.Message:
				string msg = buffer.readString();

				Debug.Log(msg);

			break;
			case PacketCode.ServerAvailable:
				string ip = buffer.readString();
				int port = buffer.readInt();

				Debug.Log("Game server available at " + ip + ":" + port);

				Immortal.addServerToList(ip, port);

			break;
			case PacketCode.tellClient:
				ip = buffer.readString();
				port = buffer.readInt();

				Debug.Log("Sending message to client at " + ip + ":" + port);

				int tempID = NetworkTransport.Connect(hostID, ip, port, 0, out error);
				tempConnections.Add(tempID);
			break;
			case PacketCode.serverEnd:
				foreach(ModuleData data in GameController.getModuleDatas()) {
					Unit unit = GameController.unitFromID(data.getParent());

					if (unit != null) { //unit may already be destroyed
						unit.addModuleData(data);
					}
				}

				toServer(new Buffer(PacketCode.clientEnd));
			break;
			case PacketCode.clientEnd:
				playerJoining --;
			break;

			case PacketCode.removePath:
				unitID = buffer.readInt();

				Debug.Log("Path removed");
				Assert.IsNotNull(GameController.fromID(unitID));

				GameController.fromID(unitID).removePath();
			break;
				
			case PacketCode.changePathAndPos:
				unitID = buffer.readInt();
				Vertex position = buffer.readVertex();
				float angle = buffer.readFloat();
				Path newPath = buffer.readPath();

				Debug.Log("New path & pos received");
				Assert.IsNotNull(GameController.fromID(unitID));

				GameController.fromID(unitID).setPosition(position);
				GameController.fromID(unitID).setAngle(angle);

				if (newPath == null) {
					GameController.fromID(unitID).removePath();
				}
				else {
					GameController.fromID(unitID).setPath(newPath, false);
				}

			break;
			case PacketCode.changeTarget:
				unitID = buffer.readInt();
				int targetID = buffer.readInt();

				Debug.Log("New target received");
				Assert.IsNotNull(GameController.fromID(unitID));
				Assert.IsNotNull(GameController.fromID(targetID));

				GameController.fromID(unitID).setTarget(GameController.fromID(targetID));

			break;
			case PacketCode.deployUnit:
				int storedUnitID = buffer.readInt();

				Debug.Log("Unit deployed " + storedUnitID);
				Assert.IsNotNull(GameController.storedFromID(storedUnitID));

				GameController.storedFromID(storedUnitID).deploy();

			break;
			case PacketCode.changeGameSpeed:
				float newGameSpeed = buffer.readFloat();

				Debug.Log("Game speed changed");

				GameController.setGameSpeed(newGameSpeed);

			break;
			case PacketCode.changePause:
				bool pauseValue = buffer.readBool();

				Debug.Log("Paused changed");

				GameController.setPause(pauseValue);

			break;
			case PacketCode.destroyUnit:
				unitID = buffer.readInt();

				if (GameController.fromID(unitID) != null) { //the unit may already be destroyed
					Debug.Log("Unit destroyed");
					GameController.destroyUnit(GameController.fromID(unitID));
				}


			break;
			case PacketCode.logMessage:
				string message = buffer.readString();

				Debug.Log("Log message : " + message);

			break;
			case PacketCode.setHealth:
				int moduleDataID = buffer.readInt();
				float newHealth = buffer.readFloat();

				Debug.Log("New module data health received :" + moduleDataID + " , " + newHealth);
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID));

				GameController.moduleDataFromID(moduleDataID).setHealth(newHealth);

			break;
			case PacketCode.setFiring:
				moduleDataID = buffer.readInt();
				bool isFiring = buffer.readBool();

				Debug.Log("New module data firing received : " + moduleDataID + " , " + isFiring);
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID));

				GameController.moduleDataFromID(moduleDataID).setFiring(isFiring);

			break;
			case PacketCode.setDisabled:
				moduleDataID = buffer.readInt();
				bool isDisabled = buffer.readBool();

				Debug.Log("New module data disabled received : " + moduleDataID + " , " + isDisabled);
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID));

				GameController.moduleDataFromID(moduleDataID).setDisabled(isDisabled);

			break;
			case PacketCode.setNullTarget:
				unitID = buffer.readInt();

				Debug.Log("Null target received");
				Assert.IsNotNull(GameController.fromID(unitID));

				GameController.fromID(unitID).setTarget(null);

			break;
			case PacketCode.setTargetSpeed:
				unitID = buffer.readInt();
				float newTargetSpeed = buffer.readFloat();
				int buttonIndex = buffer.readInt();

				Debug.Log("Null target speed");
				Assert.IsNotNull(GameController.fromID(unitID));

				GameController.fromID(unitID).setTargetSpeed(newTargetSpeed);
				GameController.fromID(unitID).setSpeedButton(buttonIndex);

			break;
			case PacketCode.setStore:
				storedUnitID = buffer.readInt();
				moduleDataID = buffer.readInt();

				Debug.Log("Stored module changed");
				Assert.IsNotNull(GameController.storedFromID(storedUnitID));
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID));

				GameController.storedFromID(storedUnitID).changeStore((ContainerModuleData)GameController.moduleDataFromID(moduleDataID));

			break;
			case PacketCode.storeUnit:
				unitID = buffer.readInt();

				Debug.Log("Unit stored");
				Assert.IsNotNull(GameController.fromID(unitID));

				GameController.destroyUnit(GameController.fromID(unitID));
				new StoredUnit(GameController.fromID(unitID));

			break;
			case PacketCode.createDeployed:
				unitID = buffer.readInt();
				int classID = buffer.readInt();

				Debug.Log("Deployed unit created");

				GameController.addDeployedUnit(new DeployedUnit(unitID, classID));

			break;
			case PacketCode.createStored:
				int size = buffer.readInt();
				Debug.Log("Stored units created : size = " + size);
				for (int i = 0; i < size; i++) {
					unitID = buffer.readInt();
					classID = buffer.readInt();
					moduleDataID = buffer.readInt();

					//Debug.Log("Stored unit created : " + unitID);

					StoredUnit unit = new StoredUnit(unitID, classID); //automatically adds the stored unit
					unit.changeStore((ContainerModuleData)GameController.moduleDataFromID(moduleDataID));
				}
			break;
			case PacketCode.createModuleData:
				size = buffer.readInt();
				Debug.Log("Module datas created : size = " + size);
				for (int i = 0; i < size; i++) {
					moduleDataID = buffer.readInt();
					unitID = buffer.readInt();
					angle = buffer.readFloat();
					Vector modulePosition = buffer.readVector();
					int moduleID = buffer.readInt();

					//Debug.Log("Module data created : " + moduleDataID);
					//Assert.IsNotNull(GameController.unitFromID(unitID)); //unit can be null
					Assert.IsNotNull(GameController.moduleFromID(moduleID));

					ModuleData data = GameController.moduleFromID(moduleID).getData(unitID, angle, modulePosition); //automatically adds the module data
					data.setID(moduleDataID);
				}

			break;
			case PacketCode.fireMissile:
				unitID = buffer.readInt();
				storedUnitID  = buffer.readInt();
				angle = buffer.readFloat();

				Assert.IsNotNull(GameController.fromID(unitID));
				Assert.IsNotNull(GameController.storedFromID(storedUnitID), storedUnitID + "");
				Debug.Log("Missile fired " + storedUnitID);

				GameController.storedFromID(storedUnitID).deploy();
				GameController.fromID(storedUnitID).setAngle(angle);

			break;
			case PacketCode.setEMCON:
				unitID = buffer.readInt();
				bool val = buffer.readBool();

				Assert.IsNotNull(GameController.fromID(unitID));
				Debug.Log("EMCON set");

				GameController.fromID(unitID).setEMCON(val);
			break;
			case PacketCode.setAntimissileSalvo:
				unitID = buffer.readInt();
				classID = buffer.readInt();
				int num = buffer.readInt();

				Assert.IsNotNull(GameController.fromID(unitID));
				Assert.IsNotNull(GameController.classFromID(classID));
				Debug.Log("Antimissile salvo set");

				GameController.fromID(unitID).setAntimissileSalvo(GameController.classFromID(classID), num);
			break;
			case PacketCode.setStoredLoadingState:
				unitID = buffer.readInt();
				int state = buffer.readInt();

				Assert.IsNotNull(GameController.storedFromID(unitID), unitID + "");
				Debug.Log("Loading state changed " + unitID + " " + state);

				GameController.storedFromID(unitID).setLoadingState((LoadingState)state);
			break;
			case PacketCode.addToMissileControl:
				moduleDataID = buffer.readInt();
				unitID = buffer.readInt();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID), moduleDataID + "");
				Debug.Log("Missile added to control " + moduleDataID + " " + unitID);

				((MissileControlModuleData)GameController.moduleDataFromID(moduleDataID)).addControlledMissile(GameController.unitFromID(unitID));
			break;
			case PacketCode.removeFromMissileControl:
				moduleDataID = buffer.readInt();
				unitID = buffer.readInt();

				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID), moduleDataID + "");
				Debug.Log("Missile removed from control " + moduleDataID + " " + unitID);

				((MissileControlModuleData)GameController.moduleDataFromID(moduleDataID)).removeControlledMissile(unitID);
			break;
			case PacketCode.addToIlluminated:
				moduleDataID = buffer.readInt();
				unitID = buffer.readInt();

				Assert.IsNotNull(GameController.fromID(unitID), unitID + "");
				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID), moduleDataID + "");
				Debug.Log("Missile added to illuminated " + moduleDataID + " " + unitID);

				((IlluminatorModuleData)GameController.moduleDataFromID(moduleDataID)).addTargetedMissile(GameController.fromID(unitID));
			break;
			case PacketCode.removeFromIlluminated:
				moduleDataID = buffer.readInt();
				unitID = buffer.readInt();

				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID), moduleDataID + "");
				Debug.Log("Missile removed from illuminated " + moduleDataID + " " + unitID);

				((IlluminatorModuleData)GameController.moduleDataFromID(moduleDataID)).removeTargetMissile(unitID);
			break;
			case PacketCode.addToTransferts:
				unitID = buffer.readInt();
				int destinationID = buffer.readInt();
				storedUnitID = buffer.readInt();

				Assert.IsNotNull(GameController.fromID(unitID), unitID + "");
				Assert.IsNotNull(GameController.moduleDataFromID(destinationID), destinationID + "");
				Assert.IsNotNull(GameController.storedFromID(storedUnitID), storedUnitID + "");
				Debug.Log("Stored unit added to transferts " + unitID + " " + destinationID + " " + storedUnitID);

				GameController.fromID(unitID).addTransfert(new Transfert(GameController.storedFromID(storedUnitID), (ContainerModuleData)GameController.moduleDataFromID(destinationID)));
			break;
			case PacketCode.removeFromTransferts:
				unitID = buffer.readInt();
				storedUnitID = buffer.readInt();

				Assert.IsNotNull(GameController.fromID(unitID), unitID + "");
				Assert.IsNotNull(GameController.unitFromID(storedUnitID), storedUnitID + "");
				Debug.Log("Stored unit removed from transferts " + unitID  + " " + storedUnitID);

				GameController.fromID(unitID).removeTransfert(storedUnitID);
			break;
			case PacketCode.setCIWSAmmo:
				moduleDataID = buffer.readInt();
				int ammo = buffer.readInt();

				Assert.IsNotNull(GameController.moduleDataFromID(moduleDataID), moduleDataID + "");
				Debug.Log("New CIWS ammo received " + moduleDataID);

				((CIWSModuleData)GameController.moduleDataFromID(moduleDataID)).setAmmo(ammo);
			break;
			case PacketCode.setLoadout:
				unitID = buffer.readInt();
				int loadoutIndex = buffer.readInt();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Debug.Log("New loadout received " + loadoutIndex);

				Unit unitNewLoadout = GameController.unitFromID(unitID);
				unitNewLoadout.setCurrentLoadout(unitNewLoadout.getClass().getLoadout(loadoutIndex));
			break;
			case PacketCode.addBird:
				unitID = buffer.readInt();
				int birdID = buffer.readInt();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Debug.Log("Add bird received " + birdID);

				GameController.fromID(unitID).addBird(GameController.fromID(birdID));
			break;
			case PacketCode.removeBird:
				unitID = buffer.readInt();
				birdID = buffer.readInt();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Debug.Log("Remove bird received " + birdID);

				GameController.fromID(unitID).removeBird(birdID);
			break;
			case PacketCode.setMotherShip:
				unitID = buffer.readInt();
				int motherShipID = buffer.readInt();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Debug.Log("Set mothership received " + motherShipID);

				GameController.fromID(unitID).setMotherShip(GameController.fromID(motherShipID));
			break;
			case PacketCode.setPositionTarget:
				unitID = buffer.readInt();
				Vertex positionTarget = buffer.readVertex();

				Assert.IsNotNull(GameController.unitFromID(unitID), unitID + "");
				Debug.Log("Set Position target received " + unitID);

				GameController.fromID(unitID).setPositionTarget(positionTarget);
			break;




				//REQUESTS
			case PacketCode.requestMovement:
				unitID = buffer.readInt();
				Vertex destination = buffer.readVertex();
				bool shift = buffer.readBool();

				Debug.Log("Movement requested");

				Requests.moveUnit(GameController.fromID(unitID), destination, shift);

			break;
			case PacketCode.requestChangeTarget:
				unitID = buffer.readInt();
				targetID = buffer.readInt();

				Debug.Log("Target change requested");

				Requests.changeTarget(GameController.fromID(unitID), GameController.fromID(targetID));

			break;
			case PacketCode.requestOrder:
				unitID = buffer.readInt();
				string order = buffer.readString();

				Debug.Log("Order requested");

				Requests.unitOrder(GameController.fromID(unitID), order);

			break;
			case PacketCode.requestChangeGameSpeed:
				newGameSpeed = buffer.readFloat();

				Debug.Log("Game speed change requested");

				Requests.changeGameSpeed(newGameSpeed);

			break;
			case PacketCode.requestChangePause:
				pauseValue = buffer.readBool();

				Debug.Log("Pause change requested");

				Requests.changePause(pauseValue);
			break;
			case PacketCode.requestSetDisabled:
				moduleDataID = buffer.readInt();
				isDisabled = buffer.readBool();

 				Debug.Log("New module data disabled request received");

				Requests.disableModule(GameController.moduleDataFromID(moduleDataID), isDisabled);
			break;
			case PacketCode.requestSetNullTarget:
				unitID = buffer.readInt();

				Debug.Log("Null target request received");

				Requests.setNullTarget(GameController.fromID(unitID));
			break;
			case PacketCode.requestStop:
				unitID = buffer.readInt();

				Debug.Log("Stop request received");
				Assert.IsNotNull(GameController.fromID(unitID));

				Requests.removePath(GameController.fromID(unitID));
			break;
			case PacketCode.requestSetTargetSpeed:
				unitID = buffer.readInt();
				float targetSpeed = buffer.readFloat();
				buttonIndex = buffer.readInt();

				Debug.Log("New target speed request received");
				Assert.IsNotNull(GameController.fromID(unitID));

				Requests.setTargetSpeed(GameController.fromID(unitID), targetSpeed, buttonIndex);
			break;
			case PacketCode.requestSetEMCON:
				unitID = buffer.readInt();
				val = buffer.readBool();

				Assert.IsNotNull(GameController.fromID(unitID));
				Debug.Log("EMCON request received");

				Requests.setEMCON(GameController.fromID(unitID), val);
			break;
			case PacketCode.requestSetAntimissileSalvo:
				unitID = buffer.readInt();
				classID = buffer.readInt();
				num = buffer.readInt();

				Assert.IsNotNull(GameController.fromID(unitID));
				Assert.IsNotNull(GameController.classFromID(classID));
				Debug.Log("Antimissile salvo request received");

				Requests.setAntimissileSalvo(GameController.fromID(unitID), GameController.classFromID(classID), num);
			break;
			case PacketCode.requestSetPositionTarget:
				unitID = buffer.readInt();
				positionTarget = buffer.readVertex();

				Assert.IsNotNull(GameController.fromID(unitID));
				Debug.Log("Position target request received");

				Requests.setPositionTarget(GameController.fromID(unitID), positionTarget);
			break;
		}
	}

	public static void manageNetworkOutputs() {
		tryToSend();
	}

	public static void connectToServer(Tuple<string, int> info) {
		connectToServer(info._1, info._2);
	}

	public static void connectToServer(string ip, int port) {
		if (connectingToServer) {
			return;
		}

		byte error;

		Buffer buffer = new Buffer(PacketCode.tellServer);
		buffer.writeString(ip);
		buffer.writeInt(port);
		NetworkTransport.Send(hostID, masterServerID, selfChannelID, buffer.getArray(), buffer.getLength(), out error);


		serverID = NetworkTransport.Connect(hostID, ip, port, 0, out error);
		//serverID = NetworkTransport.ConnectWithSimulator(hostID, ip, port, 0, out error, new ConnectionSimulatorConfig(100, 200, 100, 200, 0.01f));

		connectingToServer = true;
	}

	public static bool isServer() {
		return isAServer;
	}

	public static bool isConnectedToMasterServer() {
		return connectedToMasterServer;
	}

	public static bool isConnectedToServer() {
		return connectedToServer;
	}

	public static void toServer(Buffer buffer) {

		byte error;
		NetworkTransport.Send(hostID, serverID, selfChannelID, buffer.getArray(), buffer.getLength(),  out error);

		if (error != 0) {
			Debug.LogError("Network error : " + error);
		}
	}

	public static void toClients(Buffer buffer) {
		if (clientIDs == null) { //Dev
			clientIDs = new List<int>();
		}

		foreach(int clientID in clientIDs) {
			toSend.Add(new Tuple<int, Buffer>(clientID, buffer));
		}
		tryToSend();
	}

	public static void toClient(int clientID, Buffer buffer) {
		toSend.Add(new Tuple<int, Buffer>(clientID, buffer));
		tryToSend();
	}

	private static void tryToSend() {
		List<Tuple<int, Buffer>> toBeRemoved = new List<Tuple<int, Buffer>>();

		if (toSend == null) { //Dev
			toSend = new List<Tuple<int, Buffer>>();
		}

		foreach(Tuple<int, Buffer> tuple in toSend) {
			byte error;
			NetworkTransport.Send(hostID, tuple._1, selfChannelID, tuple._2.getArray(), tuple._2.getLength(),  out error);

			if (error != 0) {
				break; //otherwise, will not send in order
			}
			else {
				toBeRemoved.Add(tuple);
			}
		}

		foreach(Tuple<int, Buffer> tuple in toBeRemoved) {
			toSend.Remove(tuple);
		}
	}

	public static void addToLog(int unitID, string message) {
		//if unitID belongs to this player, just add to the log
		//else, send it to the right client

		addToLog(message); //temporary
	}

	public static void addToLog(string message) {
		Buffer buffer = new Buffer(PacketCode.logMessage);
		buffer.writeString(message);

		toClients(buffer);

		Debug.Log("Log message : " + message);
	}

	public static int getPlayerJoining() {
		return playerJoining;
	}
}
