package com.foxx.androidcast.network.transport;

/*********************************************************************
 * StreamProtectionFactory.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 StreamProtectionFactory
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (3 commits, 72 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	d95251aee912c4c9073550c2ffd1db792d73e960ef11cee2143f509e716a8b44
**********************************************************************/
import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.network.UdpCastTransport;
import com.foxx.androidcast.network.transport.fec.ProtectionEnvelope;

/** Builds {@link StreamProtectionEngine} from {@link CastSettings.StreamProtection}. */
public final class StreamProtectionFactory {
    private static final int FEC34_DATA = 4;
    private static final int FEC34_PARITY = 3;
    private static final int RS_DATA = 8;
    private static final int RS_PARITY = 4;

    private StreamProtectionFactory() {}

    public static StreamProtectionEngine create(CastSettings.StreamProtection setting,
            TransportLossStats stats) {
        return create(setting, stats, wantsBundledNack(setting));
    }

    public static StreamProtectionEngine create(CastSettings.StreamProtection setting,
            TransportLossStats stats, boolean nackEnabled) {
        CastSettings.StreamProtection mode = StreamProtectionNegotiator.normalize(setting);
        RetransmitCache cache = new RetransmitCache();
        switch (mode) {
            case NONE:
                return StreamProtectionEngine.noop();
            case FEC_3_4:
                return wrapFec(new FecProtectionEngine(ProtectionEnvelope.MODE_FEC_34,
                        FEC34_DATA, FEC34_PARITY, stats), stats, cache, nackEnabled);
            case FEC_REED_SOLOMON:
                return wrapFec(new FecProtectionEngine(ProtectionEnvelope.MODE_REED_SOLOMON,
                        RS_DATA, RS_PARITY, stats), stats, cache, nackEnabled);
            case NACK:
                return new NackProtectionEngine(stats, cache);
            case FEC_NACK:
            default:
                FecProtectionEngine fec = new FecProtectionEngine(ProtectionEnvelope.MODE_FEC_NACK,
                        FEC34_DATA, FEC34_PARITY, stats);
                NackProtectionEngine nack = new NackProtectionEngine(stats, cache);
                return new CombinedProtectionEngine(fec, nack);
        }
    }

    private static StreamProtectionEngine wrapFec(FecProtectionEngine fec,
            TransportLossStats stats, RetransmitCache cache, boolean nackEnabled) {
        if (!nackEnabled) {
            return fec;
        }
        return new CombinedProtectionEngine(fec, new NackProtectionEngine(stats, cache));
    }

    private static boolean wantsBundledNack(CastSettings.StreamProtection mode) {
        return mode == CastSettings.StreamProtection.FEC_3_4
                || mode == CastSettings.StreamProtection.FEC_NACK
                || mode == CastSettings.StreamProtection.FEC_REED_SOLOMON;
    }

    public static void wireUdp(UdpCastTransport udp, StreamProtectionEngine engine) {
        if (udp == null || engine == null) {
            return;
        }
        udp.setProtectionEngine(engine);
    }
}
