package org.maachang.connector ;

import java.io.IOException;
import java.net.InetAddress;

/**
 * プーリング用クライアントコネクタ.
 * 
 * @version 2008/05/25
 * @author masahito suzuki
 * @since MaachangBase 1.02
 */
class PoolingClientConnector implements ClientConnector {
    
    /**
     * 接続先IPアドレス.
     */
    private InetAddress addr = null ;
    
    /**
     * 接続先ポート番号.
     */
    private int port = -1 ;
    
    /**
     * プーリング.
     */
    private OneUseClientConnector[] pool = null ;
    
    /**
     * コンストラクタ.
     */
    private PoolingClientConnector() {
        
    }
    
    /**
     * コンストラクタ.
     * @param headerBinary 通信ヘッダバイナリを設定します.
     * @param addr 接続先のアドレスを設定します.
     * @param port 接続先のポート番号を設定します.
     * @param timeout 受信タイムアウト値を設定します.
     * @exception Exception 例外.
     */
    public PoolingClientConnector( byte[] headerBinary,InetAddress addr,int port,int timeout,int pooling )
        throws Exception {
        if( headerBinary == null || headerBinary.length <= 0 ) {
            throw new IllegalArgumentException( "ヘッダバイナリは不正です" ) ;
        }
        if( addr == null ) {
            throw new IllegalArgumentException( "接続先アドレスは不正です" ) ;
        }
        if( port <= -1 || port >= 65536 ) {
            throw new IllegalArgumentException( "接続先ポート番号は不正です" ) ;
        }
        if( timeout <= 0 ) {
            timeout = DEF_RECEIVE_TIMEOUT ;
        }
        if( pooling <= 0 ) {
            pooling = DEF_POOLING ;
        }
        else if( pooling <= MIN_POOLING ) {
            pooling = MIN_POOLING ;
        }
        else if( pooling >= MAX_POOLING ) {
            pooling = MAX_POOLING ;
        }
        this.addr = addr ;
        this.port = port ;
        
        OneUseClientConnector[] pl = new OneUseClientConnector[ pooling ] ;
        for( int i = 0 ; i < pooling ; i ++ ) {
            pl[ i ] = new OneUseClientConnector( headerBinary,addr,port,timeout ) ;
        }
        this.pool = pl ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        close() ;
    }
    
    /**
     * コネクションクローズ.
     */
    public synchronized void close() {
        if( pool != null ) {
            int len = pool.length ;
            for( int i = 0 ; i < len ; i ++ ) {
                pool[ i ].destroy() ;
            }
        }
        pool = null ;
        addr = null ;
        port = -1 ;
    }
    
    /**
     * 接続元のInetAddressを取得.
     * @return InetAddress 接続元のInetAddressが返されます.
     * @exception Exception 例外.
     */
    public synchronized InetAddress getInetAddress() throws Exception {
        return addr ;
    }
    
    /**
     * 接続元のポート番号を取得.
     * @return int 接続元のポート番号が返されます.
     * @exception Exception 例外.
     */
    public synchronized int getPort() throws Exception {
        return port ;
    }
    
    /**
     * サーバに送受信処理を行う.
     * @param binary 送信対象のバイナリを設定します.
     * @return byte[] 受信バイナリが返されます.
     * @exception Exception 例外.
     */
    public byte[] sendReceive( byte[] binary ) throws Exception {
        if( isClosed() == true ) {
            throw new IOException( "オブジェクトは既にクローズされています" ) ;
        }
        if( binary == null || binary.length <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        OneUseClientConnector cc = null ;
        for( ;; ) {
            cc = getUseConnect() ;
            if( cc == null ) {
                Thread.sleep( 50 ) ;
                continue ;
            }
            break ;
        }
        byte[] ret = null ;
        try {
            ret = cc.getConnector().sendReceive( binary ) ;
        } finally {
            cc.release() ;
        }
        return ret ;
    }
    
    /**
     * オブジェクトがクローズされているかチェック.
     * @return boolean [true]の場合、オブジェクトはクローズされています.
     */
    public synchronized boolean isClosed() {
        return ( pool == null ) ;
    }
    
    /**
     * １つの有効なコネクターを取得.
     */
    private synchronized OneUseClientConnector getUseConnect() throws Exception {
        if( isClosed() == true ) {
            throw new IOException( "オブジェクトは既にクローズされています" ) ;
        }
        int len = pool.length ;
        for( int i = 0 ; i < len ; i ++ ) {
            if( pool[ i ].isUseFlag() == false ) {
                pool[ i ].setUseFlag( true ) ;
                return pool[ i ] ;
            }
        }
        return null ;
    }
}

/**
 * 利用管理用コネクタ.
 */
class OneUseClientConnector {
    private SingleClientConnector conns = null ;
    private volatile boolean useFlag = false ;
    
    /**
     * コンストラクタ.
     */
    protected OneUseClientConnector( byte[] headerBinary,InetAddress addr,int port,int timeout )
        throws Exception {
        this.conns = new SingleClientConnector( headerBinary,addr,port,timeout ) ;
        this.useFlag = false ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        destroy() ;
    }
    
    /**
     * コネクション破棄.
     */
    protected void destroy() {
        if( conns != null ) {
            conns.close() ;
        }
        conns = null ;
        useFlag = false ;
    }
    
    /**
     * 利用フラグ設定.
     */
    protected synchronized void setUseFlag( boolean flg ) {
        useFlag = flg ;
    }
    
    /**
     * 利用フラグを取得.
     */
    protected synchronized boolean isUseFlag() {
        return useFlag ;
    }
    
    /**
     * コネクションを取得.
     */
    protected ClientConnector getConnector() {
        return conns ;
    }
    
    /**
     * コネクションをリリース.
     */
    protected void release() {
        if( isUseFlag() == true ) {
            setUseFlag( false ) ;
        }
    }
}
