package org.craft.atom.nio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import lombok.ToString;

import org.craft.atom.io.IoHandler;
import org.craft.atom.io.IoProtocol;
import org.craft.atom.nio.api.NioAcceptorConfig;
import org.craft.atom.nio.spi.NioBufferSizePredictorFactory;
import org.craft.atom.nio.spi.NioChannelEventDispatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Acceptor for incoming connection based on TCP.
 *  
 * @author mindwind
 * @version 1.0, Feb 22, 2013
 */
@ToString(callSuper = true)
public class NioTcpAcceptor extends NioAcceptor {
	
	
	private static final Logger LOG = LoggerFactory.getLogger(NioTcpAcceptor.class);
	
	
	// ~ -------------------------------------------------------------------------------------------------------------
	
	
	public NioTcpAcceptor(IoHandler handler, int port) {
		super(handler, port);
	}

	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, int port) {
		super(handler, config, port);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, int port) {
		super(handler, config, dispatcher, port);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory, int port) {
		super(handler, config, dispatcher, predictorFactory, port);
	}
	
	public NioTcpAcceptor(IoHandler handler, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
		super(handler, firstLocalAddress, otherLocalAddresses);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
		super(handler, config, firstLocalAddress, otherLocalAddresses);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
		super(handler, config, dispatcher, firstLocalAddress, otherLocalAddresses);
	}

	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory, SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
		super(handler, config, dispatcher, predictorFactory, firstLocalAddress, otherLocalAddresses);
	}
	
	public NioTcpAcceptor(IoHandler handler) {
		super(handler);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config) {
		super(handler, config);
	}
	
	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher) {
		super(handler, config, dispatcher);
	}

	public NioTcpAcceptor(IoHandler handler, NioAcceptorConfig config, NioChannelEventDispatcher dispatcher, NioBufferSizePredictorFactory predictorFactory) {
		super(handler, config, dispatcher, predictorFactory);
	}
	
	
	// ~ -------------------------------------------------------------------------------------------------------------

	
	@Override
	protected void bindByProtocol(SocketAddress address) throws IOException {
		ServerSocketChannel ssc = ServerSocketChannel.open();
		ssc.configureBlocking(false);
		ServerSocket ss = ssc.socket();
		ss.setReuseAddress(config.isReuseAddress());
		ss.bind(address, config.getBacklog());
		ssc.register(selector, SelectionKey.OP_ACCEPT);
		boundmap.put(address, ssc);
	}

	@Override
	protected NioByteChannel acceptByProtocol(SelectionKey key) throws IOException {
		if (key == null || !key.isValid() || !key.isAcceptable()) {
            return null;
        }
		
		ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
		SocketChannel sc = null;
		try {
			sc = ssc.accept();
			if (sc == null)              {            return null; }
			if (isChannelSizeOverflow()) { close(sc); return null; }
			
			sc.configureBlocking(false);
			NioByteChannel channel = new NioTcpByteChannel(sc, config, predictorFactory.newPredictor(config.getMinReadBufferSize(), config.getDefaultReadBufferSize(), config.getMaxReadBufferSize()), dispatcher);
			NioProcessor processor = pool.pick(channel);
			processor.setProtocol(IoProtocol.TCP);
			channel.setProcessor(processor);
			processor.add(channel);
			return channel;
		} catch (IOException e) {
			close(sc);
			throw e;
		}
	}
	
	private boolean isChannelSizeOverflow() throws IOException {
		int currentChannelSize = x().getAliveChannels().size();
		int allowChannelSize = config.getChannelSize();
		if (currentChannelSize >= allowChannelSize) {
			LOG.info("Channel size overflow, |allowChannelSize={}, currentChannelSize={}|", allowChannelSize, currentChannelSize);
			return true;
		}
		return false;
	}
	
	private void close(SocketChannel sc) {
		if (sc == null) { return; }
		
		try {
			sc.close();
		} catch (IOException ex) {
			LOG.error("[CRAFT-ATOM-NIO] Close exception", ex);
		}
	}

}

@@ -4715,16 +4715,33 @@
 G.info(%22
+%5BCRAFT-ATOM-NIO%5D 
 Channel 

fix - log format