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

/**
 * Integer, intに関するユーティリティクラスです。
 * 
 * @version 2.0
 * @author hrtdotnet.jp
 * <br>http://www.hrtdotnet.jp/
 * @since 2.0
 */
public class IntegerUtil {

    /**
     * ビット整数値に対して指定したビットフラグのセットの有無を判定します。
     * 
     * @param bitInteger ビット整数値
     * @param bitFlag ビットフラグ
     * @return セットされている場合はtrue、セットされていない場合はfalse
     * @since 2.0
     */
    public static boolean isBitFlag(int bitInteger, int bitFlag) {
        bitInteger = bitInteger ^ bitFlag;
        return (bitInteger & bitFlag) == 0;
    }

    /**
     * ビット整数値に対して指定したビットフラグのセット/アンセットを行います。
     * フラグをセットする場合、すでにセットされているビットフラグは何も変わりません。
     * フラグをアンセットする場合、ビットフラグがセットされていないのであれば何も変わりません。
     * 
     * @param bitInteger ビット整数値
     * @param bitFlag ビットフラグ
     * @param b セットする場合はtrue、アンセットする場合はfalse
     * @return セット/アンセットを行ったビット整数値
     * @since 2.0
     */
    public static int setBitFlag(int bitInteger, int bitFlag, boolean b) {
        bitInteger = bitInteger | bitFlag;
        if (!b) {
            bitInteger = bitInteger ^ bitFlag;
        }
        return bitInteger;
    }

    /**
     * ビット整数値に対して指定したビットフラグのセット/アンセットを反転します。
     * フラグがセットされている場合はアンセットされ、セットされていない場合はセットされます。
     * 
     * @param bitInteger ビット整数値
     * @param bitFlag ビットフラグ
     * @return セット/アンセットを行ったビット整数値
     * @since 2.0
     */
    public static int setBitFlag(int bitInteger, int bitFlag) {
        bitInteger = bitInteger ^ bitFlag;
        return bitInteger;
    }
    
    /**
     * char型文字を対応するint型の数値に変換します。
     * 指定可能な文字は数字のみです。
     * 半角・全角文字の両方に対応します。
     * 
     * @param c int型に変換するchar文字
     * @return 変換した数値値
     * @throws NumberFormatException
     *          cが数字でない場合
     * @since 2.0
     */
    public static int toNumber(char c) {
    
        // 半角に変換
        StringUtil.toSingleByteChar(c);
        
        if (c == '\u0030') return 0;
        else if (c == '\u0031') return 1;
        else if (c == '\u0032') return 2;
        else if (c == '\u0033') return 3;
        else if (c == '\u0034') return 4;
        else if (c == '\u0035') return 5;
        else if (c == '\u0036') return 6;
        else if (c == '\u0037') return 7;
        else if (c == '\u0038') return 8;
        else if (c == '\u0039') return 9;
        throw new NumberFormatException("Char is not a number.");
    }

    /**
     * 文字列をint型整数値に変換します。
     * 数字以外を指定することはできません。
     * Integer.parseIntを利用するほうが処理が早いのでそちらを推奨します。
     * 
     * @param str 整数に変換する文字列
     * @return 変換した整数値
     * @throws IllegalArgumentException
     *          strが整数でない場合
     * @since 2.0
     */
    public static int toInt(String str) {
        if (!IntegerUtil.isInt(str)) {
            throw new IllegalArgumentException("Default value is not a integer.");
        }
        return Integer.parseInt(str);
    }

    /**
     * 文字列をint型整数値に変換します。
     * 整数以外が指定された場合は0を取得します。
     * 
     * @param str 整数に変換する文字列
     * @return 変換した整数値
     * @since 2.0
     */
    public static int toIntIgnoreError(String str) {
        return IntegerUtil.toInt(str, 0);
    }

    /**
     * 文字列をint型整数値に変換します。
     * 文字列に整数以外が指定された場合はデフォルト値を取得します。
     * 
     * @param str 整数に変換する文字列
     * @param defaultValue デフォルト値、整数以外が指定された場合に返す整数
     * @return 変換した整数値
     * @since 2.0
     */
    public static int toInt(String str, int defaultValue) {
        if (!IntegerUtil.isInt(str)) {
            return defaultValue;
        }
        return toInt(str);
    }

    /**
     * 文字列をint型の自然数値に変換します。
     * このメソッドでの自然数は0以上の整数値とします。
     * 文字列に自然数以外が指定された場合はデフォルト値を取得します。
     * 
     * @param str 自然数に変換する文字列
     * @param defaultValue デフォルト値、自然数以外が指定された場合に返す自然数
     * @return 変換した自然数値
     * @throws IllegalArgumentException
     *          defaultValueが自然数以外の場合
     * @since 2.0
     */
    public static int toNatural(String str, int defaultValue) {
        if (defaultValue < 0) {
            throw new IllegalArgumentException("Default value is not a natural number.");
        }
        try {
            return IntegerUtil.toNatural(str);
        } catch (Exception e) {
        }
        return defaultValue;
    }

    /**
     * 文字列をint型の自然数値に変換します。
     * このメソッドでの自然数は0以上の整数値とします。
     * 自然数以外は指定できません。
     * 
     * @param str 自然数に変換する文字列
     * @return 変換した自然数値
     * @throws IllegalArgumentException
     *          strが自然数以外の場合
     * @since 2.0
     */
    public static int toNatural(String str) {
        if (!IntegerUtil.isNumber(str)) {
            throw new IllegalArgumentException("Argument is not a natural number.");
        }
        return Integer.parseInt(str);
    }

    /**
     * 文字列をint型の自然数値に変換します。
     * このメソッドでの自然数は0以上の整数値とします。
     * 自然数以外は指定できません。
     * 文字列に自然数以外が指定された場合は0を取得します。
     * 
     * @param str 自然数に変換する文字列
     * @return 変換した自然数値
     * @since 2.0
     */
    public static int toNaturalIgnoreError(String str)  {
        return toNatural(str, 0);
    }

    /**
     * 文字列が全半角の整数かどうか判定します。
     * 整数は全半角数字とマイナス記号（半角のみ）です。
     * ただしint型とは限りません。
     * 
     * @param str 整数の文字列
     * @return 整数であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isInteger(String str) {
        if (str == null || str.length() < 1) {
            return false;
        }
    
        char[] c = str.toCharArray();
    
        for (int i = 0; i < c.length; i++) {
            if (IntegerUtil.isNumber(c[i]) == false) {                
                if(i == 0 && c[0] == '-' && c.length > 1) continue;
                return false;
            }
        }
    
        return true;
    }

    /**
     * 文字列が半角の整数かどうか判定します。
     * 整数は半角数字とマイナス記号（半角のみ）です。
     * ただしint型とは限りません。
     * 
     * @param str 整数の文字列
     * @return 整数であればtrue、そうでなければfalse
     * @since 2.0
     */    
    public static boolean isIntegerSingleByte(String str) {
        if (str == null || str.length() < 1) {
            return false;
        }
        char[] c = str.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (IntegerUtil.isNumberSingleByte(c[i]) == false) {                
                if(i == 0 && c[0] == '-' && c.length > 1) continue;
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列が全角の整数かどうか判定します。
     * 整数は全角数字とマイナス記号（全角のみ）です。
     * ただしint型とは限りません。
     * 
     * @param str 整数の文字列
     * @return 整数であれば true、そうでなければ false
     * @since 2.0
     */    
    public static boolean isIntegerMultiByte(String str) {
        if (str == null || str.length() < 1) {
            return false;
        }
        char[] c = str.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (IntegerUtil.isNumberMultiByte(c[i]) == false) {                
                if(i == 0 && c.length > 1) {
                    if (c[0] == '\u2212' || c[0] == '\uFF0D') continue;
                }
                return false;
            }
        }
        return true;
    }

    /**
     * char文字が全半角の数字かどうか判定します。
     * 数字は半角数字[0-9]と全角数字[０-９]とします。
     * 
     * @param c 判定する文字
     * @return 数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isNumber(char c) {            
        if ((c >= '\u0030' && c <= '\u0039') || (c >= '\uFF10' && c <= '\uFF19')){
                return true;
        }
        return false;
    }

    /**
     * char文字が半角数字かどうか判定します。
     * 数字は半角数字[0-9]とします。
     * 
     * @param c 判定する文字
     * @return 数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isNumberSingleByte(char c) {            
        if (c >= '\u0030' && c <= '\u0039'){
                return true;
        }
        return false;
    }

    /**
     * char文字が数字かどうか判定します。
     * 数字が全角数字[０-９]とします。
     * 
     * @param c 判定する文字
     * @return 数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isNumberMultiByte(char c) {            
        if (c >= '\uFF10' && c <= '\uFF19'){
                return true;
        }
        return false;
    }

    /**
     * 文字列が数字のみで構成されているか判定します。
     * 数字は半角数字[0-9]と全角数字[０-９]とします。
     * また必然的に自然数かどうかの判定となります。
     * 
     * @param str 判定する文字列
     * @return 数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isNumber(String str) {        
        if (str == null || str.length() < 1){
            return false;
        }        
        for (int i = 0; i < str.length(); i++) {
            if(!isNumber(str.charAt(i))){
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列が全角数字のみで構成されているか判定します。
     * 数字は半角数字[0-9]と全角数字[０-９]とします。
     * また必然的に自然数かどうかの判定となります。
     * 
     * @param str 判定する文字列を指定します。
     * @return 数字であればtrue、そうでなければfalseを返します。
     * @since 2.0
     */
    public static boolean isNumberMultiByte(String str) {
        
        if (str == null || str.length() < 1){
            return false;
        }
        
        for (int i = 0; i < str.length(); i++) {
            if(!isNumberMultiByte(str.charAt(i))){
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列が半角数字のみで構成されているか判定します。
     * 数字は半角数字[0-9]とします。
     * また必然的に自然数かどうかの判定となります。
     * 
     * @param str 数字の文字列
     * @return 数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isNumberSingleByte(String str) {
        
        if (str == null || str.length() < 1){
            return false;
        }
        
        for (int i = 0; i < str.length(); i++) {
            if(!isNumberSingleByte(str.charAt(i))){
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列が全半角のint型の整数かどうか判定します。
     * 整数は全半角数字とマイナス記号（半角のみ）です。
     * 
     * @param str 判定する文字列
     * @return int型の数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isInt(String str) {
        if (!isInteger(str)) {
            return false;
        }
        return isIntLocal(str);
    }

    /**
     * 文字列が半角のint型の整数かどうか判定します。
     * 整数は半角数字とマイナス記号（半角のみ）です。
     * 
     * @param str 判定する文字列
     * @return int型の数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isIntSingleByte(String str) {
        if (!isIntegerSingleByte(str)) {
            return false;
        }
        return isIntLocal(str);
    }

    /**
     * 文字列が全角のint型の整数かどうか判定します。
     * 整数は全角数字とマイナス記号（全角のみ）です。
     * 
     * @param str 判定する文字列
     * @return int型の数字であればtrue、そうでなければfalse
     * @since 2.0
     */
    public static boolean isIntMultiByte(String str) {
        if (!isIntegerMultiByte(str)) {
            return false;
        }
        return isIntLocal(str);
    }

    
    // 整数チェックはしない
    private static boolean isIntLocal(String str) {
        if (!Comparison.comparisonIntegerLocal(str, Integer.toString(Integer.MIN_VALUE)) ) {
            return false;
        }
        if (!Comparison.comparisonIntegerLocal(Integer.toString(Integer.MAX_VALUE), str) ) {
            return false;
        }
        return true;
    }    
}
