package cs451.net.handler;

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

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * NetEventHandler for Top Level
 *
 * SendIf: based on window, add messages to be sent
 *
 * Send: SendIf
 * Deliver: SendIf
 *
 * @author C. Hölzl
 */
public class NetHandlerTOPL extends NetEventHandlerAbstract {

    private final AtomicInteger toSend = new AtomicInteger(0);
    private final AtomicInteger delivered = new AtomicInteger(0);
    private final AtomicInteger waiting = new AtomicInteger(0);

    private final AtomicBoolean status = new AtomicBoolean(false);

    private Host me;

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

    @Override
    public synchronized void broadcastIf() {
        if( !status.get() ) return;
        while (toSend.get() > 0 && waiting.get() < NetManager.WINDOW_WIDTH) {
            toSend.decrementAndGet();
            waiting.incrementAndGet();
            broadcastNextSync(NetEvent.EMPTY());
        }
    }


    @Override
    public void broadcast(NetEvent ne) {
        status.set(true);
        broadcastIf();
    }

    @Override
    public synchronized void deliver(NetEvent ne) {
        if (ne.message.src == me.getId()) {
            delivered.incrementAndGet();
            waiting.decrementAndGet();
        }
        broadcastIf();
    }

    @Override
    public boolean isDone() {
        return (toSend.get()==0) && (waiting.get()==0);
    }

    @Override
    public void start(Host h, Parser p) {
        super.start(h,p);
        me = h;
        toSend.set(p.messageCount());
    }
}
