/*
 * Comparison.java
 * 
 * Author: http://www.hrtdotnet.jp/
 * 
 */
package jp.hrtdotnet.java.util;

/**
 * 比較機能を盛り込んだユーティリティクラスです。
 * 
 * @version 2.0
 * @author hrtdotnet.jp
 * <br>http://www.hrtdotnet.jp/
 * @since 2.0
 */
public class Comparison {

    /** バージョン比較で完全一致を示す値です。 */
    public static final int VERSION = 0;
    /** バージョン比較で以降一致を示す値です。 */
    public static final int VERSION_SINCE = 1;
    /** バージョン比較で以前一致を示す値です。 */
    public static final int VERSION_BEFORE = 2;
    /** OS 比較で利用される Windows の識別値です。 */
    public static final int OS_WINDOWS = 0;
    /** OS 比較で利用される Linux の識別値です。 */
    public static final int OS_LINUX = 1;
    // public static final int OS_MAC = 2;

    /**
     * 指定されたバージョンの比較を行います。
     * バージョンのフォーマットはn.n.n（nは正の整数、.(ドット)は区切り文字）とします。
     * バージョンの桁数はint型の範囲であれば何桁でもかまいませんが、
     * 比較するバージョン同士が異なる桁数の場合は、
     * 桁数の大きいほうに合わせてバージョンを比較します。
     * その際、桁数の少ないバージョンの不足桁には0が代入されます。
     * <br><br>
     * バージョンの比較条件には以下の種類があります。
     * <ul>
     * <li>バージョン完全一致
     * <br>指定した２つのバージョンが完全に一致しているかどうかを比較します。</li>
     * <li>バージョン以降一致
     * <br>対象のバージョンが比較するバージョンと完全一致、
     * もしくは比較バージョンよりも上であるかどうかを比較します。</li>
     * <li>バージョン以前一致
     * <br>対象のバージョンが比較するバージョンよりも下であるかどうかを比較します。</li>
     * </ul>
     * 
     * @param version 対象となるバージョン
     * @param comparison 比較するバージョン
     * @param delim バージョンの区切り文字列
     * @param param バージョン比較条件
     * @return 比較条件を満たすバージョンであれば true、満たさなければ false
     * @throws IllegalArgumentException
     *          version, comparison, delimが空文字、バージョン比較条件が不正な場合
     * @throws NullPointerException
     *          version, comparison, delimがnullの場合
     * @throws NumberFormatException
     *          version, comparisonが区切り文字列を除いて数字以外で構成されている場合
     * @since 2.0
     */
    public static boolean version(String version, String comparison, String delim, int param) {
        if(version == null || comparison == null || delim == null){
            throw new NullPointerException("Arguments are null.");
        } else if (version.length() == 0 || comparison.length() == 0 || delim.length() == 0) {
            throw new IllegalArgumentException("Arguments are illegal-values.");        	
        }
        
        if (param != VERSION_SINCE && param != VERSION && param != VERSION_BEFORE) {
            throw new IllegalArgumentException("The param is invalid value.");
        }
        
        // バージョンの各桁ごとに格納したの配列取得
        int[] intVersion = getToIntArray(StringUtil.split(version, delim));
        int[] intComparison = getToIntArray(StringUtil.split(comparison, delim));
        
        // バージョンの最大桁数を取得
        int len = Math.max(intVersion.length, intComparison.length);

        // バージョン比較は各桁ごとに行う
        for(int i = 0; i < len; i++){
            int v = (i < intVersion.length) ? intVersion[i] : 0;
            int s = (i < intComparison.length) ? intComparison[i] : 0;            

            if (param == VERSION_SINCE) {
                if (v < s) {
                    return false;
                } else if (v > s) {
                    return true;
                }
            } else if (param == VERSION_BEFORE) {
                if (v > s) {
                    return false;
                } else if (v < s) {
                    return true;
                }
            } else if (param == VERSION) {
                if (v != s) {
                    return false;
                }            
            }
        }

        // 指定したバージョンと実際のバージョンが等しかったとき
        if(param == VERSION_SINCE || param == VERSION){
            return true;
        } else {
            return false;
        }
    }
    

    /**
     * OSの比較をします。
     * OSはSystemプロパティから取得したos.name項目を利用します。
     * <br><br>
     * 比較できる OS 以下のとおりです。
     * <ul>
     * <li>Windows</li>
     * <li>Linux</li>
     * </ul>
     * 現在の仕様ではOSのバージョンや名前までは比較できません。
     * 将来的にはOSの名前までは対応させる予定です。
     * 
     * @param osParam OS の識別値
     * @return 一致すればtrue、そうでなければfalse
     * @throws IllegalArgumentException
     *          OSの識別値が不正な場合
     * @since 2.0
     */
    public static boolean os(int osParam) {
        String osName = System.getProperty("os.name");
        osName = osName.toLowerCase();
        if (osParam == OS_WINDOWS) {
            if (osName.startsWith("windows")) {
                return true;
            }
        } else if (osParam == OS_LINUX) {
            if (osName.startsWith("linux")) {
                return true;
            }
        } else {
            throw new IllegalArgumentException("The osParam is invalid value.");            
        }
        return false;
    }
    
    // String 配列を int 配列に変換
    private static int[] getToIntArray(String[] strArray) throws NumberFormatException {
        int[] intArray = new int[strArray.length];
        for (int i = 0; i < strArray.length; i++) {
            intArray[i] = Integer.parseInt(strArray[i]);
        }
        return intArray;
    }


    /**
     * 文字列を整数に変換して大小の比較をします。
     * 大きいと予測される全半角の文字列と小さいと予測される全半角の文字列をそれぞれ整数値に変換し、
     * 大小を比較します。
     * 
     * @param big 比較する大きいと予測される数字の文字列
     * @param small 比較する小さいと予測される数字の文字列
     * @return 大きい数字 > 小さい数字 であればtrue、大きい数字 <= 小さい数字 の場合はfalse
     * @throws NumberFormatException 整数値で無い場合
     * @since 2.0
     */
    public static boolean comparisonInteger(String big, String small) {        
        // 整数チェック
        if (IntegerUtil.isInteger(big) == false || IntegerUtil.isInteger(small) == false) {
            throw new NumberFormatException("Not a integer.");
        }
        return comparisonIntegerLocal(big, small);
    }


    /**
     * 文字列を整数に変換して等しい整数かどうかを判定します。
     * 
     * @param str1 比較する文字列１
     * @param str2 比較する文字列２
     * @return ２つの文字列が整数として等しければtrue、等しくなければfalse
     * @throws NumberFotmatException 比較する文字列が整数ではない場合
     * @since 2.0
     */
    public static boolean equalsInteger(String str1, String str2) {
        // 整数チェック
        if (IntegerUtil.isInteger(str1) == false || IntegerUtil.isInteger(str2) == false) {
            throw new NumberFormatException("Not a integer.");
        }

        // シングルバイトに変換
        str1 = StringUtil.toSingleByteString(str1);
        str2 = StringUtil.toSingleByteString(str2);

        // 同値チェック
        if(str1.compareTo(str2) == 0) {
            return true;
        }        
        return false;
    }
    

    // 整数チェックを省く
    static boolean comparisonIntegerLocal(String big, String small) {
        
        // シングルバイトに変換
        big = StringUtil.toSingleByteString(big);
        small = StringUtil.toSingleByteString(small);
        
        boolean bigSign = IntegerUtil.isNumber(big);
        boolean smallSign = IntegerUtil.isNumber(small);
    
        // 符号チェック
        // 符号不一致の場合は結果は決まる
        if (bigSign && !smallSign) {
            return true;
        } else if(!bigSign && smallSign) {
            return false;
        }        
    
        String a = null;
        String b = null;
        
        if(bigSign && smallSign){
            a = big;
            b = small;
        } else {
            b = big.substring(1);
            a = small.substring(1);            
        }
    
        // サイズ比較
        if (a.length() > b.length()) {
            return true;
        } else if (a.length() < b.length()) {
            return false;
        }
    
        for (int i = 0; i < a.length(); i++) {
            if (IntegerUtil.toNumber(a.charAt(i)) < IntegerUtil.toNumber(b.charAt(i))) {
                return false;
            } else if(IntegerUtil.toNumber(a.charAt(i)) > IntegerUtil.toNumber(b.charAt(i))) {
                return true;
            }
        }
        return false;
    }
}
