package net.argius.filetool;

import java.math.*;

/**
 * ファイルサイズ。
 */
public final class FileSize {

    /**
     * FileSizeの生成。
     */
    private FileSize() {
        // empty
    }

    /**
     * <code>ブロック</code>
     */
    public static final int BLOCK = 512;

    /**
     * <code>キロ</code>
     */
    public static final int K = 1000;

    /**
     * <code>キロバイト</code>
     */
    public static final int KB = 1024;

    /**
     * ファイルサイズ文字列をlong値に変換する。
     * <code>long</code> の上限(8EB)以上の場合はエラー。
     * @param expression 表記
     * @return ファイルサイズ
     * @throws IllegalArgumentException 表記が不適当の場合 
     */
    public static long toLong(String expression) {
        int pointCount = 0;
        int pointIndex = -1;
        for (int p = 0, n = expression.length(); p < n; p++) {
            char c = expression.charAt(p);
            if (c == '.') {
                ++pointCount;
                pointIndex = p;
                if (pointCount > 1) {
                    break;
                }
            } else if (!Character.isDigit(c)) {
                if (p == 0) {
                    throw new IllegalArgumentException("invalid expression: " + expression);
                }
                String sizeString = expression.substring(0, p);
                String unit = expression.substring(p);
                double number = Double.parseDouble(sizeString);
                boolean isBlock = unit.equalsIgnoreCase("b");
                boolean isSIUnit = !isBlock
                                   && !unit.substring(unit.length() - 1).equalsIgnoreCase("B");
                int multiplier = (isBlock) ? 0 : getMultiplier(unit);
                if (number >= Integer.MAX_VALUE || multiplier > 3) {
                    double trial;
                    if (isBlock) {
                        trial = number * BLOCK;
                    } else {
                        trial = number;
                        for (int i = 0; i < multiplier; i++) {
                            trial *= (isSIUnit) ? K : KB;
                        }
                    }
                    if (trial >= Long.MAX_VALUE) {
                        throw new IllegalArgumentException("size too large: " + trial);
                    }
                }
                if (pointCount == 0) {
                    long value = Long.parseLong(sizeString);
                    if (isBlock) {
                        return value * BLOCK;
                    }
                    for (int i = 0; i < multiplier; i++) {
                        value *= (isSIUnit) ? K : KB;
                    }
                    return value;
                } else if (pointCount == 1) {
                    BigDecimal bd = new BigDecimal(sizeString);
                    if (isBlock) {
                        return bd.multiply(BigDecimal.valueOf(BLOCK)).longValue();
                    }
                    for (int i = 0; i < multiplier; i++) {
                        bd = bd.multiply(BigDecimal.valueOf(isSIUnit ? K : KB));
                    }
                    return bd.longValue();
                }
            }
        }
        if (pointCount == 0) {
            return Long.parseLong(expression);
        } else if (pointCount == 1 && pointIndex >= 1) {
            // 小数点以下が全てゼロなら整数とみなす
            String decimal = expression.substring(pointIndex + 1);
            final int length = decimal.length();
            if (length > 0) {
                int count = 0;
                for (int i = 0; i < length; i++) {
                    if (decimal.charAt(i) == '0') {
                        ++count;
                    }
                }
                if (count == length) {
                    return Long.parseLong(expression.substring(0, pointIndex));
                }
            }
        }
        throw new IllegalArgumentException("invalid expression: " + expression);
    }

    /**
     * 乗数の取得。
     * @param unit 単位
     * @return 乗数
     */
    private static int getMultiplier(String unit) {
        final String uc = unit.toUpperCase();
        if (uc.equals("K") || uc.equals("KB")) {
            return 1;
        } else if (uc.equals("M") || uc.equals("MB")) {
            return 2;
        } else if (uc.equals("G") || uc.equals("GB")) {
            return 3;
        } else if (uc.equals("T") || uc.equals("TB")) {
            return 4;
        } else if (uc.equals("P") || uc.equals("PB")) {
            return 5;
        } else if (uc.equals("E") || uc.equals("EB")) {
            return 6;
        } else if (uc.equals("Z") || uc.equals("ZB")) {
            return 7;
        } else if (uc.equals("Y") || uc.equals("YB")) {
            return 8;
        }
        throw new IllegalArgumentException("invalid unit: " + uc);
    }

}
