package com.foxx.androidcast.receiver;

/*********************************************************************
 * LibvpxVideoDecoder.java
 * Created at: Mon 18 May 2026 17:05:47 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class LibvpxVideoDecoder
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (3 commits, 96 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	51d754efd8877e0817033f023a3a1aa9e3b4b4d316aee7d6981314fb65c485e7
**********************************************************************/
import android.util.Log;
import android.view.Surface;

import com.foxx.androidcast.media.codec.VideoDecoderSink;
import com.foxx.androidcast.media.codec.jni.NativeCodecBridge;
import com.foxx.androidcast.receiver.av.ReceiverAvRuntime;

import java.io.IOException;

/** Software VP8/VP9 decode via libvpx, rendering to a {@link Surface}. */
public final class LibvpxVideoDecoder implements VideoDecoderSink {
    private static final String TAG = "LibvpxVideoDecoder";
    private static final int DECODE_FAIL_LOG_EVERY = 16;

    private final Object lock = new Object();
    private int decodeFailLogCount;
    private long nativeHandle;
    private Surface outputSurface;
    private Listener listener;
    private boolean released;
    private boolean firstFrameNotified;

    @Override
    public void configure(String mime, int width, int height, byte[] csd0, byte[] csd1, Surface surface,
            Listener listener) throws IOException {
        synchronized (lock) {
            releaseLocked();
            if (!NativeCodecBridge.isLibvpxAvailable()) {
                throw new IOException("libvpx not available");
            }
            nativeHandle = NativeCodecBridge.vpxDecoderCreate(mime);
            if (nativeHandle == 0L) {
                throw new IOException("libvpx decoder init failed for " + mime);
            }
            this.outputSurface = surface;
            this.listener = listener;
            this.released = false;
            this.firstFrameNotified = false;
            Log.i(TAG, "Configured libvpx decoder " + mime + " " + width + "x" + height);
        }
    }

    @Override
    public void queueFrame(long ptsUs, boolean keyFrame, byte[] data) {
        synchronized (lock) {
            if (released || nativeHandle == 0L || outputSurface == null || data == null || data.length == 0) {
                return;
            }
            if (NativeCodecBridge.vpxDecodeFrame(nativeHandle, data, ptsUs, outputSurface)) {
                ReceiverAvRuntime.video().onFrameDecoded(ptsUs);
                notifyRenderedLocked();
            } else if ((++decodeFailLogCount % DECODE_FAIL_LOG_EVERY) == 1) {
                Log.w(TAG, "vpx decode failed len=" + data.length + " key=" + keyFrame);
            }
        }
    }

    @Override
    public void release() {
        synchronized (lock) {
            releaseLocked();
        }
    }

    private void releaseLocked() {
        released = true;
        listener = null;
        outputSurface = null;
        firstFrameNotified = false;
        if (nativeHandle != 0L) {
            NativeCodecBridge.vpxDecoderRelease(nativeHandle);
            nativeHandle = 0L;
        }
    }

    private void notifyRenderedLocked() {
        Listener cb = listener;
        if (cb == null) {
            return;
        }
        if (!firstFrameNotified) {
            firstFrameNotified = true;
            Log.i(TAG, "First frame rendered (libvpx)");
            cb.onFirstFrameRendered();
        } else {
            cb.onFrameRendered();
        }
    }
}
