package com.yanzuoguang.util.helper;


import com.yanzuoguang.util.exception.CodeException;

/**
 * 通信格式转换
 * <p>
 * Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换 高、低字节之间的转换
 * windows的字节序为低字节开头 linux,unix的字节序为高字节开头 java则无论平台变化,都是高字节开头
 * @author 颜佐光
 */
public class ByteHelper {

    /**
     * 每个字节所占用的字符串的长度
     */
    public static final int BYTE_STRING_SIZE = 2;
    /**
     * 16进制转换标识
     */
    public static final int BYTE_HEX_UNIT = 16;
    /**
     * 10进制转换单位
     */
    public static final int BYTE_TEN_UNIT = 10;
    /**
     * BCD标识
     */
    public static final int BCD_MAX_FLAG = 100;

    // --------------------------------------- toLH -------------------------------------

    /**
     * 将 long 值转成低字节在前,高字节在后的byte数组
     *
     * @param n   需要转换的值
     * @param max 字节最大长度
     * @return 转换后的数据
     */
    public static byte[] toLH(long n, int max) {
        byte[] b = new byte[max];
        for (int i = 0; i < max; i++) {
            int offset = i * 8;
            b[i] = (byte) (n >> offset & 0xff);
        }
        return b;
    }

    /**
     * 将 long 值转成低字节在前,高字节在后的byte数组
     *
     * @param n 需要转换的值
     * @return 转换后的数据
     */
    public static byte[] toLH(long n) {
        return toLH(n, 8);
    }

    /**
     * 将int转为低字节在前,高字节在后的byte数组
     *
     * @param n 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] toLH(int n) {
        return toLH(n, 4);
    }

    /**
     * 将short转为低字节在前,高字节在后的byte数组
     *
     * @param n 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] toLH(short n) {
        return toLH(n, 2);
    }

    /**
     * 将float转为低字节在前,高字节在后的byte数组
     *
     * @param f 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] toLH(float f) {
        return toLH(Float.floatToRawIntBits(f));
    }

    // --------------------------------------- toHL -------------------------------------

    /**
     * 将 long 值转成高字节在前,低字节在后的byte数组
     *
     * @param n   需要转换的值
     * @param max 字节最大长度
     * @return 转换后的数据
     */
    public static byte[] toHL(long n, int max) {
        byte[] b = new byte[max];
        for (int i = 0; i < max; i++) {
            int offset = (max - 1 - i) * 8;
            b[i] = (byte) (n >> offset & 0xff);
        }
        return b;
    }

    /**
     * 将 long 值转成高字节在前,低字节在后的byte数组
     *
     * @param n 需要转换的值
     * @return 转换后的数据
     */
    public static byte[] toHL(long n) {
        return toHL(n, 8);
    }

    /**
     * 将int转为高字节在前,低字节在后的byte数组
     *
     * @param n 需要转换的值
     * @return 转换后的数据
     */
    public static byte[] toHL(int n) {
        return toHL(n, 4);
    }

    /**
     * 将short转为高字节在前,低字节在后的byte数组
     *
     * @param n 需要转换的值
     * @return 转换后的数据
     */
    public static byte[] toHL(short n) {
        return toHL(n, 2);
    }

    /**
     * 将float转为高字节在前,低字节在后的byte数组
     *
     * @param f 需要转换的值
     * @return 转换后的数据
     */
    public static byte[] toHL(float f) {
        return toHL(Float.floatToRawIntBits(f));
    }

    // --------------------------------------- byLH -------------------------------------

    /**
     * 将低字节转换为长整形
     *
     * @param bytes 字节
     * @param off   开始位置
     * @param max   最大位置
     * @return 转换后的结果
     */
    public static long toLongByLH(byte[] bytes, int off, int max) {
        long ret = 0;
        for (int i = 0; i < max; i++) {
            int b = bytes[off + i] & 0xFF;
            int offset = i * 8;
            ret |= (b << offset);
        }
        return ret;
    }

    /**
     * 将低字节数组转换为long
     *
     * @param b 需要转换的值
     * @return 转换后的数据
     */
    public static long toLongByLH(byte[] b) {
        return toLongByLH(b, 0);
    }

    /**
     * 将低字节数组转换为long
     *
     * @param b   需要转换的值
     * @param off 偏移位置
     * @return 转换后的数据
     */
    public static long toLongByLH(byte[] b, int off) {
        return toLongByLH(b, off, 8);
    }

    /**
     * 将低字节数组转换为int
     *
     * @param b 需要转换的值
     * @return 转换后的数据
     */
    public static int toIntByLH(byte[] b) {
        return toIntByLH(b, 0);
    }

    /**
     * 将低字节数组转换为int
     *
     * @param b   需要转换的值
     * @param off 偏移位置
     * @return 转换后的数据
     */
    public static int toIntByLH(byte[] b, int off) {
        return (int) toLongByLH(b, off, 4);
    }

    /**
     * 低字节数组到short的转换
     *
     * @param b 需要转换的值
     * @return 转换后的数据
     */
    public static short toShortByLH(byte[] b) {
        return toShortByLH(b, 0);
    }

    /**
     * 低字节数组到short的转换
     *
     * @param b   需要转换的值
     * @param off 偏移位置
     * @return 转换后的数据
     */
    public static short toShortByLH(byte[] b, int off) {
        return (short) toLongByLH(b, off, 2);
    }

    /**
     * 低字节数组转换为float
     *
     * @param b 需要转换的值
     * @return 转换后的数据
     */
    public static float toFloatByLH(byte[] b) {
        return Float.intBitsToFloat(toIntByLH(b));
    }


    // --------------------------------------- byHL -------------------------------------

    /**
     * 将高字节转换为长整型
     *
     * @param bytes 字节
     * @param off   开始位置
     * @param max   最大位置
     * @return 转换后的数据
     */
    public static long toLongByHL(byte[] bytes, int off, int max) {
        long ret = 0;
        for (int i = 0; i < max; i++) {
            int b = bytes[off + i] & 0xFF;
            int offset = (max - 1 - i) * 8;
            ret |= (b << offset);
        }
        return ret;
    }

    /**
     * 将高字节数组转换为long
     *
     * @param b 需要转换的值
     * @return 转换后的数据
     */
    public static long toLongByHL(byte[] b) {
        return toLongByHL(b, 0);
    }

    /**
     * 将高字节数组转换为long
     *
     * @param b   字节
     * @param off 开始位置
     * @return 转换后的数据
     */
    public static long toLongByHL(byte[] b, int off) {
        return toLongByHL(b, off, 8);
    }


    /**
     * 将高字节数组转换为int
     *
     * @param b 字节
     * @return 转换后的数据
     */
    public static int toIntByHL(byte[] b) {
        return toIntByHL(b, 0);
    }

    /**
     * 将高字节数组转换为int
     *
     * @param b   字节
     * @param off 开始位置
     * @return 转换后的数据
     */
    public static int toIntByHL(byte[] b, int off) {
        return (int) toLongByHL(b, off, 4);
    }


    /**
     * 高字节数组到short的转换
     *
     * @param b 字节
     * @return 转换后的数据
     */
    public static short toShortByHL(byte[] b) {
        return toShortByHL(b, 0);
    }

    /**
     * 高字节数组到short的转换
     *
     * @param b   字节
     * @param off 开始位置
     * @return 转换后的数据
     */
    public static short toShortByHL(byte[] b, int off) {
        return (short) toLongByHL(b, off, 2);
    }


    /**
     * 高字节数组转换为float
     *
     * @param b 字节
     * @return 转换后的数据
     */
    public static float toFloatByHL(byte[] b) {
        int i = toIntByHL(b);
        return Float.intBitsToFloat(i);
    }

    /**
     * 获取短整形的字节,高字节在前,低字节在后的byte数组
     *
     * @param value 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] getBytes(short value) {
        return toHL(value);
    }

    /**
     * 获取短整形的字节,高字节在前,低字节在后的byte数组
     *
     * @param value 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] getBytes(int value) {
        return toHL(value);
    }

    /**
     * 获取短整形的字节,高字节在前,低字节在后的byte数组
     *
     * @param value 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] getBytes(long value) {
        return toHL(value);
    }

    /**
     * 将byte数组中的元素倒序排列
     *
     * @param b 需要转换的数据
     * @return 转换后的数据
     */
    public static byte[] bytesReverseOrder(byte[] b) {
        int length = b.length;
        byte[] result = new byte[length];
        for (int i = 0; i < length; i++) {
            result[length - i - 1] = b[i];
        }
        return result;
    }

    /**
     * 将本机的 short 转换成网络上传输支持的 short ,用于多语言通讯时处理, 将 short 类型的值转换为字节序颠倒过来对应的 short 值
     *
     * @param from 需要转换的数据
     * @return 转换后的数据
     */
    public static short reverse(short from) {
        return toShortByHL(toLH(from));
    }

    /**
     * 将本机的 int 转换成网络上传输支持的 int ,用于多语言通讯时处理, 将 int 类型的值转换为字节序颠倒过来对应的 int 值
     *
     * @param from 需要转换的数据
     * @return 转换后的数据
     */
    public static int reverse(int from) {
        return toIntByHL(toLH(from));
    }

    /**
     * 将本机的 long 转换成网络上传输支持的 long ,用于多语言通讯时处理, 将 long 类型的值转换为字节序颠倒过来对应的 long 值
     *
     * @param from 需要转换的数据
     * @return 转换后的数据
     */
    public static long reverse(long from) {
        return toLongByHL(toLH(from));
    }

    /**
     * 将本机的 float 转换成网络上传输支持的 float ,用于多语言通讯时处理, 将 float 类型的值转换为字节序颠倒过来对应的 float 值
     *
     * @param from 需要转换的数据
     * @return 转换后的数据
     */
    public static float reverse(float from) {
        return toFloatByHL(toLH(from));
    }

    /**
     * 将字节数组转换为String
     *
     * @param bytes 需要转换的字节,如 [0x00,0x01]
     * @return 返回转换成功的字符串 如: 0001
     */
    public static String toHexString(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            String hv = Integer.toHexString(b & 0xff);
            if (hv.length() < 2) {
                result.append(0);
            }
            result.append(hv);
        }
        return result.toString();
    }

    /**
     * 将字节数组转换为String
     *
     * @param bytes 需要转换的字节,如 [0x00,0x01]
     * @return 返回转换成功的字符串 如: 0001
     */
    public static String toHexString1(byte[] bytes) {
        return toHexString1(bytes, 0, 0);
    }

    /**
     * 将字节数组转换为String
     *
     * @param bytes  需要转换的字节,如 [0x00,0x01]
     * @param offset 偏移量
     * @param len    需要转换的长度,当为0时,则默认为整个字符串的长度
     * @return 返回转换成功的字符串 如: 0001
     */
    public static String toHexString1(byte[] bytes, int offset, int len) {
        if (len == 0) {
            len = bytes.length;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = offset; i < len; i++) {
            byte by = bytes[i];
            sb.append(String.format("%02X", by));
        }
        return sb.toString();
    }

    /**
     * 将字节数组转换为String
     *
     * @param from 需要转换的字节,如 0001
     * @return 返回转换成功的字符串 如: [0x00,0x01]
     */
    public static byte[] fromHexString(String from) {
        if (from == null || from.length() < 1) {
            return new byte[0];
        }
        from = from.toUpperCase().replaceAll(" ", "");
        int length = from.length() / 2;

        char[] hexChars = from.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    /**
     * convert char to byte
     *
     * @param c char
     * @return byte
     */
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }


    /**
     * 将字节数组转换为String
     *
     * @param from 需要转换的字节,如 0001
     * @return 返回转换成功的字符串 如: [0x00,0x01]
     */
    public static byte[] fromHexString1(String from) {
        byte[] bytes = new byte[from.length() / BYTE_STRING_SIZE];
        for (int i = 0; i < from.length() / BYTE_STRING_SIZE; i++) {
            int b = Integer.parseInt(from.substring(i * BYTE_STRING_SIZE, i * BYTE_STRING_SIZE + BYTE_STRING_SIZE), BYTE_HEX_UNIT);
            bytes[i] = (byte) b;
        }
        return bytes;
    }

    /**
     * 将字节转换为字节字符串。如 [0x00,0x01]转成成 00 01
     *
     * @param bytes 需要转化的数据
     * @return 转换后的数据
     */
    public static String logBytes(byte[] bytes) {
        int length = bytes.length;
        StringBuilder sb = new StringBuilder(length);
        for (byte b : bytes) {
            sb.append(b);
            sb.append(" ");
        }
        return sb.toString();
    }

    /**
     * 将整形转换为BCD值
     *
     * @param from 需要转换的整形
     * @return 转换之后的值
     */
    public static byte toBCD(int from) {
        if (from >= BCD_MAX_FLAG) {
            throw new CodeException("整形转换成字节的PCD码必须小于100");
        }
        byte bt = (byte) ((from / BYTE_TEN_UNIT * BYTE_HEX_UNIT) + from % BYTE_TEN_UNIT);
        return bt;
    }

    /**
     * 将BCD值转换为整形
     *
     * @param from 需要转换的BCD值
     * @return 转换之后的值
     */
    public static int fromBCD(byte from) {
        return (from / BYTE_HEX_UNIT) * BYTE_TEN_UNIT + from % BYTE_HEX_UNIT;
    }
}