// Validation : mon cp_inference (intégré dans tts_orchestrate) doit reproduire
// cp_runner standalone bit-exact. Sur test_cp_input.bin (33 frames hidden+CB0
// du Talker PyTorch) → comparer codes CB1..15 vs test_codes_greedy.bin
// (golden cp.generate do_sample=False). Cible : 33/33 frames parfaits.
#include "cp_inference.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <vector>
#include <string>

int main(int argc, char** argv) {
    if (argc < 6) {
        printf("usage: %s <cp_gguf> <cp_heads.bin> <cp_codec_embs.bin> <test_cp_input.bin> <golden.bin>\n", argv[0]);
        return 1;
    }
    CPState s; s.n_threads = 4;
    if (!cp_load(s, argv[1], argv[2], argv[3])) { printf("cp_load FAILED\n"); return 1; }

    // input : int32 T, T * (hidden[1024] + cb0_emb[1024]) f32
    FILE* fi = fopen(argv[4], "rb"); if (!fi) { printf("input fail\n"); return 1; }
    int32_t T = 0; if (fread(&T, 4, 1, fi) != 1) return 1;
    std::vector<float> inbuf((size_t)T * 2 * 1024);
    if (fread(inbuf.data(), sizeof(float), inbuf.size(), fi) != inbuf.size()) return 1;
    fclose(fi);

    // golden : pour test_codes_greedy.bin pas de header (T * 16 int32 direct, 33 * 16 * 4 = 2112 bytes ≈ 2116)
    FILE* fg = fopen(argv[5], "rb"); if (!fg) return 1;
    fseek(fg, 0, SEEK_END); long gsz = ftell(fg); fseek(fg, 0, SEEK_SET);
    // 2 layouts possibles : (a) header int32+T*16 int32 (size = 4 + T*64),
    //                       (b) raw T*16 int32 (size = T*64). On déduit.
    bool has_hdr = (gsz == 4 + (long)T * 64);
    if (has_hdr) { int32_t ng = 0; fread(&ng, 4, 1, fg); printf("golden has header, ng=%d\n", ng); }
    std::vector<int32_t> gold((size_t)T * 16);
    size_t expect = T * 16;
    if (fread(gold.data(), sizeof(int32_t), expect, fg) != expect) {
        // Maybe golden has fewer or different format
        printf("golden short read; size %ld, expected %zu*4=%zu bytes (T=%d)\n", gsz, expect, expect*4, T);
        fclose(fg); return 1;
    }
    fclose(fg);

    printf("running CP on %d frames...\n", T);
    long n_match_cb15 = 0, total_codes_cb15 = 0;
    int n_perfect_cb15 = 0;
    for (int f = 0; f < T; f++) {
        const float* hidden  = &inbuf[(size_t)f * 2 * 1024];
        const float* cb0_emb = &inbuf[(size_t)f * 2 * 1024 + 1024];
        int32_t codes15[15];
        cp_predict(s, hidden, cb0_emb, codes15);

        // golden format = [CB0 ... CB15] (16 entries per frame)
        const int32_t* gf16 = &gold[(size_t)f * 16];
        int match = 0;
        for (int c = 0; c < 15; c++) if (codes15[c] == gf16[1 + c]) match++;
        n_match_cb15 += match; total_codes_cb15 += 15;
        if (match == 15) n_perfect_cb15++;
        if (f < 3 || match < 15) {
            printf("frame %2d: CB1..15 match %2d/15 -> CP=[%d,%d,%d,%d] PY=[%d,%d,%d,%d]\n",
                   f, match, codes15[0], codes15[1], codes15[2], codes15[3],
                   gf16[1], gf16[2], gf16[3], gf16[4]);
        }
    }
    printf("\n=== cp_predict (engine) vs cp.generate greedy (golden) ===\n");
    printf("codes match : %ld/%ld (%.1f%%)\n", n_match_cb15, total_codes_cb15, 100.0 * n_match_cb15 / total_codes_cb15);
    printf("frames parfaits : %d/%d\n", n_perfect_cb15, T);
    cp_free(s);
    return (n_match_cb15 == total_codes_cb15) ? 0 : 5;
}
