package com.foxx.androidcast.media.codec;

/*********************************************************************
 * PassthroughCodecPolicy.java
 * Created at: Sun 17 May 2026 22:49:49 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class PassthroughCodecPolicy
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (4 commits, 154 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	fdcd87ff4674eca1c4857ae70bcccb26aead56fe3b1d9024f64b793588bbce16
**********************************************************************/
import android.media.MediaFormat;

import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.media.CodecNegotiator;
import com.foxx.androidcast.media.codec.jni.NativeCodecBridge;

/**
 * Maps UI / future software codec choices to wire negotiation and HW backends.
 * Disabled libvpx / Speex / Opus entries use passthrough to system codecs until integrated.
 */
public final class PassthroughCodecPolicy {
    private PassthroughCodecPolicy() {}

    public static CastSettings.VideoCodec forNegotiation(CastSettings.VideoCodec preference) {
        if (preference == null) {
            return CastSettings.VideoCodec.AUTO;
        }
        switch (preference) {
            case LIBVPX_VP8:
            case LIBVPX_VP9:
                return preference;
            case PASSTHROUGH:
                return CastSettings.VideoCodec.AUTO;
            default:
                return preference;
        }
    }

    public static VideoCodecBackend videoBackendFor(CastSettings.VideoCodec preference) {
        if (preference == CastSettings.VideoCodec.LIBVPX_VP8
                || preference == CastSettings.VideoCodec.LIBVPX_VP9) {
            return NativeCodecBridge.isLibvpxAvailable()
                    ? VideoCodecBackend.LIBVPX_NATIVE : VideoCodecBackend.LIBVPX_STUB;
        }
        if (preference == CastSettings.VideoCodec.PASSTHROUGH) {
            return VideoCodecBackend.PASSTHROUGH;
        }
        return VideoCodecBackend.MEDIA_CODEC_HW;
    }

    public static CastSettings.AudioCodec forAudioNegotiation(CastSettings.AudioCodec preference) {
        if (preference == null) {
            return CastSettings.AudioCodec.AUTO;
        }
        switch (preference) {
            case SPEEX:
            case OPUS:
                return preference;
            case PASSTHROUGH_DEBUG:
                return CastSettings.AudioCodec.AUTO;
            default:
                return preference;
        }
    }

    public static AudioCodecBackend audioBackendFor(CastSettings.AudioCodec preference,
            boolean audioEnabled) {
        if (!audioEnabled) {
            return AudioCodecBackend.DISABLED;
        }
        if (preference == CastSettings.AudioCodec.SPEEX) {
            return NativeCodecBridge.isSpeexAvailable()
                    ? AudioCodecBackend.SPEEX_NATIVE : AudioCodecBackend.SPEEX_STUB;
        }
        if (preference == CastSettings.AudioCodec.OPUS) {
            return NativeCodecBridge.isOpusAvailable()
                    ? AudioCodecBackend.OPUS_NATIVE : AudioCodecBackend.OPUS_STUB;
        }
        if (preference == CastSettings.AudioCodec.PASSTHROUGH_DEBUG) {
            return AudioCodecBackend.PASSTHROUGH_DEBUG;
        }
        return AudioCodecBackend.MEDIA_CODEC_AAC;
    }

    public static String protectionDescription(CastSettings.StreamProtection mode, String transport,
            CastSettings.AudioCodec audioPref) {
        StringBuilder sb = new StringBuilder();
        if (mode != null) {
            sb.append(mode.name());
        } else {
            sb.append("AUTO");
        }
        if (CastSettings.AudioCodec.OPUS == audioPref) {
            sb.append(" · Opus in-band FEC (when Opus enabled)");
        } else if (CastSettings.AudioCodec.SPEEX == audioPref) {
            sb.append(" · Speex FEC (when Speex enabled)");
        }
        if (transport != null && transport.equalsIgnoreCase("udp")) {
            if (mode == null || mode == CastSettings.StreamProtection.NONE
                    || mode == CastSettings.StreamProtection.AUTO) {
                sb.append(" · UDP protection off (default)");
            } else {
                sb.append(" · UDP per-shard FEC/NACK when negotiated");
            }
        }
        return sb.toString();
    }

    public static String wireMimeHint(CastSettings.VideoCodec preference) {
        switch (preference) {
            case LIBVPX_VP8:
                return MediaFormat.MIMETYPE_VIDEO_VP8;
            case LIBVPX_VP9:
                return MediaFormat.MIMETYPE_VIDEO_VP9;
            case H264:
                return MediaFormat.MIMETYPE_VIDEO_AVC;
            case H265:
                return MediaFormat.MIMETYPE_VIDEO_HEVC;
            case AUTO:
            default:
                return "negotiated";
        }
    }

    public static String displayVideoPreference(CastSettings.VideoCodec codec) {
        if (codec == null) {
            return CodecNegotiator.displayName(null);
        }
        switch (codec) {
            case LIBVPX_VP8:
                return "libvpx (VP8)";
            case LIBVPX_VP9:
                return "libvpx (VP9)";
            case H264:
                return "H.264";
            case H265:
                return "H.265";
            case PASSTHROUGH:
                return "Passthrough";
            case AUTO:
            default:
                return "AUTO";
        }
    }

    public static String displayAudioPreference(CastSettings.AudioCodec codec) {
        if (codec == null) {
            return "AUTO";
        }
        switch (codec) {
            case SPEEX:
                return "Speex";
            case OPUS:
                return "Opus";
            case AAC:
                return "AAC";
            case PASSTHROUGH_DEBUG:
                return "Passthrough (debug)";
            case AUTO:
            default:
                return "AUTO";
        }
    }
}
