package org.maachang.comet.net.nio ;

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

import org.maachang.comet.net.ssl.SslElement;
import org.maachang.comet.net.ssl.SslOption;
import org.maachang.util.thread.LoopThread;

/**
 * NIO-SSLサーバ処理.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
class NioSslServer extends LoopThread {
    
    /**
     * サーバソケット.
     */
    private ServerSocket serverSocket = null ;
    
    /**
     * サーバソケットチャネル.
     */
    private ServerSocketChannel server = null ;
    
    /**
     * セレクタ.
     */
    private Selector selector = null ;
    
    /**
     * 受信スレッド.
     */
    private NioSslReceiveServerArray recvServer = null ;
    
    /**
     * SSLオプション.
     */
    private SslOption option = null ;
    
    /**
     * コンストラクタ.
     */
    private NioSslServer() {
        
    }
    
    /**
     * コンストラクタ.
     * @param recvLen 受信受け取りスレッド数を設定します.
     * @param channel 対象のサーバソケットを設定します.
     * @param opt SSLオプションを設定します.
     * @param array 受信実行スレッドとリンクするキューを設定します.
     * @exception Exception 例外.
     */
    public NioSslServer( int recvLen,ServerSocketChannel channel,SslOption opt,ReceiveLinkQueue queue )
        throws Exception {
        if( channel == null || queue == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.serverSocket = channel.socket() ;
        this.serverSocket.setReuseAddress( true ) ;
        this.server = channel ;
        this.option = opt ;
        this.selector = Selector.open() ;
        this.server.register( this.selector,SelectionKey.OP_ACCEPT ) ;
        this.recvServer = new NioSslReceiveServerArray( 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.option = null ;
        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() ;
                
                // accept.
                if( key.isAcceptable() ) {
                    SocketChannel sc = null ;
                    try {
                        // 新しいAcceptをセット.
                        ServerSocketChannel ssc = ( ServerSocketChannel )key.channel() ;
                        sc = ssc.accept() ;
                        if( sc != null ) {
                            setOption( sc ) ;
                            NioElement em = new NioElement( sc ) ;
                            // SSL要素を付加.
                            em.setSslElement( new SslElement(
                                em,option.getSSLEngine() ) ) ;
                            recvServer.setElement( em ) ;
                        }
                    } catch( Exception e ) {
                        if( sc != null ) {
                            try { sc.close() ; } catch( Exception ee ) {}
                        }
                    }
                }
                
            }
        }
        return true ;
    }
    
    private static final int BUFFER = 131072 ;
    private static final int LINGER = 5 ;
    
    /**
     * Acceptソケット設定.
     */
    private void setOption( SocketChannel channel )
        throws Exception {
        channel.configureBlocking( false ) ;
        Socket soc = channel.socket() ;
        soc.setSendBufferSize( BUFFER ) ;
        soc.setReceiveBufferSize( BUFFER ) ;
        soc.setKeepAlive( true ) ;
        soc.setTcpNoDelay( true ) ;
        soc.setReuseAddress( true ) ;
        soc.setSoLinger( true,LINGER ) ;
    }
}
