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

import com.lingniu.ingest.protocol.jt1078.model.Jt1078MessageId;
import com.lingniu.ingest.protocol.jt808.downlink.Jt808Commands;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;

/** JT/T 1078 下行信令 body 构造器，外层帧仍由 JT808 通道发送。 */
public final class Jt1078Commands {

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

    private Jt1078Commands() {}

    /** 0x1003 查询终端音视频属性。 */
    public static Jt808Commands.DownlinkCommand queryMediaAttributes() {
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_QUERY_MEDIA_ATTRS, new byte[0]);
    }

    /** 0x9101 实时音视频传输请求。 */
    public static Jt808Commands.DownlinkCommand realtimePlay(String ip,
                                                             int tcpPort,
                                                             int udpPort,
                                                             int channelNo,
                                                             int mediaType,
                                                             int streamType) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        writeStringWithU8Length(os, ip);
        writeU16(os, tcpPort);
        writeU16(os, udpPort);
        writeU8(os, channelNo);
        writeU8(os, mediaType);
        writeU8(os, streamType);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_REALTIME_PLAY, os.toByteArray());
    }

    /** 0x9102 音视频实时传输控制。 */
    public static Jt808Commands.DownlinkCommand realtimeControl(int channelNo,
                                                                int command,
                                                                int closeType,
                                                                int streamType) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(4);
        writeU8(os, channelNo);
        writeU8(os, command);
        writeU8(os, closeType);
        writeU8(os, streamType);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_REALTIME_CONTROL, os.toByteArray());
    }

    /** 0x9201 平台下发远程录像回放请求。 */
    public static Jt808Commands.DownlinkCommand historyPlay(String ip,
                                                            int tcpPort,
                                                            int udpPort,
                                                            int channelNo,
                                                            int mediaType,
                                                            int streamType,
                                                            int storageType,
                                                            int playbackMode,
                                                            int playbackSpeed,
                                                            String startTime,
                                                            String endTime) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        writeStringWithU8Length(os, ip);
        writeU16(os, tcpPort);
        writeU16(os, udpPort);
        writeU8(os, channelNo);
        writeU8(os, mediaType);
        writeU8(os, streamType);
        writeU8(os, storageType);
        writeU8(os, playbackMode);
        writeU8(os, playbackSpeed);
        writeBcdTime(os, startTime);
        writeBcdTime(os, endTime);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_HISTORY_PLAY, os.toByteArray());
    }

    /** 0x9202 平台下发远程录像回放控制。 */
    public static Jt808Commands.DownlinkCommand historyControl(int channelNo,
                                                               int playbackMode,
                                                               int playbackSpeed,
                                                               String playbackTime) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(9);
        writeU8(os, channelNo);
        writeU8(os, playbackMode);
        writeU8(os, playbackSpeed);
        writeBcdTime(os, playbackTime);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_HISTORY_CONTROL, os.toByteArray());
    }

    /** 0x9205 查询资源列表。 */
    public static Jt808Commands.DownlinkCommand resourceSearch(int channelNo,
                                                               String startTime,
                                                               String endTime,
                                                               long warnBit1,
                                                               long warnBit2,
                                                               int mediaType,
                                                               int streamType,
                                                               int storageType) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(24);
        writeU8(os, channelNo);
        writeBcdTime(os, startTime);
        writeBcdTime(os, endTime);
        writeU32(os, warnBit1);
        writeU32(os, warnBit2);
        writeU8(os, mediaType);
        writeU8(os, streamType);
        writeU8(os, storageType);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_RESOURCE_SEARCH, os.toByteArray());
    }

    /** 0x9206 文件上传指令。 */
    public static Jt808Commands.DownlinkCommand fileUpload(String ip,
                                                           int port,
                                                           String username,
                                                           String password,
                                                           String path,
                                                           int channelNo,
                                                           String startTime,
                                                           String endTime,
                                                           long warnBit1,
                                                           long warnBit2,
                                                           int mediaType,
                                                           int streamType,
                                                           int storageType,
                                                           int condition) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        writeStringWithU8Length(os, ip);
        writeU16(os, port);
        writeStringWithU8Length(os, username);
        writeStringWithU8Length(os, password);
        writeStringWithU8Length(os, path);
        writeU8(os, channelNo);
        writeBcdTime(os, startTime);
        writeBcdTime(os, endTime);
        writeU32(os, warnBit1);
        writeU32(os, warnBit2);
        writeU8(os, mediaType);
        writeU8(os, streamType);
        writeU8(os, storageType);
        writeU8(os, condition);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_FILE_UPLOAD, os.toByteArray());
    }

    /** 0x9207 文件上传控制。 */
    public static Jt808Commands.DownlinkCommand fileUploadControl(int responseSerialNo, int command) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(3);
        writeU16(os, responseSerialNo);
        writeU8(os, command);
        return new Jt808Commands.DownlinkCommand(Jt1078MessageId.PLATFORM_FILE_UPLOAD_CONTROL, os.toByteArray());
    }

    private static void writeStringWithU8Length(ByteArrayOutputStream os, String value) {
        byte[] bytes = value == null ? new byte[0] : value.getBytes(GBK);
        if (bytes.length > 255) {
            throw new IllegalArgumentException("jt1078 string field too long: " + bytes.length);
        }
        writeU8(os, bytes.length);
        os.write(bytes, 0, bytes.length);
    }

    private static void writeBcdTime(ByteArrayOutputStream os, String value) {
        String digits = value == null ? "" : value.replaceAll("\\D", "");
        if (digits.length() == 14 && digits.startsWith("20")) {
            digits = digits.substring(2);
        }
        if (digits.isBlank()) {
            digits = "000000000000";
        }
        if (digits.length() != 12) {
            throw new IllegalArgumentException("jt1078 time must be YYMMDDHHMMSS or yyyy-MM-dd HH:mm:ss: " + value);
        }
        for (int i = 0; i < digits.length(); i += 2) {
            int high = Character.digit(digits.charAt(i), 10);
            int low = Character.digit(digits.charAt(i + 1), 10);
            if (high < 0 || low < 0) {
                throw new IllegalArgumentException("jt1078 time contains non-decimal digit: " + value);
            }
            os.write((high << 4) | low);
        }
    }

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

    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));
    }
}
