/*
 * Created on 30-apr-2004
 */
package jaCop.heartbeat;

import java.util.*;

import convFramework.traffic.event.*;

import jaCop.traffic.*;
import jaCop.nodeStatus.*;
import jaCop.domain.*;
import jaCop.exceptions.*;

/**
 * @author Lompa
 */
public class Monitor implements IContentPacketArrivedListener, IPacketTransmittedListener {
	
	/* flag di istanzizione */
	//private static boolean instatiated = false;
	
	/* campi */
	private TreeMap controllers = new TreeMap();
	
	private COPPacketManager packetMan = null;
	private long heartbeatTimeout = 30000;
	private StatusStore store = null;
	
	/* costruttori ******************************************************************************/
	/**
	 * @param packetMan
	 * @param heartbeatTimeout
	 * @param store
	 */
	public Monitor(long heartbeatTimeout, COPPacketManager packetMan, StatusStore store) {
		this.packetMan = packetMan;
		this.heartbeatTimeout = heartbeatTimeout;
		this.store = store;
	}
	
	/* metodi per controllare o smettere i controllare nodi *************************************/
	/**
	 * @param node
	 * @param community
	 */
	public synchronized void startControl(COPNode node, String community){
		if (!isControlled(community)){
			startControl(community);
		}
		CommunityMonitor cMon = getCommunityController(community);
		cMon.control(node);
		/* mi assicuro che i fattor i di carco siano sincronizzati */
		store.setLoadFactor(community, node, node.getLoadFactor());
	}
	
	/**
	 * @param node
	 * @param community
	 */
	public synchronized void stopControl(COPNode node, String community){
		CommunityMonitor cMon = getCommunityController(community);
		if (cMon != null){
			cMon.stopControl(node);
		}	
	}
	
	/**
	 * @param node
	 * @param community
	 * @return
	 */
	public synchronized boolean isControlled(COPNode node, String community){
		CommunityMonitor cMon = getCommunityController(community);
		if (cMon != null){
			return cMon.isControlled(node);
		}
		else
			return false;
	}
	
	private CommunityMonitor getCommunityController(String community){
		return (CommunityMonitor) controllers.get(community);
	}
	
	/* metodi per controllare o smettere i controllare comunità **********************************/
	/**
	 * Predispone il controlo di una nuova comunità, senza iniziare il controllo di alcun nodo. Se la comunità
	 * è già sotto controllo non fa nulla
	 * @param community il nome della comunità
	 */
	public synchronized void startControl(String community){
		if (!isControlled(community)){
			CommunityMonitor cMon = new CommunityMonitor(community, packetMan, heartbeatTimeout);
			controllers.put(community, cMon);
		}
	}
	
	/**
	 * Termina il controllo di una comunità; se la comunità non è controllata non fa nulla
	 * @param community il nomde della comunità
	 */
	public synchronized void stopControl(String community){
		if (isControlled(community)){
			CommunityMonitor cMon = (CommunityMonitor) controllers.get(community);
			cMon.removeAllControlledNodes();
			controllers.remove(community);
		}
	}
	
	/**
	 * Verifca se una comunità è sotto controllo
	 * @param community il nome della comunità
	 * @return true se la comunità è sotto controllo
	 */
	public synchronized boolean isControlled(String community){
		return this.controllers.containsKey(community);
	}
	
	/* metodi per la gestione di eventi **********************************************************/
	/**
	 * Aggiorna il fattore di caric di un nodo in seguito all'arrivo di un pacchetto da esso
	 * @param event
	 */
	public void contentPacketArrived(ContentPacketArrivedEvent event){
		try {
			COPPacket packet = packetMan.translate((convFramework.domain.ContentPacket) event.getArrivedPacket());
			String community = packet.getCommunity();
			
			if (isControlled(community)){
				/* ack ad un eventuale pacchetto di heartbeat */
				if (packet.getType().equals(Heartbeat.getTypeDescription())){
					Heartbeat rhb = (Heartbeat) packet;
					/*WARNING rispondi solo agli hack provenienti dai vicini: in questa implementazione le cose 
					 * dovrebbero funzionare solo perchè ci vuole un po' perchè parta il primo heartbeat*/
					if (rhb.isAckRequired() && store.isNeighbor(community, rhb.getSender())){
						Heartbeat hb = packetMan.newHeartbeat(packet.getConvID(), community);
						hb.setReceiver(packet.getSender());
						hb.acksTo(packet.getPacketID());
						packetMan.send(hb);
						packetMan.removeConversation(packet.getConvID());
					}
				}				
				
				CommunityMonitor cMon = (CommunityMonitor) controllers.get(community);
				if (cMon.update(packet.getSender())) {
					store.setLoadFactor(community, packet.getSender(), packet.getSender().getLoadFactor());
				}
			}
		}
		catch(TranslationException e){
			/* se la traduzione del pacchetto non riesce non c'è problema */
		}
	}
	
	/* (non-Javadoc)
	 * @see convFramework.traffic.event.IMissingAckListener#missingAck(convFramework.traffic.event.MissingAckEvent)
	 */
	/*
	public void missingAck(MissingAckEvent event){
		try {
			A3Packet packet = packetMan.translate((convFramework.domain.ContentPacket) event.getTransmittedPacket());
			String community = packet.getCommunity();
			stopControl(packet.getReceiver(), packet.getCommunity());
		}
		catch(TranslationException e){
			
		}
	}
	*/
	
	/* (non-Javadoc)
	 * @see convFramework.traffic.event.IPacketTransmittedListener#packetTransmitted(convFramework.traffic.event.PacketTransmittedEvent)
	 */
	public void packetTransmitted(PacketTransmittedEvent event) {
		try {
			COPPacket packet = packetMan.translate((convFramework.domain.ContentPacket) event.getTransmittedPacket());
			String community = packet.getCommunity();
			if (packet.getType().equals(Heartbeat.getTypeDescription())){
				startControl(packet.getReceiver(), packet.getCommunity());
			}
		}
		catch(TranslationException e){
			/* se la traduzione del pacchetto non riesce è come se l'ack non fosse arrivato */
		}
	}
}
