package org.maachang.comet.net.nio ;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.ServiceDef;
import org.maachang.comet.conf.IniFile;
import org.maachang.comet.net.HttpServer;
import org.maachang.comet.net.ssl.SslOption;
import org.maachang.conf.Config;
import org.maachang.manager.GlobalManager;
import org.maachang.util.SequenceSync;

/**
 * ネットワークサービス.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
public class NioService {
    
    private static final Log LOG = LogFactory.getLog( NioService.class ) ;
    private static final int MIN_POOL = 1 ;
    private static final int MAX_POOL = 500 ;
    private static final int DEF_TCP_IO_LEN = 1 ;
    private static final int DEF_SSL_IO_LEN = 1 ;
    
    private NioTcpServer httpBase = null ;
    private NioSingleTcpServer singleHttpBase = null ;
    private NioSslServer sslBase = null ;
    private NioSingleSslServer singleSslBase = null ;
    private NioPoolThread[] pool = null ;
    
    /**
     * コンストラクタ.
     */
    public NioService() {
        
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.close() ;
    }
    
    /**
     * オープン.
     * <BR><BR>
     * NetServiceをオープンします.
     * <BR>
     * @param call 受信コールバックオブジェクトを設定します.
     * @param http HTTPサーバオブジェクトを設定します.
     * @param ssl SSLサーバオブジェクトを設定します.
     * @param pool プーリング数を設定します.
     * @exception Exception 例外.
     */
    public void open( HttpReceiveCallback call,HttpServer http,HttpServer ssl,int pool )
        throws Exception {
        if( call == null || http == null || http.isClosed() == true ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( pool <= MIN_POOL ) {
            pool = MIN_POOL ;
        }
        else if( pool >= MAX_POOL ) {
            pool = MAX_POOL ;
        }
        this.close() ;
        Config conf = ( Config )( ( IniFile )GlobalManager.getInstance().get( ServiceDef.MANAGER_BY_CONFIG ) ).getConfig() ;
        int tcpRecvLen = conf.getInt( "net","io-thread",0 ) ;
        if( tcpRecvLen <= 0 ) {
            tcpRecvLen = DEF_TCP_IO_LEN ;
        }
        int sslRecvLen = 0 ;
        if( ssl != null ) {
            sslRecvLen = conf.getInt( "ssl","io-thread",0 ) ;
            if( sslRecvLen <= 0 ) {
                sslRecvLen = DEF_SSL_IO_LEN ;
            }
        }
        boolean turboMode = conf.getBoolean( "net","turbo-mode",0 ) ; ;
        if( LOG.isDebugEnabled() ) {
            LOG.debug( new StringBuilder().append( "## tcpRecvLen:" ).append( tcpRecvLen ).
                append( " sslRecvLen:" ).append( sslRecvLen ).
                append( " turboMode:" ).append( turboMode ).toString() ) ;
        }
        try {
            ReceiveLinkQueue queue = new ReceiveLinkQueue() ;
            // TCP/IP処理.
            if( tcpRecvLen <= 1 ) {
                this.singleHttpBase = new NioSingleTcpServer( turboMode,http.channel(),queue ) ;
                if( LOG.isDebugEnabled() ) {
                    LOG.debug( "## [single]Http起動" ) ;
                }
            }
            else {
                this.httpBase = new NioTcpServer( tcpRecvLen,turboMode,http.channel(),queue ) ;
                if( LOG.isDebugEnabled() ) {
                    LOG.debug( "## Http起動" ) ;
                }
            }
            // SSL処理が存在する.
            if( ssl != null ) {
                if( sslRecvLen <= 1 ) {
                    this.singleSslBase = new NioSingleSslServer( ssl.channel(),( SslOption )ssl.option(),queue ) ;
                    if( LOG.isDebugEnabled() ) {
                        LOG.debug( "## [single]Ssl起動" ) ;
                    }
                }
                else {
                    this.sslBase = new NioSslServer( sslRecvLen,ssl.channel(),( SslOption )ssl.option(),queue ) ;
                    if( LOG.isDebugEnabled() ) {
                        LOG.debug( "## Ssl起動" ) ;
                    }
                }
            }
            SequenceSync seq = new SequenceSync() ;
            this.pool = new NioPoolThread[ pool ] ;
            for( int i = 0 ; i < pool ; i ++ ) {
                this.pool[ i ] = new NioPoolThread( call,queue,seq,i ) ;
            }
        } catch( Exception e ) {
            LOG.error( "## error",e ) ;
            this.close() ;
            throw e ;
        }
    }
    
    /**
     * クローズ処理.
     * <BR><BR>
     * NetServiceをクローズします.
     */
    public void close() {
        if( pool != null ) {
            int len = pool.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                if( pool[ i ] != null ) {
                    pool[ i ].destroy() ;
                }
                pool[ i ] = null ;
            }
        }
        if( httpBase != null ) {
            httpBase.destroy() ;
        }
        if( singleHttpBase != null ) {
            singleHttpBase.destroy() ;
        }
        if( sslBase != null ) {
            sslBase.destroy() ;
        }
        if( singleSslBase != null ) {
            singleSslBase.destroy() ;
        }
        pool = null ;
        httpBase = null ;
        singleHttpBase = null ;
        sslBase = null ;
        singleSslBase = null ;
    }
    
    /**
     * 設定されているプーリング全体数を取得.
     * @return int 設定されているプーリング全体数が返されます.
     */
    public int getAllPooling() {
        if( pool == null ) {
            return 0 ;
        }
        return pool.length ;
    }
    
    /**
     * 有効プーリングスレッド数を取得.
     * @return int 有効プーリングスレッド数が返されます.
     */
    public int getUsePooling() {
        if( pool == null ) {
            return 0 ;
        }
        int ret = 0 ;
        int len = pool.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( pool[ i ] != null && pool[ i ].isStop() == false ) {
                ret ++ ;
            }
        }
        return ret ;
    }
    
    /**
     * Activeプーリングスレッド数を取得.
     * @return int Activeプーリングスレッド数が返されます.
     */
    public int getActivePooling() {
        if( pool == null ) {
            return 0 ;
        }
        int ret = 0 ;
        int len = pool.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( pool[ i ] != null && pool[ i ].isStop() == false && pool[ i ].isExecution() ) {
                ret ++ ;
            }
        }
        return ret ;
    }
}
