/*
 * 文 件 名:  ByteUtils
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  字节处理工具类
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/22
 */
package com.ruoyi.ems.charging.utils;

import io.netty.buffer.ByteBuf;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;

/**
 * 字节处理工具类
 * 处理BCD码、BIN码(低位在前高位在后)、ASCII码转换
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/22]
 */
public final class ByteUtils {

    private ByteUtils() {
        // 私有构造函数，防止实例化
    }

    // ==================== BCD码处理 ====================

    /**
     * 字符串转BCD码(右对齐，左补0)
     * 例如: "32010600019236" -> [0x32, 0x01, 0x06, 0x00, 0x01, 0x92, 0x36]
     *
     * @param str    字符串
     * @param length 目标字节长度
     * @return BCD码字节数组
     */
    public static byte[] strToBcd(String str, int length) {
        if (str == null) {
            str = "";
        }
        // 确保字符串长度为偶数
        int targetLen = length * 2;
        String padded = String.format("%" + targetLen + "s", str).replace(' ', '0');
        if (padded.length() > targetLen) {
            padded = padded.substring(padded.length() - targetLen);
        }

        byte[] result = new byte[length];
        for (int i = 0; i < length; i++) {
            int high = Character.digit(padded.charAt(i * 2), 16);
            int low = Character.digit(padded.charAt(i * 2 + 1), 16);
            result[i] = (byte) ((high << 4) | low);
        }
        return result;
    }

    /**
     * BCD码转字符串
     * 例如: [0x32, 0x01, 0x06] -> "320106"
     *
     * @param bcd BCD码字节数组
     * @return 字符串
     */
    public static String bcdToStr(byte[] bcd) {
        if (bcd == null || bcd.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bcd) {
            sb.append(String.format("%02X", b & 0xFF));
        }
        return sb.toString();
    }

    /**
     * 从ByteBuf读取BCD码并转为字符串
     *
     * @param buf    ByteBuf
     * @param length 读取字节长度
     * @return 字符串
     */
    public static String readBcd(ByteBuf buf, int length) {
        byte[] bcd = new byte[length];
        buf.readBytes(bcd);
        return bcdToStr(bcd);
    }

    /**
     * 将BCD字符串写入ByteBuf
     *
     * @param buf    ByteBuf
     * @param str    字符串
     * @param length 写入字节长度
     */
    public static void writeBcd(ByteBuf buf, String str, int length) {
        byte[] bcd = strToBcd(str, length);
        buf.writeBytes(bcd);
    }

    // ==================== BIN码处理(低位在前，高位在后) ====================

    /**
     * 从ByteBuf读取无符号字节
     *
     * @param buf ByteBuf
     * @return 无符号值
     */
    public static int readUnsignedByte(ByteBuf buf) {
        return buf.readByte() & 0xFF;
    }

    /**
     * 从ByteBuf读取2字节无符号整数(低位在前)
     *
     * @param buf ByteBuf
     * @return 无符号值
     */
    public static int readUnsignedShortLE(ByteBuf buf) {
        return buf.readShortLE() & 0xFFFF;
    }

    /**
     * 从ByteBuf读取4字节无符号整数(低位在前)
     *
     * @param buf ByteBuf
     * @return 无符号值
     */
    public static long readUnsignedIntLE(ByteBuf buf) {
        return buf.readIntLE() & 0xFFFFFFFFL;
    }

    /**
     * 从ByteBuf读取5字节无符号整数(低位在前)
     *
     * @param buf ByteBuf
     * @return 无符号值
     */
    public static long readUnsignedInt5LE(ByteBuf buf) {
        long low = buf.readIntLE() & 0xFFFFFFFFL;
        long high = buf.readByte() & 0xFF;
        return (high << 32) | low;
    }

    /**
     * 写入1字节无符号整数
     *
     * @param buf   ByteBuf
     * @param value 值
     */
    public static void writeUnsignedByte(ByteBuf buf, int value) {
        buf.writeByte(value & 0xFF);
    }

    /**
     * 写入2字节无符号整数(低位在前)
     *
     * @param buf   ByteBuf
     * @param value 值
     */
    public static void writeUnsignedShortLE(ByteBuf buf, int value) {
        buf.writeShortLE(value & 0xFFFF);
    }

    /**
     * 写入4字节无符号整数(低位在前)
     *
     * @param buf   ByteBuf
     * @param value 值
     */
    public static void writeUnsignedIntLE(ByteBuf buf, long value) {
        buf.writeIntLE((int) (value & 0xFFFFFFFFL));
    }

    /**
     * 写入5字节无符号整数(低位在前)
     *
     * @param buf   ByteBuf
     * @param value 值
     */
    public static void writeUnsignedInt5LE(ByteBuf buf, long value) {
        buf.writeIntLE((int) (value & 0xFFFFFFFFL));
        buf.writeByte((int) ((value >> 32) & 0xFF));
    }

    // ==================== ASCII码处理 ====================

    /**
     * 从ByteBuf读取ASCII字符串
     *
     * @param buf    ByteBuf
     * @param length 读取字节长度
     * @return 字符串(去除末尾的0)
     */
    public static String readAscii(ByteBuf buf, int length) {
        byte[] bytes = new byte[length];
        buf.readBytes(bytes);
        // 找到第一个0的位置
        int end = 0;
        while (end < bytes.length && bytes[end] != 0) {
            end++;
        }
        return new String(bytes, 0, end, StandardCharsets.US_ASCII);
    }

    /**
     * 将ASCII字符串写入ByteBuf(不足补0)
     *
     * @param buf    ByteBuf
     * @param str    字符串
     * @param length 写入字节长度
     */
    public static void writeAscii(ByteBuf buf, String str, int length) {
        byte[] bytes = new byte[length];
        if (str != null) {
            byte[] strBytes = str.getBytes(StandardCharsets.US_ASCII);
            System.arraycopy(strBytes, 0, bytes, 0, Math.min(strBytes.length, length));
        }
        buf.writeBytes(bytes);
    }

    // ==================== CP56Time2a时间格式处理 ====================

    /**
     * 从ByteBuf读取CP56Time2a格式时间
     * 格式:
     * Byte1-2: 毫秒 (0-59999)
     * Byte3: 分 (0-59) + IV标志(bit7)
     * Byte4: 时 (0-23) + SU标志(bit7)
     * Byte5: 星期(高3位) + 日(低5位)
     * Byte6: 月 (1-12)
     * Byte7: 年 (0-99, 从2000年开始)
     *
     * @param buf ByteBuf
     * @return LocalDateTime
     */
    public static LocalDateTime readCp56Time2a(ByteBuf buf) {
        int milliseconds = buf.readShortLE() & 0xFFFF;
        int minutes = buf.readByte() & 0x3F;
        int hours = buf.readByte() & 0x1F;
        int dayAndWeek = buf.readByte() & 0xFF;
        int day = dayAndWeek & 0x1F;
        int month = buf.readByte() & 0x0F;
        int year = (buf.readByte() & 0x7F) + 2000;

        int seconds = milliseconds / 1000;
        int millis = milliseconds % 1000;

        try {
            return LocalDateTime.of(year, month, day, hours, minutes, seconds, millis * 1000000);
        }
        catch (Exception e) {
            return LocalDateTime.now();
        }
    }

    /**
     * 将LocalDateTime写入ByteBuf(CP56Time2a格式)
     *
     * @param buf      ByteBuf
     * @param dateTime 时间
     */
    public static void writeCp56Time2a(ByteBuf buf, LocalDateTime dateTime) {
        if (dateTime == null) {
            dateTime = LocalDateTime.now();
        }

        int milliseconds = dateTime.getSecond() * 1000 + dateTime.getNano() / 1000000;
        buf.writeShortLE(milliseconds);
        buf.writeByte(dateTime.getMinute());
        buf.writeByte(dateTime.getHour());
        int dayOfWeek = dateTime.getDayOfWeek().getValue() % 7; // 0=周日, 1-6=周一到周六
        buf.writeByte((dayOfWeek << 5) | dateTime.getDayOfMonth());
        buf.writeByte(dateTime.getMonthValue());
        buf.writeByte(dateTime.getYear() - 2000);
    }

    // ==================== 数据转换辅助方法 ====================

    /**
     * 将电压值转换为协议格式(保留一位小数)
     * 例如: 225.1V -> 2251
     *
     * @param voltage 电压值
     * @return 协议格式值
     */
    public static int voltageToProtocol(double voltage) {
        return (int) Math.round(voltage * 10);
    }

    /**
     * 将协议格式转换为电压值
     *
     * @param protocolValue 协议格式值
     * @return 电压值
     */
    public static double protocolToVoltage(int protocolValue) {
        return protocolValue / 10.0;
    }

    /**
     * 将电流值转换为协议格式(保留一位小数)
     *
     * @param current 电流值
     * @return 协议格式值
     */
    public static int currentToProtocol(double current) {
        return (int) Math.round(current * 10);
    }

    /**
     * 将协议格式转换为电流值
     *
     * @param protocolValue 协议格式值
     * @return 电流值
     */
    public static double protocolToCurrent(int protocolValue) {
        return protocolValue / 10.0;
    }

    /**
     * 将电量值转换为协议格式(保留四位小数)
     *
     * @param energy 电量值(kWh)
     * @return 协议格式值
     */
    public static long energyToProtocol(double energy) {
        return Math.round(energy * 10000);
    }

    /**
     * 将协议格式转换为电量值
     *
     * @param protocolValue 协议格式值
     * @return 电量值(kWh)
     */
    public static double protocolToEnergy(long protocolValue) {
        return protocolValue / 10000.0;
    }

    /**
     * 将金额值转换为协议格式(保留四位小数)
     *
     * @param amount 金额值(元)
     * @return 协议格式值
     */
    public static long amountToProtocol(double amount) {
        return Math.round(amount * 10000);
    }

    /**
     * 将协议格式转换为金额值
     *
     * @param protocolValue 协议格式值
     * @return 金额值(元)
     */
    public static double protocolToAmount(long protocolValue) {
        return protocolValue / 10000.0;
    }

    /**
     * 将费率值转换为协议格式(保留五位小数)
     *
     * @param rate 费率值(元/度)
     * @return 协议格式值
     */
    public static long rateToProtocol(double rate) {
        return Math.round(rate * 100000);
    }

    /**
     * 将协议格式转换为费率值
     *
     * @param protocolValue 协议格式值
     * @return 费率值(元 / 度)
     */
    public static double protocolToRate(long protocolValue) {
        return protocolValue / 100000.0;
    }

    /**
     * 温度值转换(协议中温度有-50偏移量)
     *
     * @param protocolValue 协议温度值
     * @return 实际温度值
     */
    public static int protocolToTemperature(int protocolValue) {
        return protocolValue - 50;
    }

    /**
     * 实际温度转协议格式
     *
     * @param temperature 实际温度值
     * @return 协议温度值
     */
    public static int temperatureToProtocol(int temperature) {
        return temperature + 50;
    }

    // ==================== 调试辅助方法 ====================

    /**
     * 字节数组转16进制字符串
     *
     * @param bytes 字节数组
     * @return 16进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        if (bytes == null) {
            return "null";
        }
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b & 0xFF));
        }
        return sb.toString().trim();
    }

    /**
     * 从 ByteBuf 中读取小端序长整型（支持可变长度）
     * 用于读取电表读数（5字节）
     *
     * @param buf    ByteBuf
     * @param length 读取长度（1-8字节）
     * @return 长整型值
     */
    public static long readLongLE(ByteBuf buf, int length) {
        if (length < 1 || length > 8) {
            throw new IllegalArgumentException("Length must be between 1 and 8");
        }

        long result = 0;
        for (int i = 0; i < length; i++) {
            long byteValue = buf.readByte() & 0xFFL;
            result |= (byteValue << (i * 8));
        }

        return result;
    }

    /**
     * ByteBuf转16进制字符串(不改变读索引)
     *
     * @param buf ByteBuf
     * @return 16进制字符串
     */
    public static String byteBufToHex(ByteBuf buf) {
        if (buf == null) {
            return "null";
        }
        int readerIndex = buf.readerIndex();
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        buf.readerIndex(readerIndex);
        return bytesToHex(bytes);
    }

    /**
     * 16进制字符串转字节数组
     *
     * @param hex 16进制字符串(可以包含空格)
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hex) {
        if (hex == null || hex.isEmpty()) {
            return new byte[0];
        }
        hex = hex.replaceAll("\\s+", "");
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    /**
     * CP56Time2a 格式转 Date
     * 7字节格式: 毫秒低位, 毫秒高位(秒含), 分, 时, 日(含周), 月, 年
     *
     * @param bytes 7字节时间数据
     * @return Date对象
     */
    public static Date cp56Time2aToDate(byte[] bytes) {
        if (bytes == null || bytes.length < 7) {
            return null;
        }

        try {
            // 毫秒 (2字节, 小端序, 含秒信息)
            int msLow = bytes[0] & 0xFF;
            int msHigh = bytes[1] & 0xFF;
            int totalMs = msLow | (msHigh << 8);
            int second = totalMs / 1000;
            int millisecond = totalMs % 1000;

            // 分钟 (6位有效)
            int minute = bytes[2] & 0x3F;

            // 小时 (5位有效)
            int hour = bytes[3] & 0x1F;

            // 日 (5位有效, 高3位为周)
            int day = bytes[4] & 0x1F;

            // 月 (4位有效)
            int month = bytes[5] & 0x0F;

            // 年 (7位有效, 相对于2000年)
            int year = (bytes[6] & 0x7F) + 2000;

            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.YEAR, year);
            cal.set(Calendar.MONTH, month - 1);
            cal.set(Calendar.DAY_OF_MONTH, day);
            cal.set(Calendar.HOUR_OF_DAY, hour);
            cal.set(Calendar.MINUTE, minute);
            cal.set(Calendar.SECOND, second);
            cal.set(Calendar.MILLISECOND, millisecond);

            return cal.getTime();

        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Date 转 CP56Time2a 格式
     *
     * @param date Date对象
     * @return 7字节时间数据
     */
    public static byte[] dateToCp56Time2a(Date date) {
        byte[] bytes = new byte[7];

        if (date == null) {
            return bytes;
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        int year = cal.get(Calendar.YEAR) - 2000;
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK) - 1; // 0=Sunday
        int hour = cal.get(Calendar.HOUR_OF_DAY);
        int minute = cal.get(Calendar.MINUTE);
        int second = cal.get(Calendar.SECOND);
        int millisecond = cal.get(Calendar.MILLISECOND);

        // 毫秒 + 秒
        int totalMs = second * 1000 + millisecond;
        bytes[0] = (byte) (totalMs & 0xFF);
        bytes[1] = (byte) ((totalMs >> 8) & 0xFF);

        // 分钟
        bytes[2] = (byte) (minute & 0x3F);

        // 小时
        bytes[3] = (byte) (hour & 0x1F);

        // 日 + 周
        bytes[4] = (byte) ((day & 0x1F) | ((dayOfWeek & 0x07) << 5));

        // 月
        bytes[5] = (byte) (month & 0x0F);

        // 年
        bytes[6] = (byte) (year & 0x7F);

        return bytes;
    }
}
