package cc.smtweb.framework.core.util;


import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 个人身份证帮助类
 *
 * @author : AKzz
 * @version : $Revision:$
 */
public final class PersonUIDUtil {
    private final static String uid_chars = "0123456789XZ";
    private static Map<String, String> map_uid_chars;

    static {
        map_uid_chars = new HashMap<>();
        map_uid_chars.put("０", "0");
        map_uid_chars.put("１", "1");
        map_uid_chars.put("２", "2");
        map_uid_chars.put("３", "3");
        map_uid_chars.put("４", "4");
        map_uid_chars.put("５", "5");
        map_uid_chars.put("６", "6");
        map_uid_chars.put("７", "7");
        map_uid_chars.put("８", "8");
        map_uid_chars.put("９", "9");
        map_uid_chars.put("Ｘ", "X");
        map_uid_chars.put("ｘ", "X");
        map_uid_chars.put("Ｚ", "Z");
        map_uid_chars.put("ｚ", "Z");
    }

    /*
     大陆的身份证为18位，老的身份证是15位。

     地址码（身份证前六位）第一、第二数字表示公民所在地的省份（或自治区、直辖市）；
                          第三、第四位数字表示公民 所在地的市（或州）；
                          第五、第六位数字表示公民所在地的县（或县级市）；
     生日期码（身份证第七位到第十四位）接下来的8位数字表示公民的出生年、月、日；
     顺序码（身份证第十五位到十七位）为同一地址码所标识的区域范围内，对同年、月、日出生的人员编定的顺序号
     第17位:奇数为男性，偶数为女性)
     第十八位数字的计算方法为：
     1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为：7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
     2.将这17位数字和系数相乘的结果相加。
     3.用加出来和除以11，看余数是多少？
     4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'
     */
    public final static int NEW_CARD_NUMBER_LENGTH = 18;
    public final static int OLD_CARD_NUMBER_LENGTH = 15;
    // 18位身份证中，各个数字的生成校验码时的权值
    private final static int[] VERIFY_CODE_WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    // 18位身份证中最后一位校验码
    private final static char[] VERIFY_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

    public static boolean checkPersonUID(String uid) {
        return checkPersonUIDA(uid, false);
    }

    /**
     * 检查身份证是否合法.
     */
    public static boolean checkPersonUIDA(String uid, boolean include15) {
        String uid_15_ex = "^\\d{6}\\d{2}(0[1-9]|10|11|12)(0[1-9]|1[0-9]|2[0-9]|3[0-1])\\d{3}$";
        String uid_18_ex = "^\\d{6}(19|20)\\d{2}(0[1-9]|10|11|12)(0[1-9]|1[0-9]|2[0-9]|3[0-1])\\d{3}[0-9X]{1}$";
        String regx, bdate;
        if (include15 && uid.length() == 15) {
            regx = uid_15_ex; // 15位 地址码 + (年+月+日) + 顺序号
            bdate = "19" + uid.substring(6, 12);
        } else if (uid.length() == NEW_CARD_NUMBER_LENGTH) {
            regx = uid_18_ex; // 18位 身份证
            bdate = uid.substring(6, 14);
        } else {
            return false;
        }
        if (!DateUtil.isDateFormat(bdate, "YYYYMMDD")) return false;
        Pattern pattern = Pattern.compile(regx);
        boolean ret = pattern.matcher(uid).matches();
        if (ret && uid.length() == NEW_CARD_NUMBER_LENGTH) {
            return calculateVerifyCode(uid) == uid.charAt(NEW_CARD_NUMBER_LENGTH - 1);
        }
        return ret;
    }

    /**
     * 检查身份证是否合法
     *
     * @param uid 要检查的身份证号；
     * @return 对于可以转换的自动验证转换为正确的身份证号，如18位51012719731204001X，则能自动转换为正确的，15位自动转换为18位，返回的message中。
     */
    public static boolean checkPersonUIDEx(String uid) {
        return checkPersonUIDExA(uid, false);
    }

    public static boolean checkPersonUIDExA(String uid, boolean include15) {
        String uid_15_ex = "^\\d{6}\\d{2}(0[1-9]|10|11|12)(0[1-9]|1[0-9]|2[0-9]|3[0-1])\\d{3}$";
        String uid_18_ex = "^\\d{6}(19|20)\\d{2}(0[1-9]|10|11|12)(0[1-9]|1[0-9]|2[0-9]|3[0-1])\\d{3}[0-9A-Z]{1}$";
        String regx, bdate;   //11011720031031501Z

        if (include15 && uid.length() == 15) {
            regx = uid_15_ex; // 15位 地址码 + (年+月+日) + 顺序号
            bdate = "19" + uid.substring(6, 12);
        } else if (uid.length() == NEW_CARD_NUMBER_LENGTH) {
            regx = uid_18_ex; // 18位 身份证
            bdate = uid.substring(6, 14);
        } else {
            return false;
        }
        if (!DateUtil.isDateFormat(bdate, "YYYYMMDD")) {
            return false;
        }
        Pattern pattern = Pattern.compile(regx);
        boolean ret = pattern.matcher(uid).matches();
        if (!ret) {
            return false;
        }

        if (uid.length() == NEW_CARD_NUMBER_LENGTH) {//18位
            char c = calculateVerifyCode(uid);
            if (c == uid.charAt(NEW_CARD_NUMBER_LENGTH - 1))
                return true;//正确
            return false;//错误
        } else {//15位则换算成18位
            return true;
        }
    }

    /**
     * 校验码（第十八位数）：
     * 十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0...16 ，先对前17位数字的权求和；
     * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4
     * 2;
     * 计算模 Y = mod(S, 11)<
     * 通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2
     */
    public static char calculateVerifyCode(CharSequence uid) {
        int sum = 0;
        for (int i = 0; i < NEW_CARD_NUMBER_LENGTH - 1; i++) {
            char ch = uid.charAt(i);
            sum += (ch - '0') * VERIFY_CODE_WEIGHT[i];
        }
        return VERIFY_CODE[sum % 11];
    }

    /**
     * 获取身份证的第17位，奇数为男性，偶数为女性
     *
     * @param uid 身份证号；
     * @return 0: 女； 1.男；
     */
    public static int getPersonSex(CharSequence uid) {
        char genderCode;
        if (uid.length() == NEW_CARD_NUMBER_LENGTH)
            genderCode = uid.charAt(NEW_CARD_NUMBER_LENGTH - 2);
        /*else if (uid.length() == OLD_CARD_NUMBER_LENGTH)
            genderCode = uid.charAt(OLD_CARD_NUMBER_LENGTH - 1);*/
        else
            return -1;//throw new IllegalArgumentException("非法的居民身份证号[" + uid + "]!");
        return (genderCode - '0' & 0x1) == 0x01 ? 1: 0;
    }

    /**
     * 从身份证中取得出身日期
     */
    public static String getBirthday(CharSequence uid) {
        StringBuilder birthday = new StringBuilder(10);
        /*if (uid.length() == OLD_CARD_NUMBER_LENGTH) {
            birthday.append("19").append(uid.subSequence(6, 8)).append('-');
            birthday.append(uid.subSequence(8, 10)).append('-');
            birthday.append(uid.subSequence(10, 12));
        } else */
        if (uid.length() == NEW_CARD_NUMBER_LENGTH) {
            birthday.append(uid.subSequence(6, 10)).append('-');
            birthday.append(uid.subSequence(10, 12)).append('-');
            birthday.append(uid.subSequence(12, 14));
        } else {
            throw new IllegalArgumentException("非法的居民身份证号[" + uid + "]!");
        }
        return birthday.toString();
    }

    /**
     * 把15位身份证号码转换到18位身份证号码<br>
     * 15位身份证号码与18位身份证号码的区别为：<br>
     * 1、15位身份证号码中，"出生年份"字段是2位，转换时需要补入"19"，表示20世纪<br>
     * 2、15位身份证无最后一位校验码。18位身份证中，校验码根据根据前17位生成
     */
    public static String contertToNewCardNumber(String oldUID) {
        if (oldUID.length() != 15) {
            char c = calculateVerifyCode(oldUID);
            return oldUID.substring(0, NEW_CARD_NUMBER_LENGTH - 1) + c;
        }

        StringBuilder buf = new StringBuilder(NEW_CARD_NUMBER_LENGTH);
        buf.append(oldUID.substring(0, 6));
        buf.append("19");
        buf.append(oldUID.substring(6));
        buf.append(calculateVerifyCode(buf));
        return buf.toString().toUpperCase();
    }

    /**
     * 根据身份证和传入的年月，计算人员年龄；
     *
     * @param person_uid 身份证号
     * @param curYear    年度
     * @param curMonth   月份
     */
    public static int getPersonAge(String person_uid, int curYear, int curMonth) {
        String birth_date = getBirthday(person_uid);
        int year = Integer.parseInt(birth_date.substring(0, 4));
        int month = Integer.parseInt(birth_date.substring(5, 7));
        int age = curYear - year;
        if (curMonth < month)
            age--;
        return age;
    }

    public static int getPersonAge(String person_uid, String period) {
        int y = Integer.parseInt(period.substring(0, 4));
        int m = Integer.parseInt(period.substring(4));
        return getPersonAge(person_uid, y, m);
    }

    /**
     * 根据身份证和传入的年月，计算人员年龄；
     *
     * @param person_uid 身份证号
     * @param nowDate    比较日期 2017-01-01
     */
    public static int getPersonAgeEx(String person_uid, String nowDate, int ageType) {
        int y = Integer.parseInt(nowDate.substring(0, 4));
        int m = Integer.parseInt(nowDate.substring(5, 7));
        int d = Integer.parseInt(nowDate.substring(8, 10));

        String birth_date = getBirthday(person_uid);
        int year = Integer.parseInt(birth_date.substring(0, 4));
        int month = Integer.parseInt(birth_date.substring(5, 7));
        int day = Integer.parseInt(birth_date.substring(8, 10));

        int age = y - year;
        if (ageType == 0) {
            if (m < month) age--;
        } else if (ageType == 4) {
            if (m < month || (m == month && d < day)) age--;
            else age++;
        } else if (ageType == 2) {
            age++;
        } else if (ageType == 3) {
            age--;
        }
        return age;
    }

    public static int getPersonAge(String person_uid, String period, int ageType) {
        int y = Integer.parseInt(period.substring(0, 4));
        int m = Integer.parseInt(period.substring(4));

        String birth_date = getBirthday(person_uid);
        int year = Integer.parseInt(birth_date.substring(0, 4));
        int month = Integer.parseInt(birth_date.substring(5, 7));

        int age = y - year;
        if (ageType == 0) {
            if (m < month) age--;
        } else if (ageType == 2) {
            age++;
        } else if (ageType == 3) {
            age--;
        }
        return age;
    }

    public static int getPersonAge(String person_uid, int year, int month, int ageType) {

        String birth_date = getBirthday(person_uid);
        int y = Integer.parseInt(birth_date.substring(0, 4));
        int m = Integer.parseInt(birth_date.substring(5, 7));

        int age = year - y;
        if (ageType == 0) {
            if (month < m) age--;
        } else if (ageType == 2) {
            age++;
        } else if (ageType == 3) {
            age--;
        }
        return age;
    }

    //计算年龄，按年算
    public static int getPersonAgeYear(String person_uid, int curYear) {
        String birth_date = getBirthday(person_uid);
        int year = Integer.parseInt(birth_date.substring(0, 4));
        return curYear - year;
    }

    public static int getPersonAgeYear(String person_uid, String period) {
        int y = Integer.parseInt(period.substring(0, 4));
        return getPersonAgeYear(person_uid, y);
    }

    /**
     * 根据身份证，根据当前年月，计算人员年龄；
     *
     * @param person_uid 身份证号
     */
    public static int getPersonAge(String person_uid) {
        String birth_date = getBirthday(person_uid);
        int year = Integer.parseInt(birth_date.substring(0, 4));
        int month = Integer.parseInt(birth_date.substring(5, 7));
        int age = Integer.parseInt(DateUtil.nowYear()) - year;
        if (Integer.parseInt(DateUtil.nowMonth()) < month)
            age--;
        return age;
    }

    //去掉非法字符
    public static String cleanPersonUid(String personUid) {
        if (personUid == null) return "";
        personUid = personUid.trim().toUpperCase();
        StringBuilder sb = new StringBuilder(personUid.length());
        for (int len = personUid.length(), i = 0; i < len; i++) {
            char c = personUid.charAt(i);
            if (c == 'X' || (c >= '0' && c <= '9')) sb.append(c);
        }
        return sb.toString();
    }

    //去掉非法字符，含z
    public static String cleanPersonUidEx(String personUid) {
        if (personUid == null) return "";
        personUid = personUid.trim().toUpperCase();
        StringBuilder sb = new StringBuilder(18);
        String s;
        for (int len = personUid.length(), i = 0; i < len; i++) {
            char c = personUid.charAt(i);
            if (uid_chars.indexOf(c) >= 0) sb.append(c);
            else {
                s = String.valueOf(c);
                if (map_uid_chars.containsKey(s)) sb.append(map_uid_chars.get(s));
            }
        }
        return sb.toString();
    }


    /**
     * 显示uid的时候保密，用一些**符号
     *
     * @param person_uid
     * @return
     */
    public static String secretPersonUID(String person_uid) {
        return secretPersonUID(person_uid, 8);
    }

    public static String secretPersonUID(String person_uid, int secretLength) {
        if (person_uid.length() > 14) {
            String head = person_uid.substring(0, 6);
            String tail = person_uid.substring(14, person_uid.length());
            StringBuilder sb = new StringBuilder();
            sb.append(head);
            int i = 0;
            while (i < secretLength) {
                sb.append("*");
                i++;
            }
            sb.append(tail);
            return sb.toString();
        } else return person_uid;
    }

    public static void main(String[] args) {
    }
}
