package com.foxx.androidcast.network.transport.fec;

/*********************************************************************
 * FecShardReassembly.java
 * Created at: Mon 18 May 2026 14:25:02 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class FecShardReassembly
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 93 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	029547f18662efca05cb784e7216010b48c90198fc5770d1e6952f5734b39331
**********************************************************************/
import java.util.Arrays;

/** Collects per-datagram FEC shards until Reed–Solomon decode succeeds. */
public final class FecShardReassembly {
    private final int groupId;
    private final byte mode;
    private final int originalLen;
    private final int dataShards;
    private final int parityShards;
    private final int totalShards;
    private final int shardSize;
    private final int baseMessageId;
    private final long startedMs;
    private final byte[][] shards;
    private int received;

    public FecShardReassembly(FecShardWire.Parsed first) {
        groupId = first.groupId;
        mode = first.mode;
        originalLen = first.originalLen;
        dataShards = first.dataShards;
        parityShards = first.parityShards;
        totalShards = first.totalShards;
        shardSize = first.shardData.length;
        baseMessageId = first.baseMessageId;
        startedMs = System.currentTimeMillis();
        shards = new byte[totalShards][];
    }

    public int getGroupId() {
        return groupId;
    }

    public long getStartedMs() {
        return startedMs;
    }

    public int getBaseMessageId() {
        return baseMessageId;
    }

    public int getTotalShards() {
        return totalShards;
    }

    /** @return true if this shard was new */
    public boolean addShard(FecShardWire.Parsed shard) {
        if (shard.groupId != groupId || shard.originalLen != originalLen
                || shard.dataShards != dataShards || shard.parityShards != parityShards
                || shard.baseMessageId != baseMessageId) {
            return false;
        }
        if (shard.shardData.length != shardSize) {
            return false;
        }
        int idx = shard.shardIndex;
        if (idx < 0 || idx >= totalShards || shards[idx] != null) {
            return false;
        }
        shards[idx] = Arrays.copyOf(shard.shardData, shard.shardData.length);
        received++;
        return true;
    }

    public int receivedCount() {
        return received;
    }

    public boolean hasShard(int index) {
        return index >= 0 && index < totalShards && shards[index] != null;
    }

    public int nackMessageIdForIndex(int shardIndex) {
        return baseMessageId + shardIndex;
    }

    public byte[] tryDecode() {
        if (received < dataShards) {
            return null;
        }
        ReedSolomonFec fec = new ReedSolomonFec(dataShards, parityShards);
        return fec.decode(shards, originalLen);
    }
}
