package org.maachang.util;

import java.io.Serializable;

/**
 * ロールオブジェクト.
 *
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangProxy 1.00
 */
public class Roll implements Serializable {
    private static final long serialVersionUID = 8691857712645177084L;
    
    /**
     * 送信ロール配列.
     */
    private Object[] roll = null ;
    
    /**
     * ロール管理最大数.
     */
    private int max = 0 ;
    
    /**
     * 現在位置.
     */
    private int now = 0 ;
    
    /**
     * 格納数.
     */
    private int nowLen = 0 ;
    
    /**
     * コンストラクタ.
     */
    private Roll(){}
    
    /**
     * コンストラクタ.
     * <BR><BR>
     * ロール管理数を設定して生成します.
     * <BR>
     * @param max ロール管理数を設定します.<BR>
     *            設定値は[32]以下を設定した場合、その値となります.
     * @exception Exception 例外.
     */
    public Roll( int max )
        throws Exception {
        if( max <= 0 ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        int i ;
        Object[] roll = null ;
        roll = new Object[ max ] ;
        for( i = 0 ; i < max ; i ++ ){
            roll[ i ] = null ;
        }
        this.roll = roll ;
        this.max = max ;
        this.now = 0 ;
        this.nowLen = 0 ;
    }
    
    /**
     * ファイナライズ処理定義.
     * <BR><BR>
     * ファイナライズ処理定義.
     * @exception Exception 例外処理が返されます.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     * <BR><BR>
     * オブジェクトを破棄します.
     */
    public void destroy() {
        int i ;
        int len ;
        Object[] roll = null ;
        len = this.max ;
        if( len > 0 ){
            roll = this.roll ;
            for( i = 0 ; i < len ; i ++ ){
                roll[ i ] = null ;
            }
        }
        this.roll = null ;
        this.max = 0 ;
        this.now = 0 ;
        this.nowLen = 0 ;
    }
    
    /**
     * 情報追加.
     * <BR><BR>
     * 対象情報を追加します.<BR>
     * この処理は最後のロールに情報を追加します.<BR>
     * <BR>
     * @param value 対象のオブジェクトを設定します.
     * @return boolean 設定の合否が返されます.<BR>
     *                 [true]が返された場合、正しく設定されました.<BR>
     *                 [false]が返された場合、空き情報が存在しないことから、
     *                 正しく設定できませんでした.
     * @exception Exception 例外.
     */
    public final boolean add( Object value )
        throws Exception {
        return this.addTo( false,value ) ;
    }
    
    /**
     * 情報追加.
     * <BR><BR>
     * 対象情報を追加します.<BR>
     * この処理は最初のロールに情報を追加します.<BR>
     * <BR>
     * @param value 対象のオブジェクトを設定します.
     * @return boolean 設定の合否が返されます.<BR>
     *                 [true]が返された場合、正しく設定されました.<BR>
     *                 [false]が返された場合、空き情報が存在しないことから、
     *                 正しく設定できませんでした.
     * @exception Exception 例外.
     */
    public final boolean addHead( Object value )
        throws Exception {
        return this.addTo( true,value ) ;
    }
    
    /**
     * 情報取得.
     * <BR><BR>
     * 対象の情報を取得します.
     * <BR>
     * @return Object 対象の情報が返されます.<RB>
     *                    [null]が返された場合、情報は存在しません.
     */
    public final Object get() {
        int pnt ;
        Object ret = null ;
        synchronized( this ){
            if( this.nowLen <= 0 ){
                ret = null ;
            }
            else{
                if( ( pnt = this.now - this.nowLen ) < 0 ){
                    pnt = ( this.max + pnt ) ;
                }
                ret = this.roll[ pnt ] ;
                this.roll[ pnt ] = null ;
                this.nowLen -- ;
            }
        }
        return ret ;
    }
    
    /**
     * 情報参照.
     * <BR><BR>
     * 対象の情報を参照します.
     * <BR>
     * @param no 参照位置を設定します.
     * @return Object 対象の情報が返されます.<RB>
     *                    [null]が返された場合、情報は存在しません.
     */
    public final Object reference( int no ) {
        int pnt ;
        Object ret = null ;
        synchronized( this ){
            if( this.nowLen <= 0 ){
                ret = null ;
            }
            else{
                pnt = ( this.now + no ) - this.nowLen ;
                if( pnt < 0 ){
                    pnt = ( this.max + pnt ) ;
                }
                ret = this.roll[ pnt ] ;
            }
        }
        return ret ;
    }
    
    /**
     * 対象の管理サイズを取得.
     * <BR><BR>
     * 対象の管理サイズが返されます.
     * <BR>
     * @return int 管理サイズが返されます.
     */
    public final int getMax() {
        int ret ;
        synchronized( this ){
            ret = this.max ;
        }
        return ret ;
    }
    
    /**
     * 現在の格納数を取得.
     * <BR><BR>
     * 現在の格納数を取得します.
     * <BR>
     * @return int 現在の格納数が返されます.
     */
    public final int getSize() {
        int ret ;
        synchronized( this ){
            ret = this.nowLen ;
        }
        return ret ;
    }
    
    /**
     * データが追加できるかチェック.
     * <BR><BR>
     * データが追加できるかチェックします.
     * <BR>
     * @return boolean チェック結果が返されます.<BR>
     *                 [true]が返された場合、設定可能です.<BR>
     *                 [false]が返された場合、ロールは満杯のため設定はできません.
     */
    public final boolean isAdd() {
        boolean ret ;
        synchronized( this ){
            if( this.nowLen >= this.max ){
                ret = false ;
            }
            ret = true ;
        }
        return ret ;
    }
    
    /**
     * ロールデータを追加.
     */
    private final boolean addTo( boolean hlMode,Object value )
        throws Exception {
        int next ;
        boolean ret ;
        ret = false ;
        if( value == null ){
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        synchronized( this ){
            
            // モードがキャッシュ非作成モードの場合.
            // データ格納領域が存在しない場合.
            if( this.nowLen >= this.max ){
                ret = false ;
            }
            else{
                // 追加ポイントを取得.
                next = this.getAddPoint( hlMode ) ;
                // 情報をセット.
                this.roll[ next ] = null ;
                this.roll[ next ] = value ;
                ret = true ;
            }
        }
        return ret ;
    }
    
    /**
     * 追加位置を取得.
     */
    private final int getAddPoint( boolean hlMode ) {
        int ret ;
        ret = this.now ;
        // 先頭に追加する場合.
        if( hlMode == true ){
            // データが存在する場合.
            if( this.nowLen > 0 ){
                if( ( ret = this.now - this.nowLen ) < 0 ){
                    ret = ( this.max + ret ) ;
                }
                ret -- ;
                if( ret < 0 ){
                    ret = this.max - 1 ;
                }
            }
            // データが存在しない場合.
            else{
                if( ret >= this.max ){
                    ret = 0 ;
                }
                this.now = ret + 1 ;
            }
            this.nowLen ++ ;
        }
        // 最後に追加する場合.
        else{
            if( ret >= this.max ){
                ret = 0 ;
            }
            this.nowLen ++ ;
            this.now = ret + 1 ;
        }
        return ret ;
    }
}

