#include "lamp_uart.h"
#include "lamp_adc.h"
#include "lamp_measurement.h"
#include "lamp_board_inputs.h"
#include "lamp_guard.h"
#include "lamp_guard_wire_adapter.h"
#include "lamp_ticket_dispatch.h"
#include "lamp_ui_wire_service.h"
#include "aiw_stream.h"
#include "stm32g0xx.h"
#include "system_stm32g0xx.h"

#include <stdint.h>

/* Pin map:硬件/lamp_safety/pinmap.json. Product calibration and measured
 * LED-current profile are absent, so all physical LED controls remain off. */
static aiw_stream rx_stream;
static lamp_ui_wire_service ui_service;
/* Deliberately never configured or armed until SoC tickets, calibration and
 * measured board limits are implemented. Thus every result remains rejected. */
static lg_guard rx_guard;
static volatile uint32_t system_tick_ms;
volatile uint32_t lamp_debug_crc_valid_frames;
volatile uint32_t lamp_debug_last_ticket_id;
volatile uint32_t lamp_debug_uart_dropped_bytes;
volatile uint32_t lamp_debug_uart_hardware_errors;
volatile uint32_t lamp_debug_guard_rejections;
volatile uint32_t lamp_debug_ticket_requests_queued;
volatile uint32_t lamp_debug_ticket_request_failures;
volatile uint32_t lamp_debug_ui_session_opens;
volatile uint32_t lamp_debug_ui_commands;
volatile uint32_t lamp_debug_ui_acks_queued;
volatile uint32_t lamp_debug_ui_ack_failures;
volatile uint32_t lamp_debug_raw_input_bits;
volatile uint32_t lamp_debug_ir_init_ok;

static uint16_t factory_vrefint_cal;
volatile lamp_measurement lamp_debug_measurement;
volatile uint32_t lamp_debug_measurement_valid, lamp_debug_measurement_status;
volatile uint32_t lamp_debug_factory_vrefint_cal;
volatile lamp_adc_frame lamp_debug_adc_frame;
volatile uint32_t lamp_debug_adc_valid, lamp_debug_adc_failed;

static void sample_and_poll_inputs(void) {
    const uint32_t now=system_tick_ms;
    lamp_adc_poll(now);
    lamp_adc_frame raw;
    const bool available=lamp_adc_get_frame(now,&raw);
    lamp_debug_adc_valid=available ? 1u : 0u;
    lamp_debug_adc_failed=lamp_adc_failed() ? 1u : 0u;
    lamp_debug_measurement_valid=0u;
    if(available) {
        lamp_debug_adc_frame=raw;
        lamp_measurement measured;
        const enum lamp_measurement_status status=lamp_measurement_from_frame(
            &raw,factory_vrefint_cal,now,&measured);
        lamp_debug_measurement_status=(uint32_t)status;
        if(status==LAMP_MEAS_OK) {
            lamp_debug_measurement=measured;
            lamp_debug_measurement_valid=1u;
        }
    } else lamp_debug_measurement_status=LAMP_MEAS_STALE;
    const lamp_board_input_snapshot sample = lamp_board_inputs_read(system_tick_ms);
    lamp_debug_raw_input_bits = (sample.thermal_contact_closed ? 1u : 0u) |
                               (sample.hardware_interlock_closed ? 2u : 0u) |
                               (sample.user_key_pressed ? 4u : 0u) |
                               (sample.infrared_gpio_available ? 8u : 0u) |
                               (sample.infrared_raw_high ? 16u : 0u) |
                               (sample.guard_inputs.infrared_hand_present ? 32u : 0u);
    lg_poll(&rx_guard, system_tick_ms, sample.guard_inputs);
}

#define LAMP_TICKET_REQUEST_INTERVAL_MS 100u

static bool lamp_send_ticket(void *context, const uint8_t *packet, size_t length) {
    return lamp_uart_tx_enqueue(context, packet, length);
}

static void gpio_drive(GPIO_TypeDef *port, uint32_t pin, uint32_t high) {
    const uint32_t shift = pin * 2u;
    port->BSRR = high ? (1u << pin) : (1u << (pin + 16u));
    port->OTYPER &= ~(1u << pin);
    port->OSPEEDR &= ~(3u << shift);
    port->PUPDR &= ~(3u << shift);
    port->MODER = (port->MODER & ~(3u << shift)) | (1u << shift);
}

static void gpio_input(GPIO_TypeDef *port, uint32_t pin, uint32_t pull_up) {
    const uint32_t shift = pin * 2u;
    port->MODER &= ~(3u << shift);
    port->PUPDR = (port->PUPDR & ~(3u << shift)) |
                  ((pull_up ? 1u : 2u) << shift);
}

static void board_safe_boot(void) {
    RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;
    (void)RCC->IOPENR;
    RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN;
    (void)RCC->APBENR2;

    /* Set output latches before changing pin modes to avoid an active pulse. */
    GPIOA->BSRR = (1u << (4u + 16u)) | (1u << 7u) | (1u << (9u + 16u)) |
                  (1u << (11u + 16u));
    GPIOB->BSRR = (1u << (9u + 16u));
    __DSB();

    gpio_drive(GPIOA, 4u, 0u);  /* LED_PWR_REQ_MCU: power gate disabled. */
    gpio_drive(GPIOA, 7u, 1u);  /* LED_OE_N_MCU: outputs blanked. */
    /* Stage both pin16 aliases low before restoring the default mapping.
     * DS12991 Rev6 Table12: package pin16=PA11, pin17=PA12.
     * PA9/PA10 reach these pads only with explicit SYSCFG remapping. */
    gpio_drive(GPIOA, 9u, 0u);
    gpio_drive(GPIOA, 11u, 0u); /* WDOG_KICK: no external watchdog kick. */
    gpio_input(GPIOA, 10u, 0u);
    gpio_input(GPIOA, 12u, 0u); /* THERM_OK: open defaults unsafe. */
    SYSCFG->CFGR1 &= ~(SYSCFG_CFGR1_PA11_RMP | SYSCFG_CFGR1_PA12_RMP);
    __DSB();
    gpio_drive(GPIOB, 9u, 0u);  /* LED_RESET_N_MCU: driver held in reset. */

    gpio_input(GPIOA, 0u, 0u);  /* INTERLOCK_OK: open defaults unsafe. */

    gpio_input(GPIOB, 0u, 1u);  /* USER_KEY_N: sampled only after validation. */
}

void SysTick_Handler(void) {
    ++system_tick_ms;
}

int main(void) {
    board_safe_boot();
    lamp_debug_ir_init_ok=lamp_board_ir_init() ? 1u : 0u;
    if (!aiw_stream_init(&rx_stream, AIW_STREAM_DEFAULT_TIMEOUT_MS)) {
        for (;;) __WFI();
    }
    if (!lamp_ui_wire_service_init(&ui_service, &rx_guard)) {
        for (;;) __WFI();
    }
    if (!lamp_uart_init(SystemCoreClock, 460800u) ||
        SysTick_Config(SystemCoreClock / 1000u) != 0u) {
        for (;;) __WFI();
    }

    factory_vrefint_cal=lamp_read_factory_vrefint_cal();
    lamp_debug_factory_vrefint_cal=factory_vrefint_cal;
    lamp_adc_init(system_tick_ms);
    lamp_uart_stats stats;
    lamp_uart_get_stats(&stats);
    uint32_t seen_drops = stats.dropped_bytes;
    uint32_t seen_errors = stats.hardware_errors;
    uint32_t seen_tx_drops = stats.dropped_packets;
    uint32_t next_ticket_request_ms = system_tick_ms;

    for (;;) {
        sample_and_poll_inputs();
        lamp_uart_get_stats(&stats);
        if (stats.dropped_bytes != seen_drops ||
            stats.hardware_errors != seen_errors ||
            stats.dropped_packets != seen_tx_drops) {
            aiw_stream_abort(&rx_stream);
            seen_drops = stats.dropped_bytes;
            seen_errors = stats.hardware_errors;
            seen_tx_drops = stats.dropped_packets;
        }
        lamp_debug_uart_dropped_bytes = stats.dropped_bytes;
        lamp_debug_uart_hardware_errors = stats.hardware_errors;

        uint8_t byte;
        while (lamp_uart_rx_read(&byte)) {
            aiw_message message;
            if (aiw_stream_push_message(&rx_stream, byte, system_tick_ms, &message)) {
                sample_and_poll_inputs(); /* Re-sample before accepting any command/result. */
                ++lamp_debug_crc_valid_frames;
                if (message.type == AIW_MESSAGE_RESULT) {
                    const aiw_result *result = &message.value.result;
                    lamp_debug_last_ticket_id = result->ticket_id;
                    if (!lg_accept_ai_decoded_result(&rx_guard, result,
                                                     system_tick_ms)) {
                        lg_stop(&rx_guard);
                        ++lamp_debug_guard_rejections;
                    }
                    /* The guard intentionally has no session/config/ticket source;
                       no output adapter, power request or watchdog kick is bound. */
                } else if (message.type == AIW_MESSAGE_UI_SESSION_OPEN ||
                           message.type == AIW_MESSAGE_UI_COMMAND) {
                    aiw_ui_ack ack;
                    uint8_t packet[AIW_MAX_PACKET_BYTES];
                    size_t packet_length = 0u;
                    if (message.type == AIW_MESSAGE_UI_SESSION_OPEN) {
                        ++lamp_debug_ui_session_opens;
                    } else {
                        ++lamp_debug_ui_commands;
                    }
                    if (lamp_ui_wire_service_handle(&ui_service, &message,
                                                   system_tick_ms, &ack) &&
                        aiw_encode_ui_ack(&ack, packet, sizeof(packet),
                                          &packet_length) &&
                        lamp_uart_tx_enqueue(NULL, packet, packet_length)) {
                        ++lamp_debug_ui_acks_queued;
                    } else {
                        /* A missing ACK leaves the SoC uncertain; force the
                           controller policy safe before any subsequent command. */
                        lg_stop(&rx_guard);
                        ++lamp_debug_ui_ack_failures;
                    }
                }
            }
        }
        if ((int32_t)(system_tick_ms - next_ticket_request_ms) >= 0) {
            next_ticket_request_ms = system_tick_ms + LAMP_TICKET_REQUEST_INTERVAL_MS;
            const lamp_ticket_dispatch_status dispatch =
                lamp_ticket_dispatch_issue(&rx_guard, system_tick_ms,
                                           lamp_send_ticket, NULL);
            if (dispatch == LAMP_TICKET_DISPATCH_QUEUED) {
                ++lamp_debug_ticket_requests_queued;
            } else if (dispatch == LAMP_TICKET_DISPATCH_SEND_FAILED ||
                       dispatch == LAMP_TICKET_DISPATCH_INVALID_ARGUMENT) {
                ++lamp_debug_ticket_request_failures;
            }
        }
        __WFI();
    }
}
