#pragma once
#include "face_utils.hpp"

#include <iostream>

// ── FaceAlignerFunc ───────────────────────────────────────────────────────────
/// TRACES: AR-005, AR-030 | SR-002
///
// KPN node: applies a 5-point similarity transform to each detected face,
// producing a 112×112 BGR crop suitable for ArcFace inference.
//
// Alignment is an Umeyama least-squares fit over all five landmarks (AR-005),
// not a robust one: a RANSAC fit discards the very landmarks AR-030 reads.
// Degenerate detections (where the fit fails) are dropped from the output
// vectors. The fit's residual is the AR-030 visibility measure and comes free,
// since the warp needs the transform anyway.

struct FaceAlignerFunc {
    static constexpr std::string_view label() { return "face_aligner"; }

    AlignedSceneFrame operator()(SceneFrame sf) {
        if (sf.source.eof || sf.faces.empty())
            return {std::move(sf.source), {}, {}};

        std::vector<DetectedFace> good_faces;
        std::vector<cv::Mat>      crops;
        good_faces.reserve(sf.faces.size());
        crops.reserve(sf.faces.size());

        for (auto& face : sf.faces) {
            // The AR-030 misfit comes from the transform the warp already needs,
            // so visibility costs no extra fit.
            float residual = -1.f;
            cv::Mat crop = align_face(sf.source.image, face.landmarks, &residual);
            if (crop.empty()) {
                std::cerr << "[face_aligner] degenerate detection skipped\n";
                continue;
            }
            face.alignment_residual = residual;
            good_faces.push_back(face);
            crops.push_back(std::move(crop));
        }

        return {std::move(sf.source), std::move(good_faces), std::move(crops)};
    }

};
