#ifndef LAMP_GUARD_H
#define LAMP_GUARD_H

#include <stdbool.h>
#include <stdint.h>

/* Portable control policy only; target drivers, UART byte-stream framing and AI model are separate. */
#define LG_MAX_CHANNELS 122u
#define LG_AI_MAX_RUN_MS 90000u
#define LG_MANUAL_MAX_RUN_MS 120000u

typedef enum { LG_OFF, LG_AI, LG_MANUAL, LG_FAULT } lg_mode;

/* Independent sampled safety inputs. Vision and independent, validated infrared presence must both permit light. */
typedef struct {
    uint16_t channels;
    uint32_t max_run_ms;
    uint32_t ticket_lifetime_ms;
    uint32_t max_poll_gap_ms;
    /* Worst-case INPUT power in mW at full duty, from board calibration.
       Include conversion losses. This is not LED optical power. */
    uint32_t channel_input_mw[LG_MAX_CHANNELS];
    uint32_t fixed_input_mw;
    uint32_t input_budget_mw;
    uint32_t calibration_id;
} lg_config;

typedef struct {
    bool temperature_ok;
    bool supply_ok;
    bool led_driver_ok;
    bool hardware_interlock_ok;
    bool infrared_hand_present; /* Physical/debounced IR input, never copied from AI_RESULT. */
} lg_inputs;

typedef struct {
    uint64_t session_id;
    uint32_t ticket_id;
    uint64_t min_source_frame_seq; /* Ticket only: camera must capture at/after this sequence. */
    uint64_t capture_baseline_seq; /* Result only: latest sensor sequence observed on ticket receipt. */
    uint64_t source_frame_seq; /* Result only: strictly increasing actual sensor capture sequence. */
    bool hand_present; /* Result only: classifier found a hand in this fresh source frame. */
    uint32_t calibration_id;
    uint16_t channels;
    uint8_t pwm[LG_MAX_CHANNELS];
} lg_frame;

typedef struct {
    lg_config cfg;
    lg_mode mode;
    lg_inputs inputs;
    bool hand_present; /* Latest accepted fresh vision status; reset defaults absent. */
    bool vision_status_seen;
    uint32_t vision_status_ms;
    bool configured;
    bool armed;
    bool output_enable;
    bool pending_ticket;
    bool frame_active;
    uint64_t session_id;
    uint32_t ticket_counter;
    uint64_t ticket_min_source_frame_seq;
    uint64_t last_source_frame_seq;
    bool source_frame_seen;
    uint32_t ticket_issued_ms;
    uint32_t frame_issued_ms;
    uint32_t run_started_ms;
    uint32_t run_duration_ms;
    uint32_t last_poll_ms;
    uint8_t pwm[LG_MAX_CHANNELS];
} lg_guard;

/* Session must be unique for each boot/reinitialization. A caller-generated
   random/non-repeating value is required; this module cannot provide entropy.
   All calls for one guard must be serialized on one execution context. */
bool lg_init(lg_guard *g, const lg_config *cfg, uint64_t session_id,
             uint32_t now_ms);
void lg_poll(lg_guard *g, uint32_t now_ms, lg_inputs inputs);
bool lg_arm(lg_guard *g, lg_mode mode, uint32_t duration_ms, uint32_t now_ms);
/* Read-only display data; callers must keep servicing lg_poll for safety. */
uint32_t lg_remaining_ms(const lg_guard *g, uint32_t now_ms);
void lg_stop(lg_guard *g);
/* AI and manual sessions both request fresh vision tickets; manual results update presence only. */
bool lg_issue_ticket(lg_guard *g, uint32_t now_ms, lg_frame *ticket);
bool lg_accept_ai(lg_guard *g, const lg_frame *frame, uint32_t now_ms);
bool lg_set_manual(lg_guard *g, const uint8_t *pwm, uint16_t count,
                   uint32_t now_ms);

#endif
