package com.mvc.utils;

import com.mvc.othermodel.QuantileModel;
import io.netty.util.internal.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class NumberUtil {
    /**
     * 检查BCD字符串数据
     *
     * @param data
     * @return
     */
    public static boolean checkBCDNumber(String data) {
        try {
            Integer.parseInt(data);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 二进制字符串转十进制
     *
     * @param binarySource
     * @return
     */
    public static int binaryToDecimal(String binarySource) {
        BigInteger bi = new BigInteger(binarySource, 2);
        return Integer.parseInt(bi.toString());
    }

    /**
     * 二进制字符串转十进制
     *
     * @param binarySource
     * @return
     */
    public static String binaryStringToHexString(String binarySource) {
        BigInteger bi = new BigInteger(binarySource, 2);
        return Integer.toHexString(bi.intValue());
    }

    /**
     * 转换byte为十六进制字符串
     *
     * @param bytes
     * @return
     */
    public static String bytesToHexStr(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        if (bytes == null) {
            return null;
        }
        for (int i = 0; i < bytes.length; i++) {
            int v = 0xFF & bytes[i];
            String hv = Integer.toHexString(v);

            if (hv.length() < 2) {
                stringBuilder.append('0');
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 十六进制字符串转二进制字符串
     *
     * @param hexStr 十六进制字符串
     * @return 二进制字符串
     */
    public static String hexStringToBinaryString(String hexStr) {
        String hexToBinStr = "";
        StringBuilder stringBuilder = new StringBuilder();
        try {
            int len = hexStr.length() * 4;
            hexToBinStr = Long.toBinaryString(Long.valueOf(hexStr, 16));
            stringBuilder.append(hexToBinStr);
            int start = hexToBinStr.length();

            for (int i = start; i < len; i++) {
                stringBuilder.insert(0, '0');
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        hexToBinStr = stringBuilder.toString();
        return hexToBinStr;
    }

    /**
     * 十六进制字符串转二进制数组
     *
     * @param hexStr
     * @return
     */
    public static byte[] hexStringToBytes(String hexStr) {
        if (StringUtil.isNullOrEmpty(hexStr)) {
            return null;
        }
        int len = hexStr.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexStr.charAt(i), 16) << 4)
                    + Character.digit(hexStr.charAt(i + 1), 16));
        }
        return data;
    }

    /**
     * byte转二进制字符串
     *
     * @param b
     * @return
     */
    public static String byteToBinaryString(byte[] b) {
        String ret = "";
        for (int i = 0; i < b.length; i++) {
            String bin = Integer.toBinaryString(b[i] & 0xFF);
            bin = StringUtils.leftPad(bin, 8, "0");
            ret += bin;
        }
        return ret;
    }

    /**
     * 两个字节的方法
     *
     * @param n
     * @return
     */
    public static byte[] intTo2bytes(int n) {
        int temp1 = 0, temp2 = 0;
        byte[] hex = new byte[2];
        if (n < 256) {
            hex[1] = (byte) n;
        } else {
            temp1 = n & 0xff;
            hex[1] = (byte) temp1;
            temp2 = n >> 8;
            hex[0] = (byte) temp2;
        }
        return hex;
    }

    /**
     * 两字节Byte数组转int(大端字节序)
     *
     * @param bytes
     * @return
     */
    public static int bytes2ToIntBigEndian(byte[] bytes) {
        return bytes[1] & 0xFF | (bytes[0] & 0xFF) << 8;
    }

    /**
     * 两字节Byte数组转int(小端字节序)
     *
     * @param bytes
     * @return
     */
    public static int bytes2ToIntLittleEndian(byte[] bytes) {
        return bytes[0] & 0xFF | (bytes[1] & 0xFF) << 8;
    }

    /**
     * @函数功能: 10进制串转为BCD码
     * @输入参数: 10进制串
     * @输出结果: BCD码
     */
    public static byte[] decimalToBcdByte(String asc) {
        int len = asc.length();
        int mod = len % 2;

        if (mod != 0) {
            asc = "0" + asc;
            len = asc.length();
        }

        byte abt[] = new byte[len];
        if (len >= 2) {
            len = len / 2;
        }

        byte bbt[] = new byte[len];
        abt = asc.getBytes();
        int j, k;

        for (int p = 0; p < asc.length() / 2; p++) {
            if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
                j = abt[2 * p] - '0';
            } else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
                j = abt[2 * p] - 'a' + 0x0a;
            } else {
                j = abt[2 * p] - 'A' + 0x0a;
            }

            if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
                k = abt[2 * p + 1] - '0';
            } else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
                k = abt[2 * p + 1] - 'a' + 0x0a;
            } else {
                k = abt[2 * p + 1] - 'A' + 0x0a;
            }

            int a = (j << 4) + k;
            byte b = (byte) a;
            bbt[p] = b;
        }
        return bbt;
    }

    /**
     * char数组转byte数组
     *
     * @param chars
     * @return
     */
    public static byte[] charsToBytes(char[] chars) {
        byte[] result = new byte[chars.length];
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];
            result[i] = (byte) ch;
        }
        return result;
    }

    /**
     * byte数组转char数组
     *
     * @param bytes
     * @return
     */
    public static char[] bytesToChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);

        return cb.array();
    }

    private static String intToHex(int n) {
        StringBuffer s = new StringBuffer();
        String a;
        char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        while (n != 0) {
            s = s.append(b[n % 16]);
            n = n / 16;
        }
        a = s.reverse().toString();
        return a;
    }

    /**
     * 将16进制字符串转换为10进制字符串
     *
     * @param hex
     * @return
     */
    public static String hex2DecString(String hex) {
        return Integer.valueOf(hex, 16).toString();
    }

    //将10进制整型转为16进制字符串 使用1个字节表示
    public static String numToHex8(int b) {
        return String.format("%02x", b);// 2表示需要两个16进制数
    }

    // 将10进制整型转为16进制字符串使用2个字节表示
    public static String numToHex16(int b) {
        return String.format("%04x", b);
    }

    /**
     * 字符串是否是数值
     *
     * @param str
     * @return
     */
    public static boolean isNumberic(String str) {
        if (StringUtils.isNotEmpty(str)) {
            Pattern pattern = Pattern.compile("^[-\\=]?[\\d]*$");
            boolean flag = pattern.matcher(str).matches();
            return flag;
        } else {
            return false;
        }
    }

    /**
     * 获取Double类型指定小数位后的新数值(四舍五入)
     *
     * @param value
     * @param scale
     * @return
     */
    public static double getDoubleScale(double value, int scale) {
        BigDecimal newValue = new BigDecimal(value);
        return newValue.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 16进制转Ascii
     *
     * @param hexStr
     * @return
     */
    public static String hexToAscii(String hexStr) {
        StringBuilder output = new StringBuilder("");
        for (int i = 0; i < hexStr.length(); i += 2) {
            String str = hexStr.substring(i, i + 2);
            output.append((char) Integer.parseInt(str, 16));
        }
        return output.toString();
    }

    /**
     * 获取四分位计算结果
     *
     * @param dataList    数据列表(历史数据)
     * @return
     */
    public static QuantileModel getQuantileResult(List<BigDecimal> dataList) {
        if (dataList == null || dataList.isEmpty()) {
            return null;
        }
        try {        // 排序数据
            Collections.sort(dataList);

            String dataString = dataList.stream()
                    .map(BigDecimal::toString)
                    .collect(Collectors.joining(","));
            //System.out.println(dataString);

            // 计算箱线图的关键统计量
            int n = dataList.size();
            BigDecimal minValue = dataList.get(0);
            BigDecimal maxValue = dataList.get(n - 1);

            BigDecimal q1 = calculateQuantile(dataList, 0.25);
            BigDecimal q2 = calculateQuantile(dataList, 0.5);
            BigDecimal q3 = calculateQuantile(dataList, 0.75);
            BigDecimal iqr = q3.subtract(q1);

            // 计算异常值的界限
            BigDecimal lowerBound = q1.subtract(iqr.multiply(new BigDecimal(1.5)));
            BigDecimal upperBound = q3.add(iqr.multiply(new BigDecimal(1.5)));

            /*System.out.println("minValue:" + minValue);
            System.out.println("maxValue:" + maxValue);
            System.out.println("q1:" + q1);
            System.out.println("q2:" + q2);
            System.out.println("q3:" + q3);
            System.out.println("iqr:" + iqr);
            System.out.println("lowerBound:" + lowerBound);
            System.out.println("upperBound:" + upperBound);*/

            //特殊化处理,业务中不会出现负数
            lowerBound = lowerBound.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : lowerBound;
            //System.out.println("*lowerBound:" + lowerBound);

            QuantileModel model = new QuantileModel();
            model.setMinValue(minValue);
            model.setMaxValue(maxValue);
            model.setQ1Value(q1);
            model.setQ2Value(q2);
            model.setQ3Value(q3);
            model.setIqrValue(iqr);
            model.setLowerBound(lowerBound);
            model.setUpperBound(upperBound);

            return model;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 计算分位数
     *
     * @param data
     * @param percentile
     * @return
     */
    private static BigDecimal calculateQuantile(List<BigDecimal> data, double percentile) {
        double index = (data.size() - 1) * percentile;
        int floor = (int) Math.floor(index);
        int ceil = (int) Math.ceil(index);

        if (floor == ceil) {
            return data.get(floor);
        } else {
            BigDecimal lowerValue = data.get(floor).multiply(new BigDecimal(ceil - index)).setScale(5,RoundingMode.HALF_UP);
            BigDecimal upperValue = data.get(ceil).multiply(new BigDecimal(index - floor)).setScale(5,RoundingMode.HALF_UP);
            return lowerValue.add(upperValue);
        }
    }

    public static void main(String[] args) {
        List<BigDecimal> dataList = Arrays.asList(
                new BigDecimal(7),
                new BigDecimal(15),
                new BigDecimal(36),
                new BigDecimal(39),
                new BigDecimal(40),
                new BigDecimal(41)
        );

        // 验证一个目标值是否为异常值
        BigDecimal targetValue = new BigDecimal("69.5");
    }
}
