package cc.smtweb.framework.core.util;

import org.apache.commons.jexl3.*;
import org.apache.commons.jexl3.internal.Engine;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Map;

public class MathUtil {
    //整数
    private static DecimalFormat dfLng = new DecimalFormat("##############0");
    private static DecimalFormat dfLong = new DecimalFormat("###,###,###,###,##0");
    //一位小数
    private static DecimalFormat df1 = new DecimalFormat("##############0.0");
    private static DecimalFormat df1Format = new DecimalFormat("###,###,###,###,##0.0");
    //两位小数
    private static DecimalFormat df2 = new DecimalFormat("##############0.00");
    private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00");
    //四位小数
    private static DecimalFormat df4 = new DecimalFormat("###,###,###,###,##0.0000");
    //六位小数
    private static DecimalFormat df6Number = new DecimalFormat("#######################0.000000");
    private static DecimalFormat df6NumberF = new DecimalFormat("#,###,###,###,###,###,##0.000000");

    public final static DecimalFormat stdAmountFormat = new DecimalFormat("###,###,###,###,##0.00");
    public final static DecimalFormat stdNumberFormat = new DecimalFormat("#0.00");
    public final static String DEF_NUM_TEN_THOUSAND = "10000";//万
    public static final double MAX_VALUE = 9999999999999.99D;
    public static final double MIN_VALUE = -9999999999999.99D;

    private final static BigDecimal ONE_BIG = new BigDecimal(1.00D);
    private static final String UNIT = "万仟佰拾亿仟佰拾万仟佰拾元角分";
    private static final String DIGIT = "零壹贰叁肆伍陆柒捌玖";

    /**
     * 4舍5入double，2位小数
     */
    public static double roundDouble(double src) {
        return roundDouble(src, 2);
    }

    /**
     * 4舍5入double，N 位小数
     *
     * @param src
     * @param scale 小数位数
     * @return
     */
    public static double roundDouble(Object src, int scale) {
        if (src == null) return 0.0;
        String v = src.toString();
        if (StringUtil.isEmpty(v)) return 0.0;
        if (scale < 0) scale = 2;

        BigDecimal src_b = new BigDecimal(v);
        BigDecimal src_v = src_b.divide(ONE_BIG, scale + 2, BigDecimal.ROUND_HALF_UP);// 4舍5入
        src_v = src_v.divide(ONE_BIG, scale, BigDecimal.ROUND_HALF_UP);// 4舍5入
        return src_v.doubleValue();
    }

    /**
     * 舍位处理，原生floor有坑，部分浮点数记录为.99999999999形式，导致floor结果错误
     *
     * @param d
     * @return
     */
    public static double floor(double d) {
        return Math.floor(MathUtil.roundDouble(d, 2));
    }

    /**
     * 比较两Double是否相等，将会吧他们专程BigDecimal进行比较；
     *
     * @param src double1
     * @param tag double2
     * @return src > tag 返回1, src < tag 返回-1, 否则返回0
     */
    public static int compare(double src, double tag) {
        BigDecimal src_b = new BigDecimal(src);
        BigDecimal src_v = src_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入

        BigDecimal tag_b = new BigDecimal(tag);
        BigDecimal tag_v = tag_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入

        return src_v.compareTo(tag_v);
    }

    /**
     * 自动过滤金额中的逗号转换为double,如果出错，则返回0
     *
     * @param s 源串，可能为带逗号的金额串；
     * @return double
     */
    public static Double toDouble(String s) {
        return todouble(s);
    }

    /**
     * 自动过滤金额中的逗号转换为double,如果出错，则返回0
     *
     * @param s 源串，可能为带逗号的金额串；
     * @return double
     */
    public static double todouble(String s) {
        try {
            return Double.parseDouble(s.replaceAll(",", ""));
        } catch (Exception e) {
            return 0.00;
        }
    }

    /**
     * 获取double,主要过滤d为null的情况；
     *
     * @param d Double对象；
     * @return double
     */
    public static double todouble(Double d) {
        if (d == null) return 0.0d;
        return d.doubleValue();
    }

    /**
     * 自动过滤金额中的逗号转换为float,如果出错，则返回0
     *
     * @param s 源串，可能为带逗号的金额串；
     * @return Float
     */
    public static Float toFloat(String s) {
        return tofloat(s);
    }

    /**
     * 自动过滤金额中的逗号转换为float,如果出错，则返回0
     *
     * @param s 源串，可能为带逗号的金额串；
     * @return Float
     */
    public static float tofloat(String s) {
        try {
            return Float.parseFloat(s.replaceAll(",", ""));
        } catch (Exception e) {
            return 0.0f;
        }
    }

    public static long tolong(String src, long defaultvalue) {
        try {
            return Long.parseLong(src);
        } catch (Exception e) {
            return defaultvalue;
        }
    }

    public static int toint(String src, int defaultvalue) {
        try {
            return Integer.parseInt(src);
        } catch (Exception e) {
            return defaultvalue;
        }
    }

    /**
     * 考虑使用中的精度，判断一个Value是否>0，实际是>0.00001
     *
     * @param value double类型
     * @return boolean
     */
    public static boolean isBigThanZero(double value) {
        return (value > 0.00001);
    }

    /**
     * 考虑使用中的精度，判断一个Value是否>0，实际是>0.00001
     *
     * @param value String类型
     * @return boolean
     */
    public static boolean isBigThanZero(String value) {
        return !StringUtil.isEmpty(value) && isBigThanZero(toDouble(value));
    }

    /**
     * 考虑使用中的精度，判断一个Value是否=0，实际是给出一个值范围。
     *
     * @param value double类型
     * @return boolean
     */
    public static boolean isEqualsZero(double value) {
        return (-0.00001 < value && value < 0.00001);
    }

    /**
     * 考虑使用中的精度，判断一个Value是否=0，实际是给出一个值范围。
     *
     * @param value String类型
     * @return boolean
     */
    public static boolean isEqualsZero(String value) {
        return StringUtil.isEmpty(value) || isEqualsZero(toDouble(value));
    }

    /**
     * 是否是负数
     *
     * @param db_val 要判断的double
     * @return 负数则返回true；
     */
    public static boolean isNegative(double db_val) {
        return (compare(db_val, 0.00D) == -1);
    }

    /**
     * 是否是正数
     *
     * @param db_val 要判断的double
     * @return 正数则返回true；
     */
    public static boolean isPlus(double db_val) {
        return (compare(db_val, 0.00D) == 1);
    }

    /**
     * 得到金额字符串，保持小数点2位
     *
     * @param db 将double转换为金额字符串；
     * @return 金额字符串#0.00；
     */
    public static String toStdNumberString(double db) {
        try {
            return stdNumberFormat.format(db);
        } catch (Exception e) {
            return "0.00";
        }
    }

    public static String toStdNumberStringEx(double db) {
        try {
            if (compare(db, -1d) == 0) return "-";
            return stdNumberFormat.format(db);
        } catch (Exception e) {
            return "0.00";
        }
    }

    /**
     * 将金额格式字符串，如23,333,093.01 去掉逗号
     *
     * @param s 金额串
     * @return String 去掉逗号后的串,如果amount为空,则返回0.00
     */
    public static String toStdNumberString(String s) {
        if (StringUtil.isEmpty(s))
            return "0.00";
        return stdNumberFormat.format(MathUtil.todouble(s));
    }

    /**
     * 将数据转换为两位小数的数字格式；
     *
     * @param d             数据
     * @param isZeroToEmpty 如果未0，是否返回“”；
     * @return 两位小数的字符串；
     */
    public static String toStdNumberString(double d, boolean isZeroToEmpty) {
        if (isEqualsZero(d)) {
            return isZeroToEmpty ? "" : "0.00";
        }
        return stdNumberFormat.format(d);
    }

    public static String toStdNumberString(String s, boolean isZeroToEmpty) {
        return toStdNumberString(MathUtil.todouble(s), isZeroToEmpty);
    }

    public static String toStdNumberString(double d, int scale) {
        DecimalFormat dfn = null;
        if (scale == 1) dfn = df1Format;
        if (scale == 2) dfn = df2Format;
        else if (scale == 4) dfn = df4;
        else if (scale == 6) dfn = df6NumberF;
        else if (scale <= 0) dfn = dfLong;
        else {
            StringBuilder sb = new StringBuilder("###,###,###,###,##0.");
            for (int i = 0; i < scale; i++) sb.append("0");
            dfn = new DecimalFormat(sb.toString());
        }
        return dfn.format(d);
    }

    /**
     * 将数字乘100，保留小数点后两位, 然后后面添加%
     *
     * @param d                          值
     * @param isZeroToEmpty，如果值为0，是否返回空；
     * @return 字符串；
     */
    public static String toStdPercentNumberStr(double d, boolean isZeroToEmpty) {
        if (d > -0.00000000001 && d < 0.00000000001) {
            return isZeroToEmpty ? "" : "0.00%";
        }
        return toStdNumberString(d * 100) + "%";
    }


    public static String toStdAmountString(double d) {
        return toStdAmountString(d, false);
    }

    /**
     * 将数据转换为两位小数的金额格式，带逗号；
     *
     * @param d             数据
     * @param isZeroToEmpty 如果未0，是否返回“”；
     * @return 金额格式的字符串；
     */
    public static String toStdAmountString(double d, boolean isZeroToEmpty) {
        if (isEqualsZero(d)) {
            return isZeroToEmpty ? "" : "0.00";
        }
        return stdAmountFormat.format(d);
    }

    public static String toStdAmountString(String s) {
        return toStdAmountString(MathUtil.todouble(s), false);
    }

    public static String toStdAmountString(String s, boolean isZeroToEmpty) {
        return toStdAmountString(MathUtil.todouble(s), isZeroToEmpty);
    }


    /**
     * 将小写金额转换为人民币大写金额
     *
     * @param s 金额格式的串
     * @return String 转换结果
     */
    public static String toCapsAmountString(String s) {
        if (StringUtil.isEmpty(s)) return "";
        return toCapsAmountString(todouble(s));
    }

    /**
     * 将小写金额转换为人民币大写金额
     *
     * @param v double
     * @return String 转换结果
     */
    public static String toCapsAmountString(double v) {
        if (v < MIN_VALUE || v > MAX_VALUE) return "参数非法!";

        boolean negative = isNegative(v);

        if (negative) v = Math.abs(v);
        long l = Math.round(v * 100);
        if (l == 0) return "零元整";

        String strValue = String.valueOf(l);
        // i用来控制数
        int i = 0;
        // j用来控制单位
        int j = UNIT.length() - strValue.length();
        StringBuilder rs = new StringBuilder(32);
        boolean isZero = false;
        for (; i < strValue.length(); i++, j++) {
            char ch = strValue.charAt(i);
            if (ch == '0') {
                isZero = true;
                if (UNIT.charAt(j) == '亿' || UNIT.charAt(j) == '万' || UNIT.charAt(j) == '元') {
                    rs.append(UNIT.charAt(j));
                    isZero = false;
                }
            } else {
                if (isZero) {
                    rs.append('零');
                    isZero = false;
                }
                rs.append(DIGIT.charAt(ch - '0')).append(UNIT.charAt(j));
            }
        }
        if (rs.charAt(rs.length() - 1) != '分')
            rs.append('整');

        i = rs.indexOf("亿万");
        if (i > 0) rs.delete(i + 1, i + 2);     // i+1 ->万

        if (negative)
            return rs.insert(0, '负').toString();
        else
            return rs.toString();
    }

    /**
     * 返回0 到 maxvalue的随机数
     *
     * @param maxvalue 随机数的最大值
     * @return int
     */
    public static int rnd(int maxvalue) {
        return (int) (Math.random() * (maxvalue + 1));
    }


    public static double chkDbNull(Double v) {
        return v == null ? 0 : v;
    }

    public static double max(double d1, double d2) {
        return compare(d1, d2) < 0 ? d2 : d1;
    }

    public static double min(double d1, double d2) {
        return compare(d1, d2) < 0 ? d1 : d2;
    }

    /**
     * double 去尾法
     *
     * @param src   待处理数据
     * @param scale 保留小数位数
     * @return
     */
    public static double cutDouble(double src, int scale) {
        String v = toStdNumberString(src, 6);//先到6位小数，再去计算,否则容易出错，如8.3成8.29999999999，舍位就成了8.29了
        DecimalFormat formater = new DecimalFormat();
        formater.setMaximumFractionDigits(scale);
        formater.setGroupingSize(0);
        formater.setRoundingMode(RoundingMode.FLOOR);
        return new BigDecimal(formater.format(toDouble(v))).doubleValue();
    }

    public static String getAmountStr(double amount) {
        return df2Format.format(amount);
    }

    //数字转字符串，如无小数，则去掉.00
    public static String getDoubleStr(double d) {
        return getAmountStr(d).replaceAll(",", "").replaceAll("\\.00", "");
    }

    /**
     * double 进位法
     *
     * @param src   待处理数据
     * @param scale 保留小数位数
     * @return
     */
    public static double upDouble(double src, int scale) {
        String v = toStdNumberString(src, 6);//先到6位小数，再去计算
        DecimalFormat formater = new DecimalFormat();
        formater.setMaximumFractionDigits(scale);
        formater.setGroupingSize(0);
        formater.setRoundingMode(RoundingMode.UP);
        return new BigDecimal(formater.format(toDouble(v))).doubleValue();
    }

    /**
     * 计算公式 参数以Map方式传入
     *
     * @param expr   表达式
     * @param mapVar 变量
     * @return 计算结果
     */
    public static Object calcExprMapObject(String expr, Map<String, Object> mapVar) {
        if (StringUtil.isEmpty(expr)) return "";
        JexlContext jc = new MapContext();
        jc.set("Math", Math.class);
        jc.set("UtilPub", StringUtil.class);
        for (String k : mapVar.keySet()) jc.set(k, mapVar.get(k));
        JexlExpression e = new Engine().createExpression(expr);
        return e.evaluate(jc);
    }

    /**
     * 计算公式，参数以数组方式传入，key、value交替
     *
     * @param expr 表达式
     * @param vars 变量
     * @return
     */
    public static Object calcExprObjectEx(String expr, Object... vars) {
        if (StringUtil.isEmpty(expr)) return "";
        JexlContext jc = new MapContext();
        jc.set("Math", Math.class);
        jc.set("UtilPub", StringUtil.class);
        for (int i = 0, len = vars.length; i < len; ) {
            jc.set((String) vars[i++], vars[i++]);
        }
        JexlExpression e = new Engine().createExpression(expr);
        return e.evaluate(jc);
    }

    public static double calcExprMapDouble(String expr, Map<String, Object> mapVar) {
        Object o = calcExprMapObject(expr, mapVar);
        if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString());
        return 0.0;
    }

    public static double calcExprDoubleEx(String expr, Object... vars) {
        Object o = calcExprObjectEx(expr, vars);
        if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString());
        return 0.0;
    }
}
