package org.maachang.util;

/**
 * ID発行オブジェクト.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangDao 1.00
 */
public class Sequence {

    /**
     * 取得IDなし.
     */
    public static final int NOT_ID = 0x80000000;

    /**
     * MAX-ID.
     */
    protected int maxId = 0;

    /**
     * 開始値.
     */
    protected int startId = 0;

    /**
     * 管理ID.
     */
    protected int id = 0;

    /**
     * 折り返しタイムアウト値.
     */
    protected long returnTimeout = 0L;

    /**
     * 同期オブジェクト.
     */
    protected final Object sync = new Object();

    /**
     * コンストラクタ. <BR>
     * <BR>
     * 任意の最大値を用いて情報を生成します.
     */
    public Sequence() {
        this.create();
    }

    /**
     * コンストラクタ. <BR>
     * <BR>
     * 情報を生成します. <BR>
     * 
     * @param start
     *            対象の開始値を設定します.
     * @param max
     *            管理を行うIDの幅(MAX値)を設定します.
     */
    public Sequence(int start, int max) {

        try {
            this.create(start, max);
        } catch (Exception e) {
        }

    }

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

    /**
     * 情報生成. <BR>
     * <BR>
     * 情報を生成します.
     */
    public void create() {
        try {
            this.create(0, Integer.MAX_VALUE);
        } catch (Exception e) {
        }
    }

    /**
     * 情報生成. <BR>
     * <BR>
     * 情報を生成します. <BR>
     * 
     * @param start
     *            対象の開始値を設定します.
     * @param max
     *            管理を行うIDの幅(MAX値)を設定します.
     * @exception InputException
     *                入力例外.
     */
    public void create(int start, int max) throws Exception {

        if (start < 0 || max <= 1 || start >= (max - 1)
                || max > Integer.MAX_VALUE) {
            throw new IllegalArgumentException("引数は不正です");
        }

        this.clear();

        try {
            synchronized (sync) {
                this.maxId = max;
                this.startId = start;
                this.id = start;
            }
        } catch (NullPointerException nul) {
        }
    }

    /**
     * 情報クリア. <BR>
     * <BR>
     * 情報をクリアします.
     */
    public void clear() {
        try {
            synchronized (sync) {
                this.id = this.startId;
                this.returnTimeout = System.currentTimeMillis();
            }
        } catch (NullPointerException nul) {
            this.id = this.startId;
            this.returnTimeout = System.currentTimeMillis();
        }
    }

    /**
     * 一意なID項番を取得. <BR>
     * <BR>
     * 一意なID項番を取得します. <BR>
     * 
     * @return int 一意なID項番が返されます.
     */
    public int getId() {
        int ret;

        try {
            synchronized (sync) {

                ret = this.id;

                if (ret >= this.maxId) {
                    this.returnTimeout = System.currentTimeMillis();
                    this.id = this.startId;
                } else {
                    this.id = ret + 1;
                }

            }
        } catch (NullPointerException nul) {
            ret = Sequence.NOT_ID;
        }

        return ret;
    }

    /**
     * 次に発行するID情報を設定. <BR>
     * <BR>
     * 次に発行するID情報を設定します. <BR>
     * 
     * @param id
     *            次に発行されるID位置を設定します.
     */
    public void setNextId(int id) {
        try {
            synchronized (sync) {
                this.id = (id <= this.startId || id >= this.maxId) ? ((id <= this.startId) ? this.startId
                        : this.maxId)
                        : id;
            }
        } catch (NullPointerException nul) {
        }

    }

    /**
     * 設定ID開始番号の取得. <BR>
     * <BR>
     * 設定されているID開始番号を取得します. <BR>
     * 
     * @return int 設定されているID開始番号が返されます.
     */
    public int getStartId() {
        int ret;

        try {
            synchronized (sync) {
                ret = this.startId;
            }
        } catch (NullPointerException nul) {
            ret = Sequence.NOT_ID;
        }

        return ret;
    }

    /**
     * 設定ID幅(MAX値)を取得します. <BR>
     * <BR>
     * 設定されているID幅(MAX値)を取得します. <BR>
     * 
     * @return int 設定されているID幅(MAX値)が返されます.
     */
    public int getMaxID() {
        int ret;

        try {
            synchronized (sync) {
                ret = this.maxId;
            }
        } catch (NullPointerException nul) {
            ret = Sequence.NOT_ID;
        }

        return ret;
    }

    /**
     * カウント折り返し時の時間情報を取得. <BR>
     * <BR>
     * カウントが設定ID幅(MAX値)を折り返したときの時間を取得します. <BR>
     * 
     * @return long 折り返し時間が返されます.
     */
    public long getReturnIDByTime() {
        long ret;

        try {
            synchronized (sync) {
                ret = this.returnTimeout;
            }
        } catch (NullPointerException nul) {
            ret = 0L;
        }

        return ret;
    }

}
