package com.example.zxweb.utils;

import java.util.Arrays;
import java.util.List;

/**
 * 数字转汉字的原理：
 * 拆分：由于整数部分要加权值，而小数部分直接转换即可，所以首先要将数字拆分成整数+小数；
 * 整数处理：按照我们的中国人的习惯，把数字格式化成4位一组，不足4位前面补0。每次处理4位，按位匹配数组中的汉字+单位。
 * 即按照数值找数字数组（num_lower 、num_upper ）中对应位置的汉字，按照在4位中的偏移量在单位数组（unit_lower 、unit_upper ）中的汉字。
 * 比如21，转化4位为0021，前面的0不用管，2对应数字“二”，单位是“十”，1对应数字“一”，单位是“（个）”用空字符串代替。即得到“二十一”。
 * 每4位处理完后，还要整体对应一个单位，比如“万、亿、兆”等；
 * 小数处理：小数部分直接按位对应汉字数组和单位即可。
 *
 * @author wanglin
 * @version 1.0
 * @date 2022-04-08 周五
 */
@SuppressWarnings(value = "all")
public class NumberToCnUtil {
    //num 表示数字对应的中文，lower表示小写，upper表示大写
    private static final String[] num_lower = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
    private static final String[] num_upper = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

    //unit 表示单位对应的中文，lower表示小写，upper表示大写
    private static final String[] unit_lower = {"", "十", "百", "千"};
    private static final String[] unit_upper = {"", "拾", "佰", "仟"};
    private static final String[] unit_common = {"", "万", "亿", "兆", "京", "垓", "秭", "穰", "沟", "涧", "正", "载"};

    //允许的数据类型格式
    private static final List<String> promissTypes = Arrays.asList("INTEGER", "INT", "LONG", "DECIMAL", "FLOAT", "DOUBLE", "STRING", "BYTE", "TYPE", "SHORT");

    /**
     * 数字转化为小写的汉字
     *
     * @param num 将要转化的数字
     * @return
     */
    public static String toChineseLower(Object num) {
        return format(num, num_lower, unit_lower);
    }

    /**
     * 数字转化为大写的汉字
     *
     * @param num 将要转化的数字
     * @return
     */
    public static String toChineseUpper(Object num) {
        return format(num, num_upper, unit_upper);
    }

    /**
     * 格式化数字
     *
     * @param num      原数字
     * @param numArray 数字大小写数组
     * @param unit     单位权值
     * @return
     */
    private static String format(Object num, String[] numArray, String[] unit) {
        if (!promissTypes.contains(num.getClass().getSimpleName().toUpperCase())) {
            throw new RuntimeException("不支持的格式类型");
        }
        //获取整数部分
        String intnum = getInt(String.valueOf(num));
        //获取小数部分
        String decimal = getFraction(String.valueOf(num));
        //格式化整数部分
        String result = formatIntPart(intnum, numArray, unit);
        if (!"".equals(decimal)) {//小数部分不为空
            //格式化小数
            result += "点" + formatFractionalPart(decimal, numArray);
        }
        return result;
    }

    /**
     * 格式化整数部分
     *
     * @param num      整数部分
     * @param numArray 数字大小写数组
     * @return
     */
    private static String formatIntPart(String num, String[] numArray, String[] unit) {

        //按4位分割成不同的组（不足四位的前面补0）
        Integer[] intnums = split2IntArray(num);

        boolean zero = false;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < intnums.length; i++) {
            //格式化当前4位
            String r = formatInt(intnums[i], numArray, unit);
            if ("".equals(r)) {//
                if ((i + 1) == intnums.length) {
                    sb.append(numArray[0]);//结果中追加“零”
                } else {
                    zero = true;
                }
            } else {//当前4位格式化结果不为空（即不为0）
                if (zero || (i > 0 && intnums[i] < 1000)) {//如果前4位为0，当前4位不为0
                    sb.append(numArray[0]);//结果中追加“零”
                }
                sb.append(r);
                sb.append(unit_common[intnums.length - 1 - i]);//在结果中添加权值
                zero = false;
            }
        }
        return sb.toString();
    }

    /**
     * 格式化小数部分
     *
     * @param decimal  小数部分
     * @param numArray 数字大小写数组
     * @return
     */
    private static String formatFractionalPart(String decimal, String[] numArray) {
        char[] val = String.valueOf(decimal).toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : val) {
            int n = Integer.parseInt(c + "");
            sb.append(numArray[n]);
        }
        return sb.toString();
    }

    //拆分整数和小数的方法在下面-----------------

    /**
     * 获取整数部分
     *
     * @param num
     * @return
     */
    private static String getInt(String num) {
        //检查格式
        checkNum(num);

        char[] val = String.valueOf(num).toCharArray();
        StringBuilder sb = new StringBuilder();
        int t, s = 0;
        for (char c : val) {
            if (c == '.') {
                break;
            }
            t = Integer.parseInt(c + "", 16);
            if (s + t == 0) {
                continue;
            }
            sb.append(t);
            s += t;
        }
        return (sb.length() == 0 ? "0" : sb.toString());
    }

    /**
     * 获取小数部分
     *
     * @param num
     * @return
     */
    private static String getFraction(String num) {
        int i = num.lastIndexOf(".");
        if (num.indexOf(".") != i) {
            throw new RuntimeException("数字格式不正确，最多只能有一位小数点！");
        }
        String fraction = "";
        if (i >= 0) {
            fraction = getInt(new StringBuffer(num).reverse().toString());
            if ("0".equals(fraction)) {
                return "";
            }
        }
        return new StringBuffer(fraction).reverse().toString();
    }

    /**
     * 检查数字格式
     *
     * @param num
     */
    private static void checkNum(String num) {
        if (num.indexOf(".") != num.lastIndexOf(".")) {
            throw new RuntimeException("数字[" + num + "]格式不正确!");
        }
        if (num.indexOf("-") != num.lastIndexOf("-") || num.lastIndexOf("-") > 0) {
            throw new RuntimeException("数字[" + num + "]格式不正确！");
        }
        if (num.indexOf("+") != num.lastIndexOf("+")) {
            throw new RuntimeException("数字[" + num + "]格式不正确！");
        }
        if (num.indexOf("+") != num.lastIndexOf("+")) {
            throw new RuntimeException("数字[" + num + "]格式不正确！");
        }
        if (num.replaceAll("[\\d|\\.|\\-|\\+]", "").length() > 0) {
            throw new RuntimeException("数字[" + num + "]格式不正确！");
        }
    }


    /**
     * 分割数字，每4位一组
     *
     * @param num
     * @return
     */
    private static Integer[] split2IntArray(String num) {
        String prev = num.substring(0, num.length() % 4);
        String stuff = num.substring(num.length() % 4);
        if (!"".equals(prev)) {
            num = String.format("%04d", Integer.valueOf(prev)) + stuff;
        }
        Integer[] ints = new Integer[num.length() / 4];
        int idx = 0;
        for (int i = 0; i < num.length(); i += 4) {
            String n = num.substring(i, i + 4);
            ints[idx++] = Integer.valueOf(n);
        }
        return ints;
    }


    /**
     * 格式化4位整数
     *
     * @param num
     * @param numArray
     * @return
     */
    private static String formatInt(int num, String[] numArray, String[] unit) {
        char[] val = String.valueOf(num).toCharArray();
        int len = val.length;
        StringBuilder sb = new StringBuilder();
        boolean isZero = false;
        for (int i = 0; i < len; i++) {
            //获取当前位的数值
            int n = Integer.parseInt(val[i] + "");
            if (n == 0) {
                isZero = true;
            } else {
                if (isZero) {
                    sb.append(numArray[Integer.parseInt(val[i - 1] + "")]);
                }
                sb.append(numArray[n]);
                sb.append(unit[(len - 1) - i]);
                isZero = false;
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        short s = 10;
        byte b = 10;
        char c = 'A';
        Object[] nums = {s, b, c, 0, 1001, 100100001L, 21., 205.23F, 205.23D, "01000010", "1000000100105.0123", ".142", "20.00", "1..2", true};
        System.out.println("将任意数字转化为汉字(包括整数、小数以及各种类型的数字)");
        System.out.println("--------------------------------------------");
        for (Object num : nums) {
            try {
                System.out.print("[" + num.getClass().getSimpleName() + "]" + num);
                for (int i = 0; i < 25 - (num + num.getClass().getSimpleName()).length(); i += 4) {
                    System.out.print("\t");
                }
                //调用转化为小写和大写
                System.out.print(" format:" + toChineseLower(num));
                System.out.println("【" + toChineseUpper(num) + "】");
            } catch (Exception e) {
                System.out.println(" 错误信息：" + e.getMessage());
            }
        }

        //测试:6--->>六
        System.out.println("-----------------------------");
        Integer a = 6;
        System.out.println("6转为：" + toChineseLower(a));
    }
}


