package net.i2p.router.client;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageImpl;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;

/**
 * Async writer class so that if a client app hangs, they wont take down the
 * whole router with them (since otherwise the JobQueue would block until
 * the client reads from their i2cp socket, causing all sorts of bad shit to
 * happen)
 *
 * @author zzz modded to use concurrent
 */
class ClientWriterRunner implements Runnable {
    private BlockingQueue<I2CPMessage> _messagesToWrite;
    private ClientConnectionRunner _runner;
    private Log _log;
    private long _id;
    private static long __id = 0;
    
    public ClientWriterRunner(RouterContext context, ClientConnectionRunner runner) {
        _log = context.logManager().getLog(ClientWriterRunner.class);
        _messagesToWrite = new LinkedBlockingQueue();
        _runner = runner;
        _id = ++__id;
    }

    /**
     * Add this message to the writer's queue
     *
     */
    public void addMessage(I2CPMessage msg) {
        try {
            _messagesToWrite.put(msg);
        } catch (InterruptedException ie) {}
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("["+_id+"] addMessage completed for " + msg.getClass().getName());
    }

    /**
     * No more messages - dont even try to send what we have
     *
     */
    public void stopWriting() {
        _messagesToWrite.clear();
        try {
            _messagesToWrite.put(new PoisonMessage());
        } catch (InterruptedException ie) {}
    }

    public void run() {
        I2CPMessage msg;
        while (!_runner.getIsDead()) {
            try {
                msg = _messagesToWrite.take();
            } catch (InterruptedException ie) {
                continue;
            }
            if (msg.getType() == PoisonMessage.MESSAGE_TYPE)
                break;
            _runner.writeMessage(msg);
        }
    }

    /**
     * End-of-stream msg used to stop the concurrent queue
     * See http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html
     *
     */
    private static class PoisonMessage extends I2CPMessageImpl {
        public static final int MESSAGE_TYPE = 999999;
        public int getType() {
            return MESSAGE_TYPE;
        }
        public void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException {}
        public byte[] doWriteMessage() throws I2CPMessageException, IOException { return null; }
    }
}
