package com.lingniu.ingest.protocol.jt808.downlink;

import com.lingniu.ingest.protocol.jt808.model.Jt808MessageId;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 常用下行命令 body 构造器。返回的是 body 字节，外层由 {@link com.lingniu.ingest.protocol.jt808.codec.Jt808FrameEncoder} 包装。
 */
public final class Jt808Commands {

    private static final Charset GBK = Charset.forName("GBK");

    private Jt808Commands() {}

    /** 0x8001 平台通用应答（ack 一条终端上行）。 */
    public static DownlinkCommand platformAck(int ackSerial, int ackMsgId, int result) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(5);
        writeU16(os, ackSerial);
        writeU16(os, ackMsgId);
        os.write(result & 0xFF);
        return new DownlinkCommand(Jt808MessageId.PLATFORM_GENERAL_RESPONSE, os.toByteArray());
    }

    /** 0x8100 注册应答（result=0 成功）。 */
    public static DownlinkCommand registerAck(int ackSerial, int result, String token) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        writeU16(os, ackSerial);
        os.write(result & 0xFF);
        if (result == 0 && token != null) {
            byte[] tokenBytes = token.getBytes(StandardCharsets.US_ASCII);
            os.write(tokenBytes, 0, tokenBytes.length);
        }
        return new DownlinkCommand(Jt808MessageId.PLATFORM_REGISTER_ACK, os.toByteArray());
    }

    /** 0x8201 位置信息查询。 */
    public static DownlinkCommand queryLocation() {
        return new DownlinkCommand(Jt808MessageId.PLATFORM_QUERY_LOCATION, new byte[0]);
    }

    /** 0x8104 查询终端参数（全量）。 */
    public static DownlinkCommand queryParameters() {
        return new DownlinkCommand(Jt808MessageId.PLATFORM_QUERY_PARAMS, new byte[0]);
    }

    /** 0x8107 查询终端属性。 */
    public static DownlinkCommand queryAttributes() {
        return new DownlinkCommand(Jt808MessageId.PLATFORM_QUERY_ATTRS, new byte[0]);
    }

    /**
     * 0x8103 设置终端参数：参数项列表格式。
     *
     * <p>body 形如 {@code count(1) + N*paramItem}，每个 paramItem 为 {@code paramId(4) + length(1) + value}。
     */
    public static DownlinkCommand setParameters(java.util.List<ParamItem> items) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        os.write(items.size() & 0xFF);
        for (ParamItem item : items) {
            writeU32(os, item.paramId());
            os.write(item.value().length & 0xFF);
            os.write(item.value(), 0, item.value().length);
        }
        return new DownlinkCommand(Jt808MessageId.PLATFORM_SET_PARAMS, os.toByteArray());
    }

    /** 0x8105 终端控制：命令字(1) + 命令参数(GBK)。 */
    public static DownlinkCommand terminalControl(int commandWord, String commandParams) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        os.write(commandWord & 0xFF);
        if (commandParams != null) {
            byte[] bytes = commandParams.getBytes(GBK);
            os.write(bytes, 0, bytes.length);
        }
        return new DownlinkCommand(Jt808MessageId.PLATFORM_TERMINAL_CONTROL, os.toByteArray());
    }

    /** 0x8203 人工确认报警：报警消息流水号(WORD) + 报警类型(DWORD)。 */
    public static DownlinkCommand alarmAck(int responseSerialNo, long alarmType) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(6);
        writeU16(os, responseSerialNo);
        writeU32(os, alarmType);
        return new DownlinkCommand(Jt808MessageId.PLATFORM_ALARM_ACK, os.toByteArray());
    }

    /** 0x8601 删除圆形区域：区域总数(BYTE) + N * 区域ID(DWORD)。 */
    public static DownlinkCommand deleteArea(List<Long> areaIds) {
        if (areaIds.size() > 255) {
            throw new IllegalArgumentException("jt808 delete-area supports at most 255 area ids");
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream(1 + areaIds.size() * 4);
        os.write(areaIds.size() & 0xFF);
        for (Long areaId : areaIds) {
            writeU32(os, areaId == null ? 0 : areaId);
        }
        return new DownlinkCommand(Jt808MessageId.PLATFORM_DELETE_CIRCLE_AREA, os.toByteArray());
    }

    public record DownlinkCommand(int messageId, byte[] body) {}

    public record ParamItem(long paramId, byte[] value) {}

    private static void writeU16(ByteArrayOutputStream os, int v) {
        os.write((v >> 8) & 0xFF);
        os.write(v & 0xFF);
    }

    private static void writeU32(ByteArrayOutputStream os, long v) {
        os.write((int) ((v >> 24) & 0xFF));
        os.write((int) ((v >> 16) & 0xFF));
        os.write((int) ((v >> 8) & 0xFF));
        os.write((int) (v & 0xFF));
    }
}
