package com.usky.cdi.service.sip.ps;

/**
 * GB28181 PS/PES 头生成（位级写法，与社区 gb28181_make_* 参考实现一致）。
 */
public final class Gb28181PsHeaders {

    private static final int PS_HDR_LEN = 14;
    private static final int SYS_HDR_LEN = 18;
    private static final int PSM_HDR_LEN = 24;
    private static final int PES_HDR_LEN = 19;

    private Gb28181PsHeaders() {
    }

    /** 海康 Pack 头 stuffing 长度（stuffinglen & 0x07） */
    private static final int HIKVISION_PACK_STUFFING = 6;

    /** GB28181 标准 Pack 头（14 字节，stuffing=0，与 gb28181_make_ps_header 一致） */
    public static byte[] buildPsHeader(long pts90k) {
        return buildStandardPackHeader(pts90k);
    }

    public static byte[] buildStandardPackHeader(long pts90k) {
        long scrExt = pts90k % 100;
        long scr = pts90k / 100;
        BitWriter bits = new BitWriter(PS_HDR_LEN);
        bits.write(32, 0x000001BA);
        bits.write(2, 1);
        bits.write(3, (scr >> 30) & 0x07);
        bits.write(1, 1);
        bits.write(15, (scr >> 15) & 0x7FFF);
        bits.write(1, 1);
        bits.write(15, scr & 0x7FFF);
        bits.write(1, 1);
        bits.write(9, scrExt & 0x01FF);
        bits.write(1, 1);
        bits.write(22, 255);
        bits.write(2, 3);
        bits.write(5, 0x1F);
        bits.write(3, 0);
        return bits.toArray();
    }

    /**
     * 海康 Pack 头：14 字节标准头 + 6 字节 stuffing（FF FF 00 00 00 01）。
     */
    public static byte[] buildHikvisionPackHeader(long pts90k) {
        long scrExt = pts90k % 100;
        long scr = pts90k / 100;
        BitWriter bits = new BitWriter(PS_HDR_LEN);
        bits.write(32, 0x000001BA);
        bits.write(2, 1);
        bits.write(3, (scr >> 30) & 0x07);
        bits.write(1, 1);
        bits.write(15, (scr >> 15) & 0x7FFF);
        bits.write(1, 1);
        bits.write(15, scr & 0x7FFF);
        bits.write(1, 1);
        bits.write(9, scrExt & 0x01FF);
        bits.write(1, 1);
        bits.write(22, 255);
        bits.write(2, 3);
        bits.write(5, 0x1F);
        bits.write(3, HIKVISION_PACK_STUFFING);
        byte[] header = bits.toArray();
        byte[] stuffed = new byte[header.length + HIKVISION_PACK_STUFFING];
        System.arraycopy(header, 0, stuffed, 0, header.length);
        stuffed[header.length] = (byte) 0xFF;
        stuffed[header.length + 1] = (byte) 0xFF;
        stuffed[header.length + 2] = 0x00;
        stuffed[header.length + 3] = 0x00;
        stuffed[header.length + 4] = 0x00;
        stuffed[header.length + 5] = 0x01;
        return stuffed;
    }

    public static byte[] buildSystemHeader() {
        BitWriter bits = new BitWriter(SYS_HDR_LEN);
        bits.write(32, 0x000001BB);
        bits.write(16, SYS_HDR_LEN - 6);
        bits.write(1, 1);
        bits.write(22, 50000);
        bits.write(1, 1);
        bits.write(6, 1);
        bits.write(1, 0);
        bits.write(1, 1);
        bits.write(1, 1);
        bits.write(1, 1);
        bits.write(1, 1);
        bits.write(5, 1);
        bits.write(1, 0);
        bits.write(7, 0x7F);
        bits.write(8, 0xC0);
        bits.write(2, 3);
        bits.write(1, 0);
        bits.write(13, 512);
        bits.write(8, 0xE0);
        bits.write(2, 3);
        bits.write(1, 1);
        bits.write(13, 2048);
        return bits.toArray();
    }

    /**
     * GB28181 标准 PSM（24 字节，含 H264/G711 映射 + CRC），与 gb28181_make_psm_header 一致。
     */
    public static byte[] buildStandardPsm() {
        BitWriter bits = new BitWriter(PSM_HDR_LEN);
        bits.write(24, 0x000001);
        bits.write(8, 0xBC);
        bits.write(16, 18);
        bits.write(1, 1);
        bits.write(2, 3);
        bits.write(5, 0);
        bits.write(7, 0x7F);
        bits.write(1, 1);
        bits.write(16, 0);
        bits.write(16, 8);
        bits.write(8, 0x90);
        bits.write(8, 0xC0);
        bits.write(16, 0);
        bits.write(8, 0x1B);
        bits.write(8, 0xE0);
        bits.write(16, 0);
        bits.write(8, 0x45);
        bits.write(8, 0xBD);
        bits.write(8, 0xDC);
        bits.write(8, 0xF4);
        return bits.toArray();
    }

    /** @deprecated 海康 EHome 私有 PSM，FFmpeg mpeg demuxer 无法可靠解析 */
    public static byte[] buildHikvisionPsm() {
        return HIKVISION_PSM.clone();
    }

    /** 海康 IPC 典型 PSM 模板（1280x720 H264 + G711A） */
    private static final byte[] HIKVISION_PSM = {
            0x00, 0x00, 0x01, (byte) 0xBC, 0x00, 0x5A, (byte) 0xE0, (byte) 0xFF,
            0x00, 0x24, 0x40, 0x0E, 0x48, 0x4B, 0x00, 0x00,
            0x17, (byte) 0x9D, 0x3E, (byte) 0xB4, 0x60, 0x00, 0x00, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, 0x41, 0x12, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C,
            0x1B, (byte) 0xE0, 0x00, 0x10, 0x42, 0x0E, 0x00, 0x00,
            (byte) 0xA0, 0x21, 0x05, 0x00, 0x02, (byte) 0xD0, 0x12, 0x1C,
            0x3F, 0x00, 0x1C, 0x21, 0x0F, (byte) 0xC0, 0x00, 0x0C,
            0x43, 0x0A, 0x00, 0x00, (byte) 0xFE, 0x00, (byte) 0xFA, 0x03,
            0x01, (byte) 0xF4, 0x03, (byte) 0xFF, (byte) 0xBD, (byte) 0xBD,
            0x00, 0x00, (byte) 0xBF, (byte) 0xBF
    };

    /** @deprecated 使用 {@link #buildStandardPsm()} */
    public static byte[] buildPsmHeader() {
        return buildStandardPsm();
    }

    public static byte[] buildPesHeader(int streamId, int payloadLen, long pts90k) {
        long pts = pts90k / 100;
        long dts = pts;
        int packetLen = payloadLen + 13;
        if (packetLen > 0xFFFF) {
            packetLen = 0;
        }
        BitWriter bits = new BitWriter(PES_HDR_LEN);
        bits.write(24, 0x000001);
        bits.write(8, streamId);
        bits.write(16, packetLen);
        bits.write(2, 2);
        bits.write(2, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 1);
        bits.write(1, 1);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(1, 0);
        bits.write(8, 10);
        bits.write(4, 3);
        bits.write(3, (pts >> 30) & 0x07);
        bits.write(1, 1);
        bits.write(15, (pts >> 15) & 0x7FFF);
        bits.write(1, 1);
        bits.write(15, pts & 0x7FFF);
        bits.write(1, 1);
        bits.write(4, 1);
        bits.write(3, (dts >> 30) & 0x07);
        bits.write(1, 1);
        bits.write(15, (dts >> 15) & 0x7FFF);
        bits.write(1, 1);
        bits.write(15, dts & 0x7FFF);
        bits.write(1, 1);
        return bits.toArray();
    }

    private static final class BitWriter {
        private final byte[] data;
        private int bitPos;

        BitWriter(int size) {
            data = new byte[size];
        }

        void write(int bitCount, long value) {
            for (int i = bitCount - 1; i >= 0; i--) {
                if (((value >> i) & 1) == 1) {
                    data[bitPos / 8] |= (byte) (0x80 >> (bitPos % 8));
                }
                bitPos++;
            }
        }

        byte[] toArray() {
            return data;
        }
    }
}
