#include "nail_led_aiw_adapter.h"

#include <string.h>

bool nlm_aiw_mapper_init(nlm_aiw_mapper_context *context,
                         const nlm_profile *profile,
                         const nlm_policy *policy,
                         uint32_t expected_calibration_id)
{
    if (context == NULL || profile == NULL || policy == NULL ||
        expected_calibration_id == 0u || !profile->calibrated ||
        profile->channels == 0u || profile->channels > AIW_MAX_CHANNELS ||
        profile->optical_calibration_id != expected_calibration_id ||
        policy->optical_calibration_id != expected_calibration_id ||
        profile->irradiance_weights == NULL || profile->max_pwm == NULL) {
        return false;
    }
    *context = (nlm_aiw_mapper_context){
        .profile = profile,
        .policy = policy,
        .expected_calibration_id = expected_calibration_id,
        .last_accepted_frame_seq = 0u
    };
    return true;
}

bool nlm_aiw_mapper_infer(void *opaque_context, const aiw_soc_capture *capture,
                          uint16_t channels, aiw_soc_inference *inference)
{
    if (inference != NULL) memset(inference, 0, sizeof(*inference));
    if (opaque_context == NULL || capture == NULL || inference == NULL ||
        capture->frame_handle == NULL || !capture->exposure_started_after_request) {
        return false;
    }

    nlm_aiw_mapper_context *context = opaque_context;
    if (context->profile == NULL || context->policy == NULL ||
        context->profile->channels != channels ||
        context->expected_calibration_id == 0u ||
        context->profile->optical_calibration_id != context->expected_calibration_id ||
        context->policy->optical_calibration_id != context->expected_calibration_id) {
        return false;
    }

    const nlm_aiw_model_frame *model = capture->frame_handle;
    if (model->magic != NLM_AIW_MODEL_FRAME_MAGIC ||
        model->version != NLM_AIW_MODEL_FRAME_VERSION ||
        model->optical_calibration_id != context->expected_calibration_id ||
        model->source_frame_seq != capture->source_frame_seq ||
        capture->source_frame_seq <= context->last_accepted_frame_seq) {
        return false;
    }

    const nlm_frame frame = {
        .optical_calibration_id = model->optical_calibration_id,
        .frame_seq = capture->source_frame_seq,
        .capture_baseline_seq = capture->capture_baseline_seq,
        .min_source_frame_seq = 1u, /* The peer already checks the ticket floor. */
        .last_accepted_frame_seq = context->last_accepted_frame_seq,
        .exposure_started_after_request = capture->exposure_started_after_request,
        .model_valid = model->model_valid,
        .hand_present = model->hand_present,
        .model_confidence = model->model_confidence,
        .nail_probability = model->nail_probability
    };
    nlm_result mapped;
    const nlm_status status = nlm_map_frame(context->profile, context->policy,
                                            &frame, &mapped);
    /* A fresh frame is consumed even if model inference fails or no hand is
     * present; it must never be reused on the next ticket. */
    context->last_accepted_frame_seq = capture->source_frame_seq;
    if (status == NLM_NO_HAND) return true;
    if (status != NLM_OK) return false;

    inference->hand_present = true;
    for (uint16_t i = 0u; i < channels; ++i) {
        inference->nail_channel_mask[i] = mapped.channel_mask[i];
        inference->pwm[i] = mapped.pwm[i];
    }
    return true;
}
