/// @file platforms/stub/stub_gpio.h
/// @brief Stub GPIO state tracking for native/host platform simulation
///
/// This module provides:
/// 1. Pin state tracking (replaces no-op fl::platforms::digitalWrite/digitalRead)
/// 2. Edge timing simulation for WS2812 protocol output
/// 3. Per-pin edge callbacks: any code (e.g. NativeRxDevice) can register a callback
///    for a specific pin to receive edge events fired by simulateWS2812Output().
///    This simulates a physical jumper wire connecting TX output to RX input.
///
/// Architecture:
/// - fl::platforms::digitalWrite() (pin_stub.hpp) calls setPinState()
/// - fl::platforms::digitalRead() (pin_stub.hpp) calls getPinState()
/// - Channel engine stub calls simulateWS2812Output() to fire pin callbacks
/// - NativeRxDevice registers a lambda callback on its pin in begin()

#pragma once

// IWYU pragma: private

#include "fl/stl/stdint.h"
#include "fl/stl/span.h"
#include "fl/stl/function.h"
#include "fl/chipsets/chipset_timing_config.h"
#include "fl/channels/rx.h"  // for fl::EdgeTime
#include "fl/stl/noexcept.h"

namespace fl {
namespace stub {

/// @brief Set digital pin state (called from fl::platforms::digitalWrite)
/// Records the state change and appends edge to any armed buffer for this pin.
/// @param pin Pin number
/// @param high true = HIGH, false = LOW
void setPinState(int pin, bool high) FL_NOEXCEPT;

/// @brief Get current digital pin state (called from fl::platforms::digitalRead)
/// @param pin Pin number
/// @return true = HIGH, false = LOW
bool getPinState(int pin) FL_NOEXCEPT;

/// @brief Arm edge capture for a pin
/// After arming, subsequent setPinState() calls for this pin will append EdgeTime entries.
/// Clears any previously recorded edges for this pin.
/// @param pin Pin number to arm
void armPinEdges(int pin) FL_NOEXCEPT;

/// @brief Disarm and clear edge capture for a pin
/// @param pin Pin number
void clearPinEdges(int pin) FL_NOEXCEPT;

/// @brief Get the number of edges recorded for a pin
/// @param pin Pin number
/// @return Number of EdgeTime entries recorded
size_t getEdgeCount(int pin) FL_NOEXCEPT;

/// @brief Get a specific edge entry for a pin
/// @param pin Pin number
/// @param index Edge index (0-based)
/// @return EdgeTime entry, or EdgeTime() if out of range
fl::EdgeTime getEdge(int pin, size_t index) FL_NOEXCEPT;

/// @brief Simulate WS2812 GPIO output for a pin, recording edges
///
/// Generates the WS2812 bit stream from raw LED bytes, calling setPinState()
/// for each HIGH/LOW transition with virtual timing. This fills the edge buffer
/// for the pin so NativeRxDevice can decode it.
///
/// Edge format (per bit):
///   Bit 0: EdgeTime(high=true, ns=T1) + EdgeTime(high=false, ns=T2+T3)
///   Bit 1: EdgeTime(high=true, ns=T1+T2) + EdgeTime(high=false, ns=T3)
/// After all bytes: no reset pulse added (caller can let it time out)
///
/// @param pin GPIO pin number
/// @param data Raw LED byte data (R, G, B, R, G, B, ...) from writeWS2812()
/// @param timing Chipset timing configuration
void simulateWS2812Output(int pin,
                           fl::span<const u8> data,
                           const fl::ChipsetTimingConfig& timing) FL_NOEXCEPT;

/// @brief Inject raw EdgeTime entries into a pin's edge buffer
/// Appends edges to the buffer. The pin must be armed via armPinEdges() first.
/// @param pin GPIO pin number
/// @param edges Span of EdgeTime entries to inject
void injectEdges(int pin, fl::span<const fl::EdgeTime> edges) FL_NOEXCEPT;

// ============================================================================
// Per-pin edge callbacks (simulates GPIO loopback wire from TX to RX)
// ============================================================================

/// @brief Per-pin edge callback function type
///
/// Called for each simulated GPIO edge on a specific pin.
/// @param high   true = rising edge (pin goes HIGH), false = falling edge (pin goes LOW)
/// @param duration_ns Duration of this level in nanoseconds
using PinEdgeCallback = fl::function<void(bool high, u32 duration_ns)>;

/// @brief Register a callback for simulated edges on a specific pin
///
/// Only one callback per pin at a time (replaces any existing one).
/// The callback is called for each edge generated by simulateWS2812Output()
/// on the specified pin. When NativeRxDevice is destroyed, it must call
/// clearPinEdgeCallback() to release the callback.
///
/// @param pin GPIO pin number
/// @param cb Callback function (use fl::function for value semantics)
void setPinEdgeCallback(int pin, PinEdgeCallback cb) FL_NOEXCEPT;

/// @brief Clear the edge callback for a pin
///
/// Safe to call even if no callback is set for the pin.
/// @param pin GPIO pin number
void clearPinEdgeCallback(int pin) FL_NOEXCEPT;

}  // namespace stub
}  // namespace fl
