package org.maachang.comet.net;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * HTTPリクエスト受信用バッファ.
 * 
 * @version 2008/11/12
 * @author masahito suzuki
 * @since MaachangComet 1.29
 */
public class RequestBuffer {
    private static final Log LOG = LogFactory.getLog( RequestBuffer.class ) ;
    protected static final int MAX_LENGTH = 32 * 0x00100000 ;
    private static final int BUFFER = 8192 ;
    private byte[] binary = null ;
    private int length = 0 ;
    
    /**
     * コンストラクタ.
     */
    public RequestBuffer() {
        this.binary = new byte[ BUFFER ] ;
        this.length = 0 ;
    }
    
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    protected void destroy() {
        this.binary = null ;
        this.length = 0 ;
    }
    
    /**
     * 内容リセット.
     */
    public void reset() {
        if( this.binary.length == BUFFER ) {
            this.length = 0 ;
        }
        else {
            this.binary = new byte[ BUFFER ] ;
            this.length = 0 ;
        }
    }
    
    /**
     * 書き込み処理.
     * @param b 書き込みデータを指定します.
     * @throws Exception 例外.
     */
    public void write( int b ) throws Exception {
        if( length >= MAX_LENGTH ) {
            reset() ;
            LOG.warn( "## 受信データ長が最大値["+MAX_LENGTH+"]を越した内容を確認しました" ) ;
            throw new IOException( "受信データ長が最大値["+MAX_LENGTH+"]を越しています" ) ;
        }
        if( binary.length <= length ) {
            byte[] t = binary ;
            int iLen = t.length * 2 ;
            if( iLen >= MAX_LENGTH ) {
                iLen = MAX_LENGTH ;
            }
            binary = new byte[ iLen ] ;
            System.arraycopy( t,0,binary,0,t.length ) ;
            t = null ;
        }
        binary[ length ] = ( byte )( b & 0x000000ff ) ;
        length ++ ;
    }
    
    /**
     * 指定位置のバイト情報を取得.
     * @param no 対象の項番を設定します.
     * @return byte 対象のバイナリが返されます.
     */
    public byte getByte( int no ) {
        return binary[ no ] ;
    }
    
    /**
     * 生バイナリ情報を取得.
     * @return byte[] 生バイナリ情報が返されます.
     */
    public byte[] getBinary() {
        return binary ;
    }
    
    /**
     * 指定範囲のバイナリを取得.
     * @param length 取得対象の指定範囲を設定します.
     * @return byte[] 指定範囲のバイナリ情報が返されます.
     */
    public byte[] getBinary( int length ) {
        if( length <= 0 ) {
            length = this.length ;
        }
        if( this.length < length ) {
            length = this.length ;
        }
        byte[] ret = new byte[ length ] ;
        System.arraycopy( this.binary,0,ret,0,length ) ;
        return ret ;
    }
    
    /**
     * 指定位置から残りのバッファに対して圧縮.
     * @param pos 対象のポジションを設定します.
     * @exception Exception 例外.
     */
    public void compact( int pos ) throws Exception {
        if( pos <= -1 ) {
            throw new IllegalArgumentException( "ポジション値は負の値を設定できません" ) ;
        }
        if( pos <= 0 ) {
            return ;
        }
        int len = length - pos ;
        if( len <= -1 ) {
            throw new IllegalArgumentException( "指定ポジション値(" + pos + ")は範囲を超しています" ) ;
        }
        byte[] b = new byte[ len ] ;
        System.arraycopy( binary,pos,b,0,len ) ;
        System.arraycopy( b,0,binary,0,len ) ;
        length = len ;
        b = null ;
    }
    
    /**
     * 現在のバイナリ長を取得.
     * @return int バイナリ長が返されます.
     */
    public int getLength() {
        return length ;
    }
}
