#pragma once
// Explicitly supported decode policy, NOT a claim about the unseen Awiros export.
// Codex must prove parity with the pinned official Python postprocessor before
// binding CreateContextraCrowdProcessor. COCO/Fire factories remain untouched.
#include <array>
#include <cmath>
#include <stdexcept>
#include "crowd_contract.hpp"

namespace contextra {
struct CrowdBox {
    int classId;
    float score;
    std::array<float,4> xyxy;  // normalized SOURCE_FRAME after stretch preprocessing
};
inline float crowdSigmoid(float x) {
    if (x >= 0) return 1.f / (1.f + std::exp(-x));
    const float e=std::exp(x); return e/(1.f+e);
}
inline std::vector<CrowdBox> decodeCrowdFrame(const CrowdRawContract& c,
        const std::vector<float>& boxes, const std::vector<float>& logits) {
    if (!validateCrowdDeclaration(c) || boxes.size() != static_cast<size_t>(c.queries)*4
        || logits.size() != static_cast<size_t>(c.queries)*c.logits)
        throw std::invalid_argument("CROWD_UNCONFIRMED_OR_MISMATCHED_CONTRACT");
    struct Candidate { float score; size_t index; };
    std::vector<Candidate> ranked;
    ranked.reserve(logits.size());
    for (size_t i=0; i<logits.size(); ++i)
        if (std::isfinite(logits[i])) ranked.push_back({crowdSigmoid(logits[i]),i});
    const size_t k=std::min<size_t>(300,ranked.size());
    std::partial_sort(ranked.begin(),ranked.begin()+k,ranked.end(),[](auto a,auto b) {
        return a.score != b.score ? a.score > b.score : a.index < b.index;
    });
    std::vector<CrowdBox> out;
    out.reserve(k);
    for (size_t i=0; i<k; ++i) {
        const auto r=ranked[i];
        const int cls=crowdCanonicalClass(c,static_cast<uint32_t>(r.index%c.logits));
        if (cls < 0) continue;
        const size_t q=(r.index/c.logits)*4;
        const float cx=boxes[q],cy=boxes[q+1],w=boxes[q+2],h=boxes[q+3];
        if (!std::isfinite(cx)||!std::isfinite(cy)||!std::isfinite(w)||!std::isfinite(h)
            || w <= 0 || h <= 0) continue;
        const auto clip=[](double v){return static_cast<float>(std::clamp(v,0.,1.));};
        const std::array<float,4> b={clip(double(cx)-double(w)/2),clip(double(cy)-double(h)/2),
                                    clip(double(cx)+double(w)/2),clip(double(cy)+double(h)/2)};
        if (b[2] <= b[0] || b[3] <= b[1]) continue;
        out.push_back({cls,r.score,b});
    }
    return out; // Inclusive confidence, class-aware NMS and ROI belong to Python.
}
}  // namespace contextra
