package cs451.net.handler;

import cs451.net.NetManager;
import cs451.net.event.Message;
import cs451.net.event.NetEvent;
import cs451.parser.Host;
import cs451.parser.Parser;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * NetEventHandler for Failure Detection
 *
 * Beat: Sends ping to hosts
 * Deliver: Marks peer as alive (Drop HRBT packets)
 *
 * @author C. Hölzl
 */
public class NetHandlerFD extends NetEventHandlerAbstract {

    private List<Host> hosts;
    private static final ConcurrentHashMap<Integer,Integer> alive = new ConcurrentHashMap<>();

    public NetHandlerFD(Class<? extends NetEventHandlerAbstract> deliverLayer, Class<? extends NetEventHandlerAbstract> broadcastLayer) {
        super(deliverLayer, broadcastLayer);
    }

    @Override
    public synchronized void beat() {
        hosts.forEach(h-> {
            alive.computeIfPresent(h.getId(),(k, v) -> {
                if(v > -1) broadcastNextAsync(NetEvent.MessageHRTB(h));
                return v+1;
            });
            if(alive.getOrDefault(h.getId(),-1) >= NetManager.FD_MAX_TRIES){
                crashNextAsync(NetEvent.Message(h, Message.EMPTY()));
                alive.remove(h.getId());
            }
        });
    }

    @Override
    public void broadcast(NetEvent ne) {
        if (alive.containsKey(ne.peer.getId())){
            broadcastNextSync(ne);
        }
    }

    @Override
    public void deliver(NetEvent ne) {
        alive.computeIfPresent(ne.peer.getId(),(k,v)->-1);
        if (ne.message.tpe != Message.TYPE.HRTB) {
            deliverNextSync(ne);
        }else{
            broadcastNextSync(NetEvent.MessageHRTB(ne.peer));
        }
    }

    @Override
    public void start(Host h, Parser p) {
        super.start(h, p);
        hosts = p.hosts();
        hosts.stream().filter(ch->ch.getId()!=h.getId()).forEach(ch-> alive.put(ch.getId(),-1));
    }
}
