package rabbit.io;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import rabbit.nio.ConnectHandler;
import rabbit.nio.NioHandler;
import rabbit.util.Counter;

/** A class to handle a connection to the Internet.
 *
 * @author <a href="mailto:robo@khelekore.org">Robert Olofsson</a>
 */
public class WebConnection implements Closeable {
    private int id;
    private Address address;
    private Counter counter;
    private SocketChannel channel;
    private long releasedAt = -1;
    private boolean keepalive = true;
    private boolean mayPipeline = false;
    
    private static int idCounter;

    /** Create a new WebConnection to the given InetAddress and port.
     * @param address the computer to connect to.
     * @param counter the Counter to used to collect statistics
     */
    public WebConnection (Address address, Counter counter) {
	this.id = idCounter++;
	this.address = address;
	this.counter = counter;
	counter.inc ("WebConnections created");
    }

    @Override public String toString () {
	return "WebConnection(id: " + id + 
	    ", address: "  + address + 
	    ", keepalive: " + keepalive + 
	    ", releasedAt: " + releasedAt + ")";
    }

    public Address getAddress () {
	return address;
    }

    public SocketChannel getChannel () {
	return channel;
    }

    public void close () throws IOException {
	counter.inc ("WebConnections closed");
	channel.close ();
    }

    public void connect (NioHandler nioHandler, WebConnectionListener wcl) 
	throws IOException {
	// if we are a keepalive connection then just say so..
	if (channel != null && channel.isConnected ()) {
	    wcl.connectionEstablished (this);
	} else {
	    // ok, open the connection....
	    channel = SocketChannel.open ();
	    channel.configureBlocking (false);
	    SocketAddress addr = 
		new InetSocketAddress (address.getInetAddress (),
				       address.getPort ());
	    boolean connected = channel.connect (addr);
	    if (connected) {
		channel.socket ().setTcpNoDelay (true);
		wcl.connectionEstablished (this);
	    } else {
		new ConnectListener (wcl).waitForConnection (nioHandler);
	    }
	}
    }

    private class ConnectListener implements ConnectHandler {
	private WebConnectionListener wcl;
	private Long timeout;

	public ConnectListener (WebConnectionListener wcl) {
	    this.wcl = wcl;
	}

	public void waitForConnection (NioHandler nioHandler) throws IOException {
	    timeout = nioHandler.getDefaultTimeout ();
	    nioHandler.waitForConnect (channel, this);
	}

	public void closed () {
	    wcl.failed (new IOException ("channel closed before connect"));
	}
	
	public void timeout () {
	    wcl.timeout ();
	}

	public boolean useSeparateThread () {
	    return false;
	}
	
	public String getDescription () {
	    return "WebConnection$ConnectListener: address: " + address;
	}

	public Long getTimeout () {
	    return timeout;
	}

	public void connect () {
	    try {
		channel.finishConnect ();
		channel.socket ().setTcpNoDelay (true);
		wcl.connectionEstablished (WebConnection.this);
	    } catch (IOException e) {
		wcl.failed (e);
	    }
	}
    }
    
    /** Set the keepalive value for this WebConnection, 
     *  Can only be turned off. 
     * @param b the new keepalive value.
     */
    public void setKeepalive (boolean b) {
	keepalive &= b;
    }

    /** Get the keepalive value of this WebConnection.
     * @return true if this WebConnection may be reused.
     */
    public boolean getKeepalive () {
	return keepalive;
    }

    /** Mark this WebConnection as released at current time.
     */
    public void setReleased () {
	releasedAt = System.currentTimeMillis ();
    }

    /** Get the time that this WebConnection was released.
     */
    public long getReleasedAt () {
	return releasedAt;
    }

    /** Mark this WebConnection for pipelining.
     */
    public void setMayPipeline (boolean b) {
	mayPipeline = b;
    }

    /** Check if this WebConnection may be used for pipelining.
     */
    public boolean mayPipeline () {
	return mayPipeline;
    }
}

@@ -173,16 +173,66 @@
 hannel;%0A
+import java.util.concurrent.atomic.AtomicInteger;%0A
 import r
@@ -718,20 +718,16 @@
  false;%0A
-    
 %0A    pri
@@ -742,21 +742,55 @@
 tic 
-int idCounter
+AtomicInteger idCounter = new AtomicInteger (0)
 ;%0A%0A 
@@ -1066,10 +1066,27 @@
 nter
-++
+.getAndIncrement ()
 ;%0A%09t
@@ -1258,17 +1258,16 @@
 %22 + id +
- 
 %0A%09    %22,
@@ -1290,17 +1290,16 @@
 ddress +
- 
 %0A%09    %22,
@@ -1325,17 +1325,16 @@
 palive +
- 
 %0A%09    %22,
@@ -1685,17 +1685,16 @@
 ner wcl)
- 
 %0A%09throws
@@ -2002,17 +2002,16 @@
 s addr =
- 
 %0A%09%09new I
@@ -2797,25 +2797,24 @@
 nect%22));%0A%09%7D%0A
-%09
 %0A%09public voi
@@ -2914,17 +2914,16 @@
 lse;%0A%09%7D%0A
-%09
 %0A%09public
@@ -3294,18 +3294,161 @@
 %0A%09%7D%0A
+%0A%09@Override public String toString () %7B%0A%09    return getClass ().getSimpleName () + %22%7B%22 + address + %22%7D@%22 +%0A%09%09Integer.toString (hashCode (), 16);%0A%09%7D%0A
     %7D%0A
-    
 %0A   
@@ -3499,17 +3499,16 @@
 nection,
- 
 %0A     * 
@@ -3531,17 +3531,16 @@
 ned off.
- 
 %0A     * 

Make idCounter an AtomicInteger and add a toString to the ConnectListener.