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

import java.util.Arrays;
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.util.Pair;

/**
 * Simple Implementation of a protocol, which sends 
 *
 * @author Andreas T. Meyer-Berg
 */
public class SimpleProtocol implements Protocol {

	/**
	 * Source of the protocol, and destination which responds
	 */
	Port source, destination;
	
	/**
	 * Deleted Connection if participants of the connection were removed 
	 */
	private Pair<Port,Port> deletedConnection = null;
	
	/**
	 * Package which is sent on termination 
	 */
	private Packet terminationPacket = null;
	
	/**
	 * True if the next Package is send by source, false if the next one will be send by destination
	 */
	boolean srcSends;
	
	public SimpleProtocol(){
		source = null;
		destination = null;
		srcSends = true;
	}
	public SimpleProtocol(Port src, Port dest){
		source = src;
		destination = dest;
		srcSends = true;
	}
	@Override
	public Collection<Packet> generateNextPackets(Port p, long timestep, boolean packetLost) {
		LinkedList<Packet> ret = new LinkedList<Packet>();
		if(terminationPacket!=null){
			terminationPacket.setTimestamp(timestep);
			ret.add(terminationPacket);
			terminationPacket = null;
		}
		if(p==null) return ret;
		p.setLastTrigger(timestep);
		if(packetLost)ret.add(new SimplePacket(timestep, p, p == source ? destination : source, "Lost"));
		else if(p == destination)
			ret.add(new SimplePacket(timestep, destination, source));
		else
			ret.add(new SimplePacket(timestep, source, destination));
		return ret;
	}
	
	@Override
	public int getNumberOfRoles() {
		// two different roles
		return 2;
	}
	
	@Override
	public String[] getRoles() {
		return new String[]{"Sender", "Receiver"};
	}
	
	@Override
	public Collection<Port> getDevicesWithRole(int role) {
		Port ret = null;
		switch (role) {
		case 0:
			ret = source;
			break;
		case 1:
			ret = destination;
			break;
		default:
			return null;
		}
		if(ret!=null) 
			return Arrays.asList(ret);
		else
			return Arrays.asList();
	}
	
	@Override
	public boolean addDeviceOfRole(Port device, int role) {
		if(role == 0 && source == null){
			source = device;
		}else if(role == 1 && destination == null){
			destination = device;
		}else{
			return false;
		}
		return true;
	}
	
	@Override
	public void removeDevice(Port device) {
		if(device == source){
			if(destination!=null){
				deletedConnection = new Pair<Port, Port>(new Port(source.getOwner(),(short)-1),destination);
				terminationPacket = new SimplePacket(0, source, destination, "Disconnect");
			}else if(deletedConnection!=null){
				deletedConnection.setLeft(new Port(source.getOwner(),(short)-1));
			}
			source = null;
		}
		
		if(device == destination){
			if(source!=null){
				deletedConnection = new Pair<Port, Port>(source, new Port(destination.getOwner(),(short)-1));
				terminationPacket = new SimplePacket(0, destination, source, "Disconnect");
			}else if(deletedConnection!=null){
				deletedConnection.setRight(new Port(destination.getOwner(),(short)-1));
			}
			destination = null;
		}
	}
	
	@Override
	public String getName() {
		return "SimpleProtocol";
	}

	@Override
	public int getRoleOfDevice(Port device){
		if(device == null) return -1;
		else if(device == source) return 0;
		else if(device == destination) return 1;
		else return -1;
	}
	
	@Override
	public Collection<Port> getDevices(){
		LinkedList<Port> returnDevices = new LinkedList<Port>();
		if(source!=null)
			returnDevices.add(source);
		if(destination!=null)
			returnDevices.add(destination);
		return returnDevices;
	}
	
	@Override
	public byte getTopologyType() {
		return FULLY_CONNECTED;
	}
	
	@Override
	public Collection<Pair<Port,Port>> getTopology(){
		LinkedList<Pair<Port,Port>> topology = new LinkedList<Pair<Port,Port>>();
		if(source!=null && destination!=null)
			topology.add(new Pair<Port, Port>(source, destination));
		return topology;
	}
	@Override
	public Collection<Pair<Port, Port>> getDeletedTopology() {
		LinkedList<Pair<Port, Port>> deleted = new LinkedList<Pair<Port,Port>>();
		if(deletedConnection!=null)deleted.add(deletedConnection);
		return deleted;
	}
}
