package de.tu_darmstadt.tk.SmartHomeNetworkSim.core.protocols;

import java.util.Collection;
import java.util.LinkedList;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Packet;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Port;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Protocol;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.protocols.packets.Ping_packet;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.util.Pair;

/**
 * Ping Protocol which sends simple Ping requests to the target
 * 
 * @author Andreas T. Meyer-Berg
 */
public class Ping_protocol implements Protocol {

	/**
	 * Sender / Source of the Ping Request
	 */
	private Port sender;
	/**
	 * Target / Destination of the Ping Request
	 */
	private Port target;
	/**
	 * Creates a new Ping Protocol
	 */
	public Ping_protocol(){
		sender = null;
		target = null;
	}
	/**
	 * Ping sequence number
	 */
	private short sequence = 0;
	@Override
	public Collection<Packet> generateNextPackets(Port port, long timestep, boolean packetLost) {
		/**
		 * Packets which are generated during this timestep
		 */
		LinkedList<Packet> packets = new LinkedList<>();
		if(port == null || port.getStatus() != Port.SENDING)
			return packets;
		/**
		 * Update trigger time
		 */
		port.setLastTrigger(timestep);
		
		if(port == sender && target != null){
			packets.add(new Ping_packet(timestep, sender, target, Ping_packet.EchoRequest, sequence));
			long delay = sender.getTransmissionDelayTo(target); 
			if(target.getStatus() >= Port.OPEN && !packetLost && delay != Long.MAX_VALUE){
				packets.add(new Ping_packet(timestep+target.getResponseTime()+delay+(short)(target.getJitter()*Math.random()), target, sender, Ping_packet.EchoReply, sequence++));
			}
		}
		else if(port == target){
			/**
			 * Target should not initialize ping
			 */
		}
		return packets;
	}

	@Override
	public String[] getRoles() {
		return new String[]{"Sender", "Target"};
	}

	@Override
	public Collection<Port> getDevicesWithRole(int role) {
		/**
		 * Devices of the given Role
		 */
		LinkedList<Port> devicesOfRole = new LinkedList<Port>();
		switch (role) {
		case 0:
			if(sender != null)
				devicesOfRole.add(sender);
			break;
		case 1:
			if(target != null)
				devicesOfRole.add(target);
			break;
		default:
			return null; 
		}
		return devicesOfRole;
	}

	@Override
	public boolean addDeviceOfRole(Port device, int role) {
		if(role == 0 &&sender == null)
			sender = device;
		else if(role == 1 && target == null)
			target = device;
		else
			return false;
		return true;
	}

	@Override
	public void removeDevice(Port device) {
		if(sender == device)
			sender = null;
		if(target == device)
			target = null;
	}

	@Override
	public String getName() {
		return "Ping";
	}

	@Override
	public Collection<Pair<Port, Port>> getDeletedTopology() {
		return new LinkedList<Pair<Port, Port>>();
	}

}
