using UnityEngine;
using System.Collections.Generic;public abstract class Requests {

	public static void moveUnit(DeployedUnit unit, Vertex destination, bool shift = false) {
		if (unit.getClass().getClassType() == ClassType.Missile) {
			return;
		}

		if (NetworkManager.isServer()) {
			Path path = PathFinding.findPath(unit, destination, shift);

			if (path == null) {
				NetworkManager.addToLog(unit.getID(), "Unable to find path");
				return;
			}

			if (shift) {
				unit.addPath(path);
			}
			else {
				unit.setPath(path, false);
			}

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(unit.getPath());
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestMovement);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(destination);
			buffer.writeBool(shift);
			NetworkManager.toServer(buffer);
		}
	}

	public static void changeTarget(DeployedUnit unit, DeployedUnit target) {
		if (NetworkManager.isServer()) {
			unit.setTarget(target);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changeTarget);
			buffer.writeInt(unit.getID());
			buffer.writeInt(target.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestChangeTarget);
			buffer.writeInt(unit.getID());
			buffer.writeInt(target.getID());
			NetworkManager.toServer(buffer);
		}
	}

	public static void setNullTarget(DeployedUnit unit) {
		if (NetworkManager.isServer()) {
			unit.setTarget(null);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setNullTarget);
			buffer.writeInt(unit.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetNullTarget);
			buffer.writeInt(unit.getID());
			NetworkManager.toServer(buffer);
		}
	}

	public static void unitOrder(DeployedUnit unit, string order) {
		if (unit.getClass().getClassType() == ClassType.Missile) {
			return;
		}

		if (NetworkManager.isServer()) {
			string message = Decoder.decode(unit, order);

			NetworkManager.addToLog(unit.getID(), message);
		}
		else {
			Buffer buffer = new Buffer(PacketCode.requestOrder);
			buffer.writeInt(unit.getID());
			buffer.writeString(order);
			NetworkManager.toServer(buffer);
		}
	}

	public static void deployUnit(StoredUnit unit) {
		if (NetworkManager.isServer()) {
			unit.deploy();

			Buffer buffer = new Buffer(PacketCode.deployUnit);
			buffer.writeInt(unit.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot request unit deployment");
		}
	}

	public static void missileMovement(DeployedUnit unit, DeployedUnit target, Vertex targetPosition, LaunchingTechnique launchingTechnique) {
		if (NetworkManager.isServer()) {
			Path path = PathFinding.findPath(unit, targetPosition);

			if (path == null) {
				NetworkManager.addToLog(unit.getID(), "Unable to find path");
				return;
			}

			List<Waypoint> startWaypoints = new List<Waypoint>();
			Vertex position = unit.getPosition();
			Vector direction = (targetPosition - position).normalized();
			switch(launchingTechnique) {
				default:
					//nothing
				break;
				case LaunchingTechnique.VLS:
					startWaypoints.Add(new StaticWaypoint(position + new Vector(0, 0, 100)));
					startWaypoints.Add(new StaticWaypoint(position + new Vector(0, 0, 10) + direction * 1000));
				break;
			}
			path.addStartWaypoints(startWaypoints);

			if (target != null) {
				switch(unit.getMissileTechnique()) {
					default:
						path.addEndWaypoint(new TargetWaypoint(unit, target, 1000, unit.getCruiseAltitude()));
						path.addEndWaypoint(new UnitWaypoint(target));
					break;
					case MissileTechnique.Direct:
						path.addEndWaypoint(new UnitWaypoint(target));
					break;
				}
			}

			unit.setPath(path, false);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(path);
				
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot request move toward targets");
		}
	}

	public static void landOn(DeployedUnit unit, DeployedUnit target) {
		if (NetworkManager.isServer()) {
			Path path = PathFinding.findPath(unit, target.getPosition());

			foreach(Tuple<float, float> tuple in unit.getLandingPattern()) {
				path.addEndWaypoint(new LandingWaypoint(target, tuple._1, tuple._2));
			}

			path.addEndWaypoint(new UnitWaypoint(target));

			unit.setPath(path, false);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(path);

			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot request landing");
		}
	}

	public static void updatePath(DeployedUnit unit) {
		if (NetworkManager.isServer()) {
			Path oldPath = unit.getPath();
			Path newPath;

			if (oldPath != null) {
				List<Waypoint> oldEndWaypoints = oldPath.getEndWaypoints();

				Vertex destination;
				if (oldEndWaypoints != null) {
					foreach (Waypoint endWaypoint in oldEndWaypoints) {
						endWaypoint.resetInfo();
					}
					destination = oldEndWaypoints[0].getVertex();
				}
				else {
					destination = oldPath.toList()[oldPath.size()-1].getVertex();
				}

				newPath = PathFinding.findPath(unit, destination);

				if (oldEndWaypoints != null) {
					newPath.addEndWaypoints(oldEndWaypoints);
				}


				unit.setPath(newPath, true);
			}
			else {
				newPath = null;
			}

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(newPath);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot request path updates");
		}
	}

	public static void changeGameSpeed(float newGameSpeed) {
		if (NetworkManager.isServer()) {
			GameController.setGameSpeed(newGameSpeed);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changeGameSpeed);
			buffer.writeFloat(newGameSpeed);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestChangeGameSpeed);
			buffer.writeFloat(newGameSpeed);
			NetworkManager.toServer(buffer);
		}
	}

	public static void changePause(bool value) {
		if (NetworkManager.isServer()) {
			GameController.setPause(value);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePause);
			buffer.writeBool(value);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestChangePause);
			buffer.writeBool(value);
			NetworkManager.toServer(buffer);
		}
	}

	public static void destroyUnit(DeployedUnit unit) {
		if (NetworkManager.isServer()) {
			GameController.destroyUnit(unit);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.destroyUnit);
			buffer.writeInt(unit.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot request destruction of units");
		}
	}

	public static void setHealth(ModuleData data, float newHealth) {
		if (NetworkManager.isServer()) {
			data.setHealth(newHealth);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setHealth);
			buffer.writeInt(data.getID());
			buffer.writeFloat(newHealth);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set health");
		}
	}

	public static void setIsFiring(ModuleData data, bool isFiring) {
		if (NetworkManager.isServer()) {
			//data.setFiring(isFiring); //useless as the server does not use the 'firing' boolean

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setFiring);
			buffer.writeInt(data.getID());
			buffer.writeBool(isFiring);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set firing status");
		}
	}

	public static void disableModule(ModuleData data, bool isDisabled) {
		if (NetworkManager.isServer()) {
			data.setDisabled(isDisabled);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setDisabled);
			buffer.writeInt(data.getID());
			buffer.writeBool(isDisabled);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetDisabled);
			buffer.writeInt(data.getID());
			buffer.writeBool(isDisabled);
			NetworkManager.toServer(buffer);
		}
	}

	public static void setTargetSpeed(DeployedUnit unit, float newTargetSpeed, int buttonIndex = 0) {
		if (NetworkManager.isServer()) {
			unit.setTargetSpeed(newTargetSpeed);
			unit.setSpeedButton(buttonIndex);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setTargetSpeed);
			buffer.writeInt(unit.getID());
			buffer.writeFloat(newTargetSpeed);
			buffer.writeInt(buttonIndex);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetTargetSpeed);
			buffer.writeInt(unit.getID());
			buffer.writeFloat(newTargetSpeed);
			buffer.writeInt(buttonIndex);
			NetworkManager.toServer(buffer);
		}
	}

	public static void setAltitude(DeployedUnit unit, float newAltitude) {
		if (NetworkManager.isServer()) {
			Path newPath = new Path(new Vertex(unit.getPosition().x(), unit.getPosition().y(), newAltitude));

			unit.setCruiseAltitude(newAltitude); //no need to send this update to the clients
			unit.setPath(newPath, false);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(newPath);

			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set altitude");
		}
	}

	public static void setStore(StoredUnit stored, ContainerModuleData newStore) {
		if (NetworkManager.isServer()) {

			stored.changeStore(newStore);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setStore);
			buffer.writeInt(stored.getID());
			buffer.writeInt(newStore.getID());

			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot update store");
		}
	}

	public static void storeUnit(DeployedUnit unit) {
		if (NetworkManager.isServer()) {

			GameController.destroyUnit(unit);
			new StoredUnit(unit);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.storeUnit);
			buffer.writeInt(unit.getID());

			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot store unit");
		}
	}

	public static void removePath(DeployedUnit unit) {
		if (NetworkManager.isServer()) {
			unit.removePath();

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.changePathAndPos);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(unit.getPosition());
			buffer.writeFloat(unit.getAngle());
			buffer.writePath(null);
			NetworkManager.toClients(buffer);
		}
		else {

			Buffer buffer = new Buffer(PacketCode.requestStop);
			buffer.writeInt(unit.getID());
			NetworkManager.toServer(buffer);
		}
	}

	public static void fireMissile(DeployedUnit unit, StoredUnit missile, float angle) {
		if (NetworkManager.isServer()) {
			missile.deploy();
			GameController.fromID(missile.getID()).setAngle(angle);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.fireMissile);
			buffer.writeInt(unit.getID());
			buffer.writeInt(missile.getID());
			buffer.writeFloat(angle);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot fire missiles");
		}
	}

	public static void setEMCON(DeployedUnit unit, bool newValue) {
		if (NetworkManager.isServer()) {
			unit.setEMCON(newValue);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setEMCON);
			buffer.writeInt(unit.getID());
			buffer.writeBool(newValue);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetEMCON);
			buffer.writeInt(unit.getID());
			buffer.writeBool(newValue);
			NetworkManager.toServer(buffer);
		}
	}

	public static void setAntimissileSalvo(DeployedUnit unit, Class antimissile, int num) {
		if (NetworkManager.isServer()) {
			unit.setAntimissileSalvo(antimissile, num);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setAntimissileSalvo);
			buffer.writeInt(unit.getID());
			buffer.writeInt(antimissile.getID());
			buffer.writeInt(num);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetAntimissileSalvo);
			buffer.writeInt(unit.getID());
			buffer.writeInt(antimissile.getID());
			buffer.writeInt(num);
			NetworkManager.toServer(buffer);
		}
	}

	public static void setLoadingState(StoredUnit stored, LoadingState newState) {
		if (NetworkManager.isServer()) {
			stored.setLoadingState(newState);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setStoredLoadingState);
			buffer.writeInt(stored.getID());
			buffer.writeInt((int)newState);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot change loading state");
		}
	}

	public static void addToMissileControl(MissileControlModuleData data, Unit controlled) {
		if (NetworkManager.isServer()) {
			data.addControlledMissile(controlled);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.addToMissileControl);
			buffer.writeInt(data.getID());
			buffer.writeInt(controlled.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot add to missile control");
		}
	}

	public static void removeFromMissileControl(MissileControlModuleData data, DeployedUnit controlled) {
		if (NetworkManager.isServer()) {
			data.removeControlledMissile(controlled.getID());

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.removeFromMissileControl);
			buffer.writeInt(data.getID());
			buffer.writeInt(controlled.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot remove from missile control");
		}
	}

	public static void addToIlluminated(IlluminatorModuleData data, DeployedUnit controlled) {
		if (NetworkManager.isServer()) {
			data.addTargetedMissile(controlled);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.addToIlluminated);
			buffer.writeInt(data.getID());
			buffer.writeInt(controlled.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot add to illuminated");
		}
	}

	public static void removeFromIlluminated(IlluminatorModuleData data, DeployedUnit controlled) {
		if (NetworkManager.isServer()) {
			data.removeTargetMissile(controlled.getID());

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.removeFromIlluminated);
			buffer.writeInt(data.getID());
			buffer.writeInt(controlled.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot remove from illuminated");
		}
	}

	public static void addToTransferts(DeployedUnit unit, Transfert transfert) {
		if (NetworkManager.isServer()) {
			unit.addTransfert(transfert);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.addToTransferts);
			buffer.writeInt(unit.getID());
			buffer.writeInt(transfert.destination.getID());
			buffer.writeInt(transfert.toBeMoved.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot add to transferts");
		}
	}

	public static void removeFromTransferts(DeployedUnit unit, Transfert transfert) {
		if (NetworkManager.isServer()) {
			unit.removeTransfert(transfert.toBeMoved.getID());

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.removeFromTransferts);
			buffer.writeInt(unit.getID());
			buffer.writeInt(transfert.toBeMoved.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot remove from transferts");
		}
	}

	public static void setCIWSAmmo(CIWSModuleData data, int newAmmo) {
		if (NetworkManager.isServer()) {
			data.setAmmo(newAmmo);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setCIWSAmmo);
			buffer.writeInt(data.getID());
			buffer.writeInt(newAmmo);
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set CIWS ammo");
		}
	}

	public static void setLoadout(Unit unit, Loadout loadout) {
		if (NetworkManager.isServer()) {
			unit.setCurrentLoadout(loadout);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setLoadout);
			buffer.writeInt(unit.getID());
			buffer.writeInt(unit.getClass().getIndexFromLoadout(loadout));
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set loadout");
		}
	}

	public static void addBird(DeployedUnit parent, DeployedUnit bird) {
		if (NetworkManager.isServer()) {
			parent.addBird(bird);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.addBird);
			buffer.writeInt(parent.getID());
			buffer.writeInt(bird.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot add birds");
		}
	}

	public static void removeBird(DeployedUnit parent, DeployedUnit bird) {
		if (NetworkManager.isServer()) {
			parent.removeBird(bird.getID());

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.removeBird);
			buffer.writeInt(parent.getID());
			buffer.writeInt(bird.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot remove birds");
		}
	}

	public static void setMotherShip(DeployedUnit unit, DeployedUnit motherShip) {
		if (NetworkManager.isServer()) {
			unit.setMotherShip(motherShip);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setMotherShip);
			buffer.writeInt(unit.getID());
			buffer.writeInt(motherShip.getID());
			NetworkManager.toClients(buffer);
		}
		else {
			Debug.LogError("Clients cannot set mothership");
		}
	}

	public static void setPositionTarget(DeployedUnit unit, Vertex positionTarget) {
		if (NetworkManager.isServer()) {
			unit.setPositionTarget(positionTarget);

			//Send update to clients
			Buffer buffer = new Buffer(PacketCode.setPositionTarget);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(positionTarget);
			NetworkManager.toClients(buffer);
		}
		else {
			//Send request to server
			Buffer buffer = new Buffer(PacketCode.requestSetPositionTarget);
			buffer.writeInt(unit.getID());
			buffer.writeVertex(positionTarget);
			NetworkManager.toServer(buffer);
		}
	}
}
