package net.i2p.client.streaming.impl;

import net.i2p.I2PAppContext;
import net.i2p.util.Log;

/**
 * <p>Scheduler used for after both SYNs have been ACKed and both sides 
 * have closed the stream, but either we haven't ACKed their close or
 * they haven't ACKed ours.</p>
 *
 * <h2>Entry conditions:</h2>
 * <ul>
 * <li>Both sides have closed.</li>
 * <li>At least one direction has not ACKed the close.</li>
 * </ul>
 *
 * <h2>Events:</h2>
 * <ul>
 * <li>Packets received (which may or may not ACK the ones sent)</li>
 * <li>RESET received</li>
 * <li>Message sending fails (error talking to the session)</li>
 * <li>Message sending fails (too many resends)</li>
 * </ul>
 *
 * <h2>Next states:</h2>
 * <ul>
 * <li>{@link SchedulerClosed closed} - after both sending and receiving ACKs on the CLOSE</li>
 * <li>{@link SchedulerDead dead} - after sending or receiving a RESET</li>
 * </ul>
 *
 */
class SchedulerClosing extends SchedulerImpl {

    public SchedulerClosing(I2PAppContext ctx) {
        super(ctx);
    }
    
    public boolean accept(Connection con) {
        if (con == null)
            return false;
        long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
        boolean ok = (!con.getResetSent()) && 
                     (!con.getResetReceived()) &&
                     ( (con.getCloseSentOn() > 0) || (con.getCloseReceivedOn() > 0) ) &&
                     (timeSinceClose < Connection.DISCONNECT_TIMEOUT) &&
                     ( (con.getUnackedPacketsReceived() > 0) || (con.getUnackedPacketsSent() > 0) );
        return ok;
    }
    
    public void eventOccurred(Connection con) {
        long nextSend = con.getNextSendTime();
        long now = _context.clock().now();
        long remaining;
        if (nextSend <= 0) {
            remaining = con.getOptions().getSendAckDelay();
            nextSend = now + remaining;
            con.setNextSendTime(nextSend);
        } else {
            remaining = nextSend - now;
        }
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("Event occurred w/ remaining: " + remaining + " on " + con);
        if (remaining <= 0) {
            if (con.getCloseSentOn() <= 0) {
                con.sendAvailable();
            } else {
                //con.ackImmediately();
            }
            con.setNextSendTime(now + con.getOptions().getSendAckDelay());
        } else {
            //if (remaining < 5*1000)
            //    remaining = 5*1000;
            //con.setNextSendTime(when
            reschedule(remaining, con);
        }
    }
}
