package invalid.lena.scrcpy;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

// Byte-order helpers used everywhere we touch raw streams.
//
// scrcpy server framing is big-endian (java standard).
// adb sync framing is little-endian (legacy).
//
// FOURCCs are read as a big-endian uint32, which matches the literal
// 4-char ASCII tag, so '"h264"' becomes 0x68323634.
public final class Wire {

    private Wire() {}

    // ---- big-endian (scrcpy) ----

    public static int readBe32(byte[] b, int off) {
        return ((b[off]     & 0xff) << 24)
             | ((b[off + 1] & 0xff) << 16)
             | ((b[off + 2] & 0xff) <<  8)
             |  (b[off + 3] & 0xff);
    }

    public static long readBe64(byte[] b, int off) {
        return ((long)(readBe32(b, off)) << 32) | (readBe32(b, off + 4) & 0xffffffffL);
    }

    public static void writeBe32(byte[] b, int off, int v) {
        b[off]     = (byte)(v >>> 24);
        b[off + 1] = (byte)(v >>> 16);
        b[off + 2] = (byte)(v >>>  8);
        b[off + 3] = (byte) v;
    }

    public static void writeBe64(byte[] b, int off, long v) {
        writeBe32(b, off,     (int)(v >>> 32));
        writeBe32(b, off + 4, (int) v);
    }

    // ---- little-endian (adb sync) ----

    public static int readLe32(byte[] b, int off) {
        return  (b[off]     & 0xff)
             | ((b[off + 1] & 0xff) <<  8)
             | ((b[off + 2] & 0xff) << 16)
             | ((b[off + 3] & 0xff) << 24);
    }

    public static void writeLe32(byte[] b, int off, int v) {
        b[off]     = (byte) v;
        b[off + 1] = (byte)(v >>>  8);
        b[off + 2] = (byte)(v >>> 16);
        b[off + 3] = (byte)(v >>> 24);
    }

    // ---- I/O helpers ----

    public static void readFully(InputStream in, byte[] buf, int off, int len) throws IOException {
        int got = 0;
        while (got < len) {
            int n = in.read(buf, off + got, len - got);
            if (n < 0) throw new EOFException(
                    "short read: wanted " + len + " got " + got);
            got += n;
        }
    }

    public static void readFully(InputStream in, byte[] buf) throws IOException {
        readFully(in, buf, 0, buf.length);
    }

    // scrcpy codec identifiers, mirrored from com.genymobile.scrcpy.{video,audio}.*Codec.
    // Short names (raw, aac) are NUL-padded on the left, not space-padded on the right.
    // Declared as int-literal constants so they can drive switch-case labels.
    public static final int CODEC_H264 = 0x68_32_36_34; // 'h264'
    public static final int CODEC_H265 = 0x68_32_36_35; // 'h265'
    public static final int CODEC_AV1  = 0x61_76_30_31; // 'av01'
    public static final int CODEC_OPUS = 0x6f_70_75_73; // 'opus'
    public static final int CODEC_FLAC = 0x66_6c_61_63; // 'flac'
    public static final int CODEC_AAC  = 0x00_61_61_63; // '\0aac'
    public static final int CODEC_RAW  = 0x00_72_61_77; // '\0raw'

    public static String fourccName(int v) {
        // Skip any leading NUL bytes - scrcpy left-pads short names like
        // 'raw' and 'aac' with \0, which would otherwise render as
        // non-printable characters in logs.
        char[] cs = new char[]{
                (char)((v >>> 24) & 0xff),
                (char)((v >>> 16) & 0xff),
                (char)((v >>>  8) & 0xff),
                (char)( v         & 0xff)};
        int from = 0;
        while (from < cs.length && cs[from] == 0) from++;
        return new String(cs, from, cs.length - from);
    }
}
