package com.foxx.androidcast.media;

import android.util.Log;

import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.media.codec.PassthroughCodecPolicy;
import com.foxx.androidcast.network.CastCodecFlags;

import java.util.Locale;

/**
 * Picks an audio codec both sides support, honoring user preference when possible.
 * AUTO uses {@link CodecPriorityCatalog#decideBestAudioCodec} (weights + codecs.json).
 */
public final class AudioNegotiator {
    private static final String TAG = "AudioNegotiator";

    private AudioNegotiator() {}

    /** Resolve preference for this session (explicit or AUTO). */
    public static CastSettings.AudioCodec negotiate(int senderMask, int receiverMask,
            CastSettings.AudioCodec preference, CastSettings settings) {
        return negotiate(senderMask, receiverMask, preference, settings, "negotiate");
    }

    public static CastSettings.AudioCodec negotiate(int senderMask, int receiverMask,
            CastSettings.AudioCodec preference, CastSettings settings, String logRole) {
        CastSettings scoreSettings = settings != null ? settings : new CastSettings();
        CastSettings.AudioCodec pref = preference != null ? preference : CastSettings.AudioCodec.AUTO;

        if (pref == CastSettings.AudioCodec.AUTO) {
            CodecPriorityCatalog.AudioNegotiationDecision decision =
                    CodecPriorityCatalog.decideBestAudioCodec(senderMask, receiverMask, scoreSettings);
            if (decision != null) {
                CodecPriorityCatalog.logAudioNegotiationDecision(logRole, decision);
                return decision.codec;
            }
            Log.w(TAG, logRole + " audio AUTO fallback AAC (no scored candidate)");
            return CastSettings.AudioCodec.AAC;
        }

        CastSettings.AudioCodec effective = PassthroughCodecPolicy.forAudioNegotiation(pref);
        if (effective == CastSettings.AudioCodec.AUTO) {
            return negotiate(senderMask, receiverMask, CastSettings.AudioCodec.AUTO, scoreSettings, logRole);
        }
        if (!CastCodecFlags.maskSupports(senderMask, effective)
                || !CastCodecFlags.maskSupports(receiverMask, effective)) {
            Log.w(TAG, logRole + " audio explicit " + pref.name()
                    + " unavailable on link; falling back to AUTO");
            return negotiate(senderMask, receiverMask, CastSettings.AudioCodec.AUTO, scoreSettings, logRole);
        }
        int score = CodecPriorityCatalog.priorityScore(effective, scoreSettings);
        Log.i(TAG, String.format(Locale.US, "%s audio explicit %s score=%d",
                logRole, PassthroughCodecPolicy.displayAudioPreference(effective), score));
        return effective;
    }

    public static CastSettings.AudioCodec resolveForSender(CastSettings settings, int peerAudioMask) {
        if (settings == null) {
            return CastSettings.AudioCodec.AAC;
        }
        CastSettings.AudioCodec resolved = negotiate(
                CastCodecFlags.localAudioMask(),
                peerAudioMask,
                settings.getAudioCodec(),
                settings,
                "sender");
        settings.setResolvedAudioCodec(resolved);
        return resolved;
    }

    public static CastSettings.AudioCodec effective(CastSettings settings) {
        if (settings == null) {
            return CastSettings.AudioCodec.AAC;
        }
        CastSettings.AudioCodec resolved = settings.getResolvedAudioCodec();
        if (resolved != null) {
            return resolved;
        }
        if (settings.getAudioCodec() == CastSettings.AudioCodec.AUTO) {
            return CastSettings.AudioCodec.AAC;
        }
        return PassthroughCodecPolicy.forAudioNegotiation(settings.getAudioCodec());
    }
}
