package org.maachang.util;

import java.util.Arrays;

/**
 * LongArrayオブジェクト. <BR>
 * <BR>
 * 数値[long(64bit)]配列を管理するオブジェクトです.<BR>
 * また、このオブジェクトは同期されていません.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangDao 1.00
 */
public class LongArray {

    /**
     * 配列縮小係数.
     */
    private static final double REDUCTION_ARRAY = 0.375;

    /**
     * 開始時配列管理数 : デフォルト.
     */
    private static final int DEFAULT_START_LENGTH = 8;

    /**
     * 開始時配列管理数 : 最大値.
     */
    private static final int MAX_START_LENGTH = Integer.MAX_VALUE;

    /**
     * 開始時配列管理数 : 最小値.
     */
    private static final int MIN_START_LENGTH = 2;

    /**
     * データ配列 : 空き情報データ.
     */
    private static final long NOT_USE = Long.MAX_VALUE;

    /**
     * 配列管理.
     */
    private long[] array = null;

    /**
     * 開始時管理配列数.
     */
    private int startLength = DEFAULT_START_LENGTH;

    /**
     * 配列管理数.
     */
    private int length = DEFAULT_START_LENGTH;

    /**
     * 現在格納配列数.
     */
    private int nowLength = 0;

    /**
     * コンストラクタ.
     */
    public LongArray() {
    }

    /**
     * コンストラクタ. <BR>
     * <BR>
     * 対象のバッファ係数を設定します. <BR>
     * 
     * @param length
     *            対象のバッファ係数を設定します.<BR>
     *            設定可能な最大値は[9999]です.<BR>
     *            設定可能な最小値は[2]です.<BR>
     *            また、これらの設定範囲外を設定した場合、 デフォルトの値[16]となります.
     */
    public LongArray(int length) {

        this.startLength = (length < MIN_START_LENGTH || length > MAX_START_LENGTH) ? DEFAULT_START_LENGTH
                : length;

        this.length = this.startLength;

    }

    /**
     * ファイナライズ処理定義. <BR>
     * <BR>
     * ファイナライズ処理定義.
     * 
     * @exception Exception
     *                例外処理が返されます.
     */
    protected final void finalize() throws Exception {

        try {
            this.clear();
        } catch (Exception t) {
        }

    }

    /**
     * 情報クリア. <BR>
     * <BR>
     * 対象の情報をクリアします.
     */
    public final void clear() {

        this.array = null;
        this.length = this.startLength;
        this.nowLength = 0;

    }

    /**
     * 情報追加. <BR>
     * <BR>
     * 対象の情報を追加します. <BR>
     * 
     * @param value
     *            設定対象の数値[Long]情報を追加します.
     * @exception Exception
     *                例外.
     */
    public final void add(Long value) throws Exception {

        if (value == null) {
            throw new Exception("引数は不正です");
        }

        this.add(value.longValue());
    }

    /**
     * 情報追加. <BR>
     * <BR>
     * 対象の情報を追加します. <BR>
     * 
     * @param value
     *            設定対象の数値[long(64bit)]情報を追加します.
     */
    public final void add(long value) {
        int length;
        int nowSize;

        long[] info = null;
        long[] tmp = null;

        info = this.array;
        length = this.length;
        nowSize = this.nowLength;

        if (info == null) {
            info = new long[length];
            Arrays.fill(info, LongArray.NOT_USE);
            info[nowSize] = value;

            this.array = info;
        } else if (info.length <= nowSize) {
            length *= 2;
            tmp = info;
            info = new long[length];
            Arrays.fill(info, LongArray.NOT_USE);
            System.arraycopy(tmp, 0, info, 0, nowSize);

            info[nowSize] = value;

            this.length = length;
            this.array = info;
        } else {
            info[nowSize] = value;
        }

        this.nowLength++;

    }

    /**
     * 情報設定. <BR>
     * <BR>
     * 対象の位置に情報をセットします. <BR>
     * 
     * @param no
     *            設定対象項番を設定します.
     * @param value
     *            設定対象数値[Long]情報を設定します.
     * @exception Exception
     *                例外.
     */
    public final void set(int no, Long value) throws Exception {

        if (value == null) {
            throw new Exception("引数は不正です");
        }

        this.set(no, value.longValue());
    }

    /**
     * 情報設定. <BR>
     * <BR>
     * 対象の位置に情報をセットします. <BR>
     * 
     * @param no
     *            設定対象項番を設定します.
     * @param value
     *            設定対象情報を設定します.
     * @exception Exception
     *                例外.
     */
    public final void set(int no, long value) throws Exception {
        long nowLen;

        nowLen = this.nowLength;

        if (no < 0 || no >= nowLen) {
            throw new Exception("引数は不正です");
        }

        this.array[no] = value;

    }

    /**
     * 情報削除. <BR>
     * <BR>
     * 対象の情報を削除します. <BR>
     * 
     * @param no
     *            削除対象の項番を設定します.
     * @return long 削除された数値[long(64bit)]情報が返されます.<BR>
     *         削除情報が存在しない場合等は、[Long.MAX_VALUE]が返されます.
     */
    public final long remove(int no) {
        int nowSize;
        int length;
        int newLength;

        long[] info = null;
        long[] tmp = null;

        long ret = Long.MAX_VALUE;

        nowSize = this.nowLength;
        length = this.length;

        if (no < 0 || no >= nowSize || nowSize == 0) {
            return Long.MAX_VALUE;
        }

        info = this.array;

        ret = info[no];
        info[no] = LongArray.NOT_USE;

        if (no == 0) {

            tmp = info;
            System.arraycopy(tmp, 1, info, 0, (nowSize - 1));

            info[nowSize - 1] = LongArray.NOT_USE;

        } else if ((nowSize - no) != 1) {

            tmp = info;
            System.arraycopy(tmp, 0, info, 0, no);
            System.arraycopy(tmp, no + 1, info, no, nowSize - (no + 1));

            info[nowSize - 1] = LongArray.NOT_USE;

        }

        nowSize--;

        if (nowSize != 0 && (length * REDUCTION_ARRAY) >= nowSize) {

            newLength = length / 2;
            tmp = new long[newLength];
            Arrays.fill(tmp, LongArray.NOT_USE);
            System.arraycopy(info, 0, tmp, 0, newLength);

            info = null;
            info = tmp;

            this.length = newLength;

        } else if (nowSize == 0) {

            info = null;

        }

        this.array = info;
        this.nowLength = nowSize;

        info = null;
        tmp = null;

        return ret;
    }

    /**
     * 情報取得. <BR>
     * <BR>
     * 対象の情報を取得します. <BR>
     * 
     * @param no
     *            取得対象の項番を設定します.
     * @return long 取得された数値[long(64bit)]情報が返されます.
     *         削除情報が存在しない場合等は、[Long.MAX_VALUE]が返されます.
     */
    public final long get(int no) {

        if (no < 0 || no >= this.nowLength) {
            return Long.MAX_VALUE;
        }

        return this.array[no];
    }

    /**
     * 対象の条件が一致する先頭の値を取得. <BR>
     * <BR>
     * 対象の条件と一致する先頭の値を取得します. また、この処理の実行前に１度ソートする必要があります. <BR>
     * 
     * @param key
     *            対象の条件を設定します.
     * @return long 結果情報が返されます.<BR>
     *         [-1]が返された場合、条件の内容は存在しません.
     */
    public final long indexOf(long key) {
        return this.indexOf(key, 0);
    }

    /**
     * 対象の条件が一致する先頭の値を取得. <BR>
     * <BR>
     * 対象の条件と一致する先頭の値を取得します. また、この処理の実行前に１度ソートする必要があります. <BR>
     * 
     * @param key
     *            対象の条件を設定します.
     * @return long 結果情報が返されます.<BR>
     *         [-1]が返された場合、条件の内容は存在しません.
     */
    public final long indexOf(long key, int index) {
        long ret;

        if (this.nowLength != 0) {
            ret = Arrays.binarySearch(this.array, key);
            ret = (ret < 0 || ret >= this.nowLength) ? -1 : ret;
        } else {
            ret = -1;
        }

        return ret;
    }

    /**
     * 対象の数値[long(64bit)]配列を取得. <BR>
     * <BR>
     * 対象の数値[long(64bit)]配列を取得します.<BR>
     * また、この数値[long(64bit)]配列は配列の再構成を行った場合、 情報の保証は行われません.<BR>
     * また、この数値[long(64bit)]群は基本的の読み込み専用で利用する ことを進めます. <BR>
     * 
     * @return long[] 対象の数値[long(64bit)]配列が返されます.
     */
    public final long[] getObjects() {
        return this.array;
    }

    /**
     * 格納情報数の取得. <BR>
     * <BR>
     * 格納されている情報数を取得します. <BR>
     * 
     * @return int 格納されている情報数が返されます.
     */
    public final int size() {
        return this.nowLength;
    }

}
