package org.maachang.util;

import java.util.Random;

/**
 * ランダムユーティリティ.
 * 
 * @version 2007/10/18
 * @author masahito suzuki
 * @since MaachangBase 1.00
 */
public class RandomUtil {

    /**
     * ランダム文字列出力係数.
     */
    private static final int RAND_STRING_TO_NUMBER = 0;

    private static final int RAND_STRING_TO_HALF_ASC = 1;

    private static final int RAND_STRING_TO_FULL_ASC = 2;

    /**
     * ランダム文字列出力データ幅.
     */
    private static final int RAND_STRING_TO_NUBER_BY_LENGTH = (int) ('9' - '0');

    private static final int RAND_STRING_TO_HALF_BY_LENGTH = (int) ('z' - 'a');

    private static final int RAND_STRING_TO_FULL_BY_LENGTH = (int) ('Z' - 'A');

    /**
     * 乱数オブジェクト.
     */
    private static final Random RAND = new Random(System.currentTimeMillis());

    /**
     * ランダムシートの設定. <BR>
     * <BR>
     * 新たにランダム用のシート情報を設定します.
     */
    public static final void randomize() {
        synchronized (RAND) {

            RAND.setSeed(System.currentTimeMillis());

        }
    }

    /**
     * ランダムな値を取得. <BR>
     * <BR>
     * ランダムな値を取得します. <BR>
     * 
     * @param max
     *            ランダム値の最大番号を指定します。
     * @return int 上記指定した情報のランダム値を取得します。
     */
    public static final int random(int max) {
        int ret;

        synchronized (RAND) {

            ret = (int) RAND.nextInt(max);

        }

        return ret;
    }

    /**
     * ランダムな値をバイナリで取得. <BR>
     * <BR>
     * ランダムな値をバイナリで取得します. <BR>
     * 
     * @param out
     *            ランダム値を格納するバイナリ情報を設定します.
     * @exception IllegalArgumentException
     *                入力例外.
     */
    public static final void randomBinary(byte[] out)
            throws IllegalArgumentException {
        if (out == null) {
            throw new IllegalArgumentException("引数は不正です");
        }

        synchronized (RAND) {

            RAND.nextBytes(out);

        }
    }

    /**
     * ランダムな値をバイナリで取得. <BR>
     * <BR>
     * ランダムな値をバイナリで取得します. <BR>
     * 
     * @param length
     *            生成対象のバイナリ情報長を設定します.
     * @return byte[] ランダム値が格納されたバイナリ情報が返されます.
     * @exception IllegalArgumentException
     *                入力例外.
     */
    public static final byte[] randomBinary(int length)
            throws IllegalArgumentException {
        byte[] ret = null;

        if (length <= 0) {
            throw new IllegalArgumentException("引数は不正です");
        }

        ret = new byte[length];

        synchronized (RAND) {

            RAND.nextBytes(ret);

        }

        return ret;
    }

    /**
     * ランダムな文字列を取得. <BR>
     * <BR>
     * ランダムな文字列を取得します. <BR>
     * 
     * @param size
     *            ランダムに設定する文字数を指定します.
     * @param number
     *            数字を許可するか指定します.
     * @param half
     *            半角文字を許可するか指定します.
     * @param full
     *            全角文字を許可するか指定します.
     * @return String 上記指定された内容のランダムな文字列が返されます.
     */
    public static final String randomString(int size, boolean number,
            boolean half, boolean full) {
        int i;
        int rnd = 0;
        int randSize = 0;

        int[] check = null;
        String ret = null;

        if (size <= 0 || (number == false && half == false && full == false)) {
            if (number == false && half == false && full == false) {
                number = true;
            } else {
                return null;
            }
        }

        check = new int[3];
        if (number == true) {
            check[randSize] = RAND_STRING_TO_NUMBER;
            randSize += 1;
        }
        if (half == true) {
            check[randSize] = RAND_STRING_TO_HALF_ASC;
            randSize += 1;
        }
        if (full == true) {
            check[randSize] = RAND_STRING_TO_FULL_ASC;
            randSize += 1;
        }

        StringBuilder buf = new StringBuilder(size);

        for (i = 0; i < size; i++) {

            // 乱数を発生
            rnd = random(randSize);

            // 取得した対象情報から情報を生成する
            switch (check[rnd]) {

            // 数字
            case RAND_STRING_TO_NUMBER:
                buf
                        .append((char) ('0' + (byte) (random(RAND_STRING_TO_NUBER_BY_LENGTH))));
                break;

            // 半角
            case RAND_STRING_TO_HALF_ASC:
                buf
                        .append((char) ('a' + (byte) (random(RAND_STRING_TO_HALF_BY_LENGTH))));
                break;

            case RAND_STRING_TO_FULL_ASC:
                buf
                        .append((char) ('A' + (byte) (random(RAND_STRING_TO_FULL_BY_LENGTH))));
                break;

            default:

                // 数字として出力.
                buf
                        .append((char) ('0' + (byte) (random(RAND_STRING_TO_NUBER_BY_LENGTH))));
                break;

            }
        }

        ret = buf.toString();

        buf = null;
        check = null;

        return ret;
    }

}
