package org.maachang.comet.net.nio ;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.util.thread.LoopThread;

/**
 * NIO-TCP/IPサーバ処理.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
class NioTcpServer extends LoopThread {
    
    /**
     * ログ.
     */
    private static final Log LOG = LogFactory.getLog( NioTcpServer.class ) ;
    
    /**
     * サーバソケット.
     */
    private ServerSocket serverSocket = null ;
    
    /**
     * サーバソケットチャネル.
     */
    private ServerSocketChannel server = null ;
    
    /**
     * セレクタ.
     */
    private Selector selector = null ;
    
    /**
     * 受信スレッド.
     */
    private NioReceiveServerArray recvServer = null ;
    
    /**
     * コンストラクタ.
     */
    private NioTcpServer() {
        
    }
    
    /**
     * コンストラクタ.
     * @param recvLen 受信受け取りスレッド数を設定します.
     * @param turboMode [true]の場合、ターボモードで実行されます.
     * @param channel 対象のサーバソケットを設定します.
     * @param array 受信実行スレッドとリンクするキューを設定します.
     * @exception Exception 例外.
     */
    public NioTcpServer( int recvLen,boolean turboMode,ServerSocketChannel channel,ReceiveLinkQueue queue )
        throws Exception {
        if( channel == null || queue == 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.recvServer = new NioReceiveServerArray( turboMode,queue,recvLen ) ;
        this.startThread() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        super.stopThread() ;
    }
    
    /**
     * オブジェクトクリア.
     */
    protected void clear() {
        if( this.recvServer != null ) {
            this.destroy() ;
        }
        this.recvServer = null ;
        if( this.selector != null ) {
            try {
                Iterator itr = this.selector.keys().iterator();
                while(itr.hasNext()){
                    try {
                        SelectionKey key = (SelectionKey)itr.next();
                        Channel c = key.channel();
                        c.close();
                    } 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 ;
    }
    
    /**
     * 実行処理.
     * @exception Exception 例外.
     */
    protected boolean execution() throws Exception {
        while ( selector.select() > 0 ) {
            Iterator itr = selector.selectedKeys().iterator() ;
            while(itr.hasNext()) {
                SelectionKey key = ( SelectionKey )itr.next() ;
                itr.remove() ;
                // keyが存在しない場合.
                if( key == null ) {
                    continue ;
                }
                // use-socket.
                if( key.isValid() == false ) {
                    NioTcpUtil.destroyKey( key ) ;
                }
                // accept.
                NioTcpUtil.toAcceptByPlural( key,recvServer ) ;
            }
        }
        return true ;
    }
    
    /**
     * 例外ハンドラ.
     */
    protected void toException( Throwable e ) {
        LOG.warn( "error",e ) ;
    }
}
