package ca.sebastiendionne.grizzly.server;

import java.nio.channels.SelectionKey;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ca.sebastiendionne.grizzly.filter.EchoQueryManagerFilter;
import ca.sebastiendionne.grizzly.filter.EchoQueryProtocolFilter;
import ca.sebastiendionne.grizzly.manager.IEchoManager;
import ca.sebastiendionne.grizzly.queue.QueueConsumer;
import ca.sebastiendionne.grizzly.queue.QueueItem;

import com.sun.grizzly.BaseSelectionKeyHandler;
import com.sun.grizzly.Controller;
import com.sun.grizzly.ControllerStateListenerAdapter;
import com.sun.grizzly.DefaultProtocolChain;
import com.sun.grizzly.DefaultProtocolChainInstanceHandler;
import com.sun.grizzly.ProtocolChain;
import com.sun.grizzly.ProtocolChainInstanceHandler;
import com.sun.grizzly.TCPSelectorHandler;
import com.sun.grizzly.util.ConnectionCloseHandler;

public class GrizzlyServer implements IEchoManager {

	private static final Logger s_logger = LoggerFactory.getLogger(GrizzlyServer.class);
	
	private int port = 5000;
	
	private Controller controller;
	
	//queue
	protected ThreadGroup f_tg = null;
	protected BlockingQueue<QueueItem> blockingQueue;
	protected QueueConsumer queueConsumer;
	
	//cache
	public ConcurrentMap<SelectionKey,ClientConnectionHandler> cacheMap = new ConcurrentHashMap<SelectionKey,ClientConnectionHandler>();
	
	public void init(){
		
		f_tg = new ThreadGroup("ThreadGroup");
		
		final CountDownLatch started = new CountDownLatch(1);
	
		controller = new Controller();
		TCPSelectorHandler tcpSelectorHandler = new TCPSelectorHandler();
		tcpSelectorHandler.setPort(port);
		controller.addSelectorHandler(tcpSelectorHandler);

		BaseSelectionKeyHandler selectionKeyHandler = new BaseSelectionKeyHandler();
		
		// to be notify when a client/server close the connection
		selectionKeyHandler.setConnectionCloseHandler(new ConnectionCloseHandler() {

			public void locallyClosed(SelectionKey key) {
					if(s_logger.isDebugEnabled()){
						s_logger.debug(key + " is being locally cancelled");
					}
			   }
	
			   public void remotlyClosed(SelectionKey key) {
				   if(s_logger.isDebugEnabled()){
					   s_logger.debug(key + " is being remotly cancelled (connection closed)");
				   }
			   }
		});
		
		tcpSelectorHandler.setSelectionKeyHandler(selectionKeyHandler);
		
		// STATE Listener
		controller.addStateListener(new ControllerStateListenerAdapter() {
	
			public void onException(Throwable e) {
				s_logger.error("Grizzly controller exception:" + e.getMessage());
			}
	
			public void onReady() {
				if(s_logger.isDebugEnabled()){
					s_logger.debug("Ready!");
				}
				started.countDown();
			}
	
		});
		
		
		// the protocol chain
		EchoQueryProtocolFilter protocolParser = new EchoQueryProtocolFilter();
		EchoQueryManagerFilter echoManagerFilter = new EchoQueryManagerFilter(this);
		   
		final ProtocolChain protocolChain = new DefaultProtocolChain();
		protocolChain.addFilter(protocolParser);
		protocolChain.addFilter(echoManagerFilter);
		((DefaultProtocolChain) protocolChain).setContinuousExecution(true);


		ProtocolChainInstanceHandler pciHandler = new DefaultProtocolChainInstanceHandler() {

           public boolean offer(ProtocolChain protocolChain) {
               return false;

           }

           public ProtocolChain poll() {

               return protocolChain;
           }
       };

       controller.setProtocolChainInstanceHandler(pciHandler);
       
       // start the server
       new Thread(f_tg, controller,"Server").start();
       
       try {
			started.await();
		} catch (Exception e) {
			s_logger.error("Timeout in wait" + e.getMessage());
		}
		
	   // extra stuff : Queue
	   blockingQueue = new LinkedBlockingQueue<QueueItem>();
	   queueConsumer = new QueueConsumer(this, blockingQueue);
	   new Thread(f_tg, queueConsumer,"QueueConsumer").start();
	   
	}
	
	public void addToCache(SelectionKey key, ClientConnectionHandler ch){
		cacheMap.put(key, ch);
	}
	
	public void removeFromCache(SelectionKey key){
		cacheMap.remove(key);
	}
	
	public void addToQueue(QueueItem item){
		blockingQueue.add(item);
	}
	
	public void process(QueueItem item){
		String msg = item.getQuery() + " : To you from Server[EOQ]";		
		item.getClientConnectionHandler().sendToClient(msg);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		GrizzlyServer server = new GrizzlyServer();
		
		server.init();
		
		System.out.println("Initialization finished");

	}

}
