package com.ruoyi.device.mqtt.handler.encoder;

import com.ruoyi.device.mqtt.client.EmqClient;
import com.ruoyi.device.mqtt.constants.MqttConstants;
import com.ruoyi.device.mqtt.domain.BaseBody;
import com.ruoyi.device.mqtt.enums.MsgTypeEnum;
import com.ruoyi.device.mqtt.enums.QosEnum;
import com.ruoyi.device.mqtt.enums.VersionEnum;
import com.ruoyi.device.mqtt.util.CRC16Standard;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 报文组帧能力
 *
 * @author lwm
 */
public abstract class AbstractEncoder<T> implements IEncoder<T>
{
    private static final Logger log = LoggerFactory.getLogger(AbstractEncoder.class);

    @Autowired
    private EmqClient emqClient;

    /**
     * 组帧报文
     *
     * @param t java对象
     */
    @Override
    public void encode(T t)
    {
        ByteBuf header = Unpooled.buffer(800);
        header.writeShortLE(MqttConstants.FRAME_HEADER);
        header.writeByte(VersionEnum.THIRD.getValue());
        header.writeIntLE(1);
        header.writeByte(msgType().getFirstType());
        header.writeShortLE(msgType().getSecondType());

        ByteBuf body = Unpooled.buffer(800);
        encode(t, body);

        int length = body.readableBytes();
        byte[] msgBytes = new byte[length];
        body.getBytes(body.readerIndex(), msgBytes);

        header.writeShortLE(msgBytes.length + 2);
        header.writeBytes(msgBytes);

        int totalLength = header.readableBytes();
        byte[] totalBytes = new byte[totalLength];
        header.getBytes(header.readerIndex(), totalBytes);

        byte[] crcBytes = CRC16Standard.getCRCBytes(totalBytes);
        byte[] bytes = ArrayUtils.addAll(totalBytes, crcBytes);

        log.info("发送消息:{}", bytesToHexLog(bytes));
        emqClient.publish(topic(t), bytes, qos(), retain());
    }

    /**
     * byte[] 转16进制 打印日志
     *
     * @param bytes bytes
     * @return 16进制字符串 打印日志
     */
    public static String bytesToHexLog(byte[] bytes)
    {
        try
        {
            StringBuilder sb = new StringBuilder();
            String tmp;
            sb.append("[");
            for (byte b : bytes)
            {
                tmp = Integer.toHexString(0xFF & b);
                if (tmp.length() == 1)
                {
                    tmp = "0" + tmp;
                }
                sb.append(tmp).append(" ");
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append("]");
            return sb.toString();
        }
        catch (Exception e)
        {
            log.error("原始数据转换为16进制错误：{}", e.getMessage(), e);
        }
        return "";
    }

    /**
     * 组帧报文
     *
     * @param t java对象
     */
    protected abstract void encode(T t, ByteBuf body);

    /**
     * 获取下发的topic
     *
     * @param t java对象
     * @return topic
     */
    protected abstract String topic(T t);

    /**
     * 获取报文类型
     *
     * @return 报文类型
     */
    protected abstract MsgTypeEnum msgType();

    /**
     * 获取报文Qos
     *
     * @return Qos
     */
    protected abstract QosEnum qos();

    /**
     * 获取报文是否保留
     *
     * @return 是否保留
     */
    protected abstract boolean retain();

    /**
     * 设置目标设备类型
     *
     * @param deviceType    设备型号
     * @param body          报文体
     */
    protected void setTargetType(String deviceType, ByteBuf body)
    {
        String q = StringUtils.substring(deviceType, 0, 2);
        String h = StringUtils.substring(deviceType, 2, 4);
        byte[] bytes = hexStringToByte(h + q);
        body.writeBytes(bytes);
    }

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

    /**
     * 生成topic
     *
     * @param baseBody java对象
     * @return topic
     */
    protected String generateTopic(BaseBody baseBody)
    {
        String deviceSnFormat = String.format("%010d", baseBody.getDeviceSn());
        return String.format(MqttConstants.DEVICE_DOWN_TOPIC, baseBody.getDeviceType(), deviceSnFormat);
    }
}
