/**
 * 
 */
package com.trendrr.strest.server.connections;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.jboss.netty.channel.ChannelFuture;

import com.trendrr.strest.server.ResponseBuilder;
import com.trendrr.strest.server.callbacks.DisconnectCallback;
import com.trendrr.strest.server.v2.models.StrestRequest;
import com.trendrr.strest.server.v2.models.StrestResponse;
import com.trendrr.strest.server.v2.models.StrestHeader.TxnStatus;



/**
 * @author Dustin Norlander
 * @created May 31, 2012
 * 
 */
public abstract class StrestConnectionChannel {

	public abstract boolean isConnected();
	
	private ConcurrentHashMap<String, StrestConnectionTxn> transactions = new ConcurrentHashMap<String, StrestConnectionTxn>();
	private ConcurrentHashMap<String,Object> connectionStorage = new ConcurrentHashMap<String,Object>();
	private ConcurrentLinkedQueue<DisconnectCallback> disconnectCallbacks = new ConcurrentLinkedQueue<DisconnectCallback>();
	
	
	/**
	 * send a message. 
	 * @param response
	 * @return 
	 * @throws Exception
	 */
	protected abstract Object doSendMessage(StrestResponse response) throws Exception;
	
	/**
	 * return the address of the connected client (or null if unavailable).
	 * @return
	 */
	public abstract String getRemoteAddress();
	
	public Object sendMessage(ResponseBuilder responseBuilder) throws Exception{
		return this.sendMessage(responseBuilder.getResponse());
	}
	
	public Object sendMessage(StrestResponse response) throws Exception {
		// Write the response.
		if (response.getTxnStatus() == TxnStatus.COMPLETED) {
			//remove the txn
			this.txnComplete(response.getTxnId());
		}
		return this.doSendMessage(response);
	}
	
	/**
	 * registers a callback for when the connection is disconnected..
	 * @param callback
	 */
	public void onDisconnect(DisconnectCallback callback) {
		this.disconnectCallbacks.add(callback);
	}
	
	/**
	 * gets the threadsafe connection storage, never null.
	 * @return
	 */
	public Map<String, Object> getStorage() {
		return connectionStorage;
	}
	
	public synchronized void cleanup() {		
		//first cleanup all the txns
		for (String txnId : this.transactions.keySet()) {
			this.transactions.get(txnId).cleanup();
		}
		this.transactions = null;
		
		//call all the disconnectCallbacks
		while(!this.disconnectCallbacks.isEmpty()) {
			DisconnectCallback cb = this.disconnectCallbacks.poll();
			cb.disconnected(this);
		}
		this.disconnectCallbacks.clear();
//		this.channel = null;
		this.connectionStorage.clear();
	}
	
	public void incoming(StrestRequest request) {
		String txnId = request.getTxnId();
		if (txnId == null) {
			//duh, what do we do now?
			//guess this is a normal HTTP request.
			return;
		}
		if (this.transactions.get(txnId) == null) {
			this.transactions.putIfAbsent(txnId, new StrestConnectionTxn(request));
		}
	}
	
	public StrestConnectionTxn getTxnConnection(String txnId) {
		return this.transactions.get(txnId);
	}
	
	public void txnComplete(String id) {
		if (id == null)
			return;
		StrestConnectionTxn txn = this.transactions.remove(id);
		if (txn == null)
			return;
		txn.cleanup();
	}
	
	
}

@@ -2141,16 +2141,88 @@
 p() %7B%09%09%0A
+%09%09if (this.transactions == null) %7B%0A%09%09%09//already cleaned.%0A%09%09%09return;%0A%09%09%7D%0A
 %09%09//firs

check if channel has already been cleaned