package org.maachang.comet.net.nio ;

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;

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


/**
 * Nio-SSL用受信サーバ群管理.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1C
 */
class NioSslReceiveServerArray {
    
    /**
     * デフォルトサイズ.
     */
    private static final int MIN_SIZE = 1 ;
    
    /**
     * NioReceiveServer.
     */
    private NioSslReceiveServer[] recvs = null ;
    
    /**
     * 受信スレッド管理数.
     */
    private int size = -1 ;
    
    /**
     * シーケンスID.
     */
    private volatile int seq = 0 ;
    
    /**
     * コンストラクタ.
     */
    private NioSslReceiveServerArray() {
        
    }
    
    /**
     * コンストラクタ.
     * @param queue 対象のキューを設定します.
     * @param size 受信スレッド数を設定します.
     * @exception Exception 例外.
     */
    public NioSslReceiveServerArray( ReceiveLinkQueue queue,int size )
        throws Exception {
        if( queue == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( size <= MIN_SIZE ) {
            size = MIN_SIZE ;
        }
        NioSslReceiveServer[] rv = new NioSslReceiveServer[ size ] ;
        for( int i = 0 ; i < size ; i ++ ) {
            rv[ i ] = new NioSslReceiveServer( queue ) ;
        }
        this.recvs = rv ;
        this.size = size ;
        this.seq = 0 ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public synchronized void destroy() {
        if( recvs != null ) {
            int len = recvs.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( recvs[ i ] != null ) {
                    recvs[ i ].destroy() ;
                }
                recvs[ i ] = null ;
            }
            recvs = null ;
        }
    }
    
    /**
     * Acceptされた要素を受信用にセット.
     * @param element 対象のElementを設定します.
     * @exception Exception 例外.
     */
    public synchronized void setElement( NioElement element )
        throws Exception {
        if( element == null ) {
            return ;
        }
        if( recvs == null ) {
            element.destroy() ;
            return ;
        }
        if( seq >= size ) {
            seq = 0 ;
        }
        recvs[ seq ].setElement( element ) ;
        seq ++ ;
    }
}

/**
 * 1つの受信ソケット.
 */
class NioSslReceiveServer extends LoopThread {
    
    /**
     * ログ.
     */
    private static final Log LOG = LogFactory.getLog( NioSslReceiveServer.class ) ;
    
    /**
     * セレクタタイムアウト.
     */
    //private static final long SELECTOR_TIMEOUT = 1000L ;
    
    /**
     * Selector.
     */
    private Selector selector = null ;
    
    /**
     * 読み込み可能キュー.
     */
    private IsReadQueue isRecvQueue = null ;
    
    /**
     * 書き込み可能キュー.
     */
    private IsWriteQueue isWriteQueue = null ;
    
    /**
     * 受信処理キュー.
     */
    private ReceiveLinkQueue executionQueue = null ;
    
    /**
     * コンストラクタ.
     */
    private NioSslReceiveServer() {
        
    }
    
    /**
     * コンストラクタ.
     * @param queue 受信実行スレッドとリンクするキューを設定します.
     * @exception Exception 例外.
     */
    public NioSslReceiveServer( ReceiveLinkQueue executionQueue )
        throws Exception {
        if( executionQueue == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.selector = Selector.open() ;
        this.isRecvQueue = new IsReadQueue() ;
        this.isWriteQueue = new IsWriteQueue() ;
        this.executionQueue = executionQueue ;
        this.startThread() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        super.stopThread() ;
    }
    
    /**
     * AcceptされたNioElementを受信用にセット.
     * @param element 対象のElementを設定します.
     * @exception Exception 例外.
     */
    public synchronized void setElement( NioElement element )
        throws Exception {
        element.setIsWriteQueue( isWriteQueue ) ;
        isRecvQueue.append( element ) ;
        if( selector != null && selector.isOpen() ) {
            selector.wakeup() ;
        }
    }
    
    /**
     * オブジェクトクリア.
     */
    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) {}
            try {
                this.selector.close() ;
            } catch(Exception e) {}
        }
        this.selector = null ;
        this.isRecvQueue = null ;
        this.isWriteQueue = null ;
        this.executionQueue = null ;
    }
    
    /**
     * 実行処理.
     * @exception Exception 例外.
     */
    protected boolean execution() throws Exception {
        while (true) {
            if( super.isStop() ) {
                break ;
            }
            NioTcpUtil.isSendOrWrite( selector,isRecvQueue,isWriteQueue ) ;
            while( selector.select() > 0 ) {
                Iterator itr = selector.selectedKeys().iterator() ;
                while(itr.hasNext()) {
                    NioTcpUtil.isSendOrWrite( selector,isRecvQueue,isWriteQueue ) ;
                    SelectionKey key = ( SelectionKey )itr.next() ;
                    itr.remove() ;
                    // keyが存在しない場合.
                    if( key == null ) {
                        continue ;
                    }
                    // use-socket.
                    if( key.isValid() == false ) {
                        NioTcpUtil.destroyKey( key ) ;
                        continue ;
                    }
                    // read.
                    NioSslUtil.isRead( selector,key,executionQueue ) ;
                    // write.
                    NioSslUtil.isWrite( selector,key,executionQueue ) ;
                }
            }
        }
        return true ;
    }
    
    /**
     * 例外ハンドラ.
     */
    protected void toException( Throwable e ) {
        LOG.warn( "error",e ) ;
    }
}

