package org.maachang.comet.net.nio ;

import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

import org.maachang.util.thread.LoopThread;

/**
 * NIO-TCP/IPシングルサーバ処理.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
class NioSingleTcpServer extends LoopThread {
    
    /**
     * ログ.
     */
    //private static final Log LOG = LogFactory.getLog( NioSingleTcpServer.class ) ;
    
    /**
     * 受信バッファサイズ.
     */
    private static final int BUFFER_LENGTH = 512 ;
    
    /**
     * サーバソケット.
     */
    private ServerSocket serverSocket = null ;
    
    /**
     * サーバソケットチャネル.
     */
    private ServerSocketChannel server = null ;
    
    /**
     * セレクタ.
     */
    private Selector selector = null ;
    
    /**
     * 書き込み可能キュー.
     */
    private IsWriteQueue isWriteQueue = null ;
    
    /**
     * 受信処理キュー.
     */
    private ReceiveLinkQueue executionQueue = null ;
    
    /**
     * 受信バッファ.
     */
    private ByteBuffer buffer = null ;
    
    /**
     * ターボモード.
     */
    private boolean turboMode = false ;
    
    /**
     * コンストラクタ.
     */
    private NioSingleTcpServer() {
        
    }
    
    /**
     * コンストラクタ.
     * @param turboMode [true]の場合、ターボモードで実行されます.
     * @param channel 対象のサーバソケットを設定します.
     * @param executionQueue 受信実行スレッドとリンクするキューを設定します.
     * @exception Exception 例外.
     */
    public NioSingleTcpServer( boolean turboMode,ServerSocketChannel channel,ReceiveLinkQueue executionQueue )
        throws Exception {
        if( channel == null || executionQueue == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.serverSocket = channel.socket() ;
        this.serverSocket.setReuseAddress( true ) ;
        this.server = channel ;
        this.selector = Selector.open() ;
        this.server.register( this.selector,SelectionKey.OP_ACCEPT ) ;
        this.executionQueue = executionQueue ;
        this.isWriteQueue = new IsWriteQueue() ;
        this.buffer = ByteBuffer.allocateDirect( BUFFER_LENGTH ) ;
        this.turboMode = turboMode ;
        this.startThread() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        super.stopThread() ;
    }
    
    /**
     * オブジェクトクリア.
     */
    protected void clear() {
        if( this.selector != null ) {
            try {
                Iterator itr = this.selector.keys().iterator();
                while(itr.hasNext()){
                    try {
                        NioTcpUtil.destroyKey( (SelectionKey)itr.next() ) ;
                    } catch( Exception ee ) {
                    }
                }
            } catch(Exception e) {}
        }
        this.selector = null ;
        if( this.server != null ) {
            try {
                this.server.close() ;
            } catch( Exception e ) {
            }
        }
        if( this.serverSocket != null ) {
            try {
                this.serverSocket.close() ;
            } catch( Exception e ) {
            }
        }
        this.serverSocket = null ;
        this.server = null ;
        this.isWriteQueue = null ;
        this.executionQueue = null ;
        this.buffer = null ;
    }
    
    /**
     * 実行処理.
     * @exception Exception 例外.
     */
    protected boolean execution() throws Exception {
        NioTcpUtil.isWrite( isWriteQueue ) ;
        while ( selector.select() > 0 ) {
            Iterator itr = selector.selectedKeys().iterator() ;
            while(itr.hasNext()) {
                NioTcpUtil.isWrite( isWriteQueue ) ;
                SelectionKey key = ( SelectionKey )itr.next() ;
                itr.remove() ;
                // keyが存在しない場合.
                if( key == null ) {
                    continue ;
                }
                // use-socket.
                if( key.isValid() == false ) {
                    NioTcpUtil.destroyKey( key ) ;
                    continue ;
                }
                // accept.
                else if( NioTcpUtil.toAcceptBySingle( selector,key,isWriteQueue ) == false ) {
                    // read.
                    NioTcpUtil.isRead( turboMode,selector,key,executionQueue,buffer ) ;
                    // write.
                    NioTcpUtil.isWrite( selector,key ) ;
                }
            }
        }
        return true ;
    }
    
    /**
     * 例外ハンドラ.
     */
    protected void toException( Throwable e ) {
        //LOG.warn( "error",e ) ;
    }
}
