package CK.IA;

import java.util.HashSet;

public class SpawnAgent extends Entity {
	private long current_spawn_check;
	private long last_spawn_ns;
	private long phase_ns;
	/**
	 * local copy of all soldier agents in the environment
	 */
	private HashSet<SoldierAgent> soldier_agents;
	private long spawn_interval_ns;
	private int team;

	public SpawnAgent(int team, Vector3d position, float radius, long spawn_interval_ns, long phase_ns, EnvironmentAgent environment) {
		super(GoalSimulatorConfig.SPAWN_HEALTH, position, radius, EntityType.SPAWN_AGENT, environment);
		this.paused = true;
		this.last_spawn_ns = getTime() + phase_ns;
		this.spawn_interval_ns = spawn_interval_ns;
		this.team = team;
		this.environment = environment;
		this.phase_ns = phase_ns;
	}

	public void debugStatus(String text) {
		if (GoalSimulatorConfig.DEBUG)
			System.out.println("SpawnAgent:" + this.toString() + " " + text);
	}

	public void executeCycle() {
		debugStatus("executeCycle()");
		super.executeCycle();
		if (!this.isPaused()) {
			// TICK-TACK
			current_spawn_check = getTime();
			long dif = current_spawn_check - last_spawn_ns;
			// ALERT!
			if (dif >= spawn_interval_ns) {
				// wake the dead
				spawnCheck();
				// set last spawn as current
				last_spawn_ns = current_spawn_check;
			}
		}
	}

	public float getInterval() {
		return this.spawn_interval_ns;
	}

	/**
	 * Returns the progress of the spawn timer in the range 0...1, being 0 recently spawn and 1 about to spawn.
	 * 
	 * @return The progress
	 */
	public float getSpawnProgress() {
		debugStatus("getSpawnProgress()");
		if (!this.isPaused() && last_spawn_ns > 0) {
			current_spawn_check = getTime();
			long dif = current_spawn_check - last_spawn_ns;
			float v = ((float) (dif)) / ((float) (spawn_interval_ns));
			if (v < 0) {
				v = 0;
			}
			return v;
		} else
			return 0.0f;
	}

	public int getTeam() {
		return this.team;
	}

	public long getTime() {
		// time must be got LOCALLY
		// do not request from environment
		// this seems to take 1,676 us (on minimum)
		return System.nanoTime();
	}

	/**
	 * request the environment the positioning of the soldier agents
	 */
	public void informSoldierPositioning(int id) {
		debugStatus("informSoldierRevival() [" + id + "] informSoldierPositioning()");
		// send a message specifying new position the message receiver
		Message m = new Message(Performative.INFORM, Subject.ID_ENTITY, Property.POSITION_VECTOR3D, id, id, this.getId());
		// create a new spawn position where this spawn is
		Vector3d position = new Vector3d(this.getPosition());
		// generate random spawn position in the plane XZ
		position.addX(this.getRadius() * Math.random() * 2 - 1);
		position.addZ(this.getRadius() * Math.random() * 2 - 1);
		// add offset to position it nicely on the floor
		position.add(GoalSimulatorConfig.SPAWN_OFFSET);
		position.add(GoalSimulatorConfig.SOLDIER_DEAD_OFFSET);
		m.setContent(position);
		sendMessage(m);
		// set a random yaw (angle)
		// actual angle will be in the range ]-PI PI[
		float random = (float) Math.random();
		float yaw = GoalSimulatorConfig.PI_FLOAT * (random * 2.0f - 1);
		m.setProperty(Property.YAW_FLOAT);
		m.setContent(yaw);
		sendMessage(m);
	}

	/**
	 * request the environment the revival of the soldier agents
	 */
	public void informSoldierRevival(int id) {
		debugStatus("informSoldierRevival() [" + id + "] informSoldierPositioning()");
		// send a message specifying new health for the message receiver
		Message m = new Message(Performative.INFORM, Subject.ID_ENTITY, Property.HEALTH_FLOAT, id, id, this.getId());
		m.setContent(GoalSimulatorConfig.SOLDIER_HEALTH_NORMAL);
		sendMessage(m);
	}

	@SuppressWarnings("unchecked")
	/*
	 * processes the message received from the environment
	 */
	public boolean receiveMessage(Message original) {
		// debugStatus("receiveMessage() " + original.toString());
		// as we are an entity, first let the base method respond
		boolean message_processed = super.receiveMessage(original);
		// the message must be for us
		if (original.getDestination_id() == this.getId()) {
			// is it an information (fact)?
			if (original.getPerformative() == Performative.INFORM) {
				// is it about all soldier agents?
				if (original.getSubject() == Subject.ALL_SOLDIER_AGENTS) {
					// is it the hashtable?
					if (original.getProperty() == Property.HASHSET) {
						// update our hashtable of soldier agents
						soldier_agents = (HashSet<SoldierAgent>) original.getContent();
						message_processed = true;
						if (GoalSimulatorConfig.DEBUG)
							System.out.println(this.toString() + " received ALL_SOLDIER_AGENTS.HASHSET");
					}
				}
			}
			// some request
		}
		if (!message_processed) {
			System.out.println("SpawnAgent:SOMETHING's MISSING HERE!!!");
		}
		return message_processed;
	}

	/**
	 * request the environment the hashtable of the soldier agents
	 */
	public void requestUpdateSoldierAgents() {
		debugStatus("requestUpdateAgents()");
		// request the environment the hashtable of all soldier agents
		Message m = new Message(Performative.REQUEST, Subject.ALL_SOLDIER_AGENTS, Property.HASHSET, -1, EnvironmentAgent.ENVIROMENT_ID, this.getId());
		sendMessage(m);
	}

	public void setPaused(boolean paused) {
		// warn parent
		super.setPaused(paused);
		// reset the timer
		if (paused == false) {
			last_spawn_ns = getTime() + phase_ns;
		}
	}

	/**
	 * checks for this spawn's team agents need to spawn
	 */
	public void spawnCheck() {
		debugStatus("spawnCheck()");
		// get all agents
		requestUpdateSoldierAgents();
		// if theres agents to check
		if (soldier_agents != null && soldier_agents.size() > 0) {
			// scan them all
			for (SoldierAgent sa : soldier_agents) {
				// if some agent is from this spawn's team and he wants to respawn
				if (sa.getTeam() == this.getTeam() && sa.waitingToSpawn()) {
					// spawn him near within this spawn's radius
					informSoldierRevival(sa.getId());
					informSoldierPositioning(sa.getId());
				}
			}
		}
	}

	public String toString() {
		return super.toString() + " team:" + this.getTeam() + " interval:" + this.getInterval();
	}

}
