/*
 * SPDX-License-Identifier: LicenseRef-TraxProbe-Commercial
 * Copyright (c) 2026 Embedya. All rights reserved.
 *
 * TraxProbe core engine. CONFIDENTIAL and proprietary to Embedya.
 * Licensed under the TraxProbe Commercial License (see LICENSE-COMMERCIAL.txt).
 * No use, copying, modification, redistribution, decompilation, or reverse
 * engineering is permitted except as expressly authorized by that agreement.
 */

/**
******************************************************************************
* @file           : trax_meta_type.h
* @date        	  : Jan 18, 2026
* @author         : bemre
* @version        : TODO
*
* @brief          : TODO
******************************************************************************
* @attention
* 	TODO
******************************************************************************
* @code {.c}
*      ...
* 	TODO
*      ...
* @endcode
*/

#ifndef TRAX_META_TYPE_H_
#define TRAX_META_TYPE_H_

#include <trax_data_types.h>
#include "trax_config_default.h"  /* For TRAX_CFG_META_*_LEN */

/*=============================================================================
 ====================GLOBAL MACRO DEFINITIONS==================================
 ============================================================================*/

/*=============================================================================
 ====================GLOBAL MACRO FUNCTIONS====================================
 ============================================================================*/

/*=============================================================================
 ====================GLOBAL TYPEDEF============================================
 ============================================================================*/

/*=============================================================================
 ====================UTILITY TYPES=============================================
 ============================================================================*/

/**
 * @brief Rational value for exact representation of fractional values.
 *
 * 64-bit numerator and denominator give head-room for sample rates well
 * past the 32-bit ~4.29 GHz ceiling (e.g. 16/32/65 GS/s high-speed ADCs)
 * and for sub-sample time offsets at fine fractional resolutions, with no
 * runtime cost — the value is interpreted as a `double` once at metadata
 * load time.
 */
struct trax_rational_t {
    uint64_t numerator;
    uint64_t denominator;
} TRAX_PACKED;

/**
 * @brief Construct a rational number { numerator, denominator }
 */
#define TRAX_RATIONAL(_num, _denom) { .numerator = (_num), .denominator = (_denom) }

/*=============================================================================
 ====================LOG METADATA==============================================
 ============================================================================*/

/**
 * @brief Log metadata (auto-generated at each TRAX_LOG_xxx call site)
 *
 * Stored in .trax_log ELF section (flash or NOLOAD depending on config).
 * Auto-generated by TRAX_LOG_INFO, TRAX_LOG_ERROR, etc. macros.
 * Contains format string, log level, and source location for each log call site.
 *
 * Key fields:
 *   - level: Log severity (0=EMERGENCY to 8=TRACE), determined at compile time
 *   - flags: Bit flags (TRAX_LOG_FLAG_HEX for hex dumps)
 *
 * @note Multiple entries may exist for the same TID if TRAX_LOG_xxx is called
 *       from different locations. Scope should use the first matching entry.
 */
struct trax_log_meta_t {
    uint16_t id;                                    /**< Trace ID (TID) - base TID only */
    uint16_t reserved1;                             /**< Reserved */
    uint8_t arg_count;                              /**< Number of format arguments */
    uint8_t level;                                  /**< Log level (0-8, compile-time) */
    uint8_t flags;                                  /**< Flags (TRAX_LOG_FLAG_xxx) */
    uint8_t reserved2;                              /**< Reserved for alignment */
    char p_tag[TRAX_CFG_META_TAG_LEN];                 /**< Filter tag (shown in Context column, used for log filtering) */
    char p_format[TRAX_CFG_META_FORMAT_LEN];          /**< Printf format string */
} TRAX_PACKED;

/**
 * @brief Log variable bindings metadata (optional, user-defined)
 *
 * Stored in .trax_log_vars ELF section.
 * Defined via TRAX_LOG_DEFINE_VARS macro.
 * Maps log arguments to variable (VAR) TIDs for data linking.
 * Use 0 for arguments that are NOT variables (preserves position).
 *
 * Usage:
 *   // arg[0]=VAR_TEMP, arg[1]=plain, arg[2]=VAR_SPEED, arg[3]=plain
 *   TRAX_LOG_DEFINE_VARS(TRAX_TID_MOTOR_LOG, VAR_TEMP, 0, VAR_SPEED, 0);
 *
 * If not defined for a TID, Scope treats arguments as plain values.
 * If defined, Scope links arguments to VAR metadata (type, units, etc.).
 */
struct trax_log_vars_meta_t {
    uint16_t id;                                    /**< Trace ID (TID) */
    uint8_t arg_count;                              /**< Number of arguments */
    uint8_t reserved;                               /**< Reserved for alignment */
    uint16_t p_arg_vars[TRAX_CFG_LOG_MAX_ARGS];       /**< VAR IDs for each argument (0 = not a VAR) */
} TRAX_PACKED;

/*=============================================================================
 ====================VARIABLE METADATA=========================================
 ============================================================================*/

/**
 * @brief Variable (VAR) metadata
 *
 * Stored in .trax_var ELF section (flash or NOLOAD depending on config).
 * Uses inline strings for zero-copy transmission when TRAX_CFG_META_STORAGE == TRAX_META_IN_FLASH.
 *
 * NOTE: VARs do NOT have source location (file/func/line).
 * VARs are identified by TID only and can be bound to multiple log messages.
 *
 * Value conversion is defined separately via TRAX_VAR_FORMULA().
 *
 * min_value/max_value are `double` so the declared display range can cover
 * every VAR data type: full INT64/UINT64 spans (double is approximate past
 * 2^53, which is irrelevant for Y-axis scaling) and fractional ranges for
 * FLOAT/DOUBLE vars (e.g. -1.5..1.5). The firmware never reads these fields
 * at runtime — they are compile-time constants consumed by the host only.
 */
struct trax_var_meta_t {
    uint16_t id;                                /**< Trace ID (TID) */
    uint8_t data_type;                          /**< Data type (enum trax_data_type_t) */
    uint8_t flags;                              /**< Bits [1:0] = plot style (TRAX_PLOT_xxx), [7:2] reserved */
    double min_value;                           /**< Minimum expected value (for Y-axis scaling) */
    double max_value;                           /**< Maximum expected value (for Y-axis scaling) */
    char p_name[TRAX_CFG_META_NAME_LEN];          /**< Variable name */
    char p_unit[TRAX_CFG_META_UNIT_LEN];          /**< Unit string ("" if none) */
    uint32_t color;                             /**< 0x00RRGGBB scope color (0 = auto-assign) */
} TRAX_PACKED;

/*=============================================================================
 ====================VAR FORMULA METADATA======================================
 ============================================================================*/

/** @brief Maximum expression string length for var formulas */
#ifndef TRAX_CFG_FORMULA_EXPR_LEN
#define TRAX_CFG_FORMULA_EXPR_LEN  48
#endif

/**
 * @brief Variable formula metadata
 *
 * Stored in .trax_var_formula ELF section.
 * Multiple formulas can be defined per VAR via TRAX_VAR_FORMULA().
 * Traxcope lets the user select which formula is active at runtime.
 *
 * The expression string uses 'x' as the raw value placeholder.
 * Supported operators: +, -, *, /, ^ (power)
 * Supported functions: sqrt, sin, cos, tan, asin, acos, atan,
 *                      log (base-10), ln (natural), exp, abs, ceil, floor
 * Supported constants: pi, e
 */
struct trax_var_formula_meta_t {
    uint16_t var_id;                            /**< Which VAR TID this formula belongs to */
    uint8_t  reserved[2];                       /**< Alignment padding */
    char p_name[TRAX_CFG_META_NAME_LEN];        /**< Formula name (e.g., "Voltage", "Temp_C") */
    char p_unit[TRAX_CFG_META_UNIT_LEN];        /**< Unit when this formula is active ("V", "°C") */
    char p_expr[TRAX_CFG_FORMULA_EXPR_LEN];     /**< Expression string, 'x' = raw value */
} TRAX_PACKED;

/*=============================================================================
 ====================VAR FILTER (TRANSFER FUNCTION) METADATA==================
 ============================================================================*/

/**
 * @brief Variable filter metadata (discrete-time transfer function H(z))
 *
 * Stored in .trax_var_filter ELF section. Filters are STREAM-VAR ONLY:
 * defined for vars that produce continuous-time samples (ADC streams,
 * synthesised waveforms). Per-sample async vars get formulas, not filters.
 *
 * The filter implements a difference equation:
 *   y[n] = b[0]*x[n] + b[1]*x[n-1] + ... + b[num_b-1]*x[n-num_b+1]
 *        - a[1]*y[n-1] - a[2]*y[n-2] - ... - a[num_a-1]*y[n-num_a+1]
 *
 * Conventions:
 *   - a[0] is implicitly 1.0 (coefficients are normalised; do NOT ship a[0]).
 *     The first transmitted `a` coefficient (index 0 in the wire array) is
 *     a[1] in difference-equation notation.
 *   - num_a == 0 means an FIR filter (no feedback).
 *   - num_b >= 1 always (at least the b[0] direct term).
 *
 * Multiple filters can be defined per VAR via TRAX_VAR_FILTER().
 * Traxcope shows them in a per-channel dropdown alongside formulas; the
 * user picks at runtime, possibly with a different selection per channel.
 *
 * Wire size at default TRAX_CFG_FILTER_MAX_ORDER=12 and TRAX_CFG_META_NAME_LEN=32:
 *   2 + 1 + 1 + 32 + 13*4 + 13*4 = 140 bytes per record.
 *
 * Host promotes float -> double on ingest; the inner MAC loop runs in
 * double precision regardless of wire format.
 */
struct trax_var_filter_meta_t {
    uint16_t var_id;                                           /**< Which VAR TID this filter belongs to */
    uint8_t  num_b;                                            /**< Count of valid b coefficients (>=1, <=TRAX_CFG_FILTER_MAX_ORDER+1) */
    uint8_t  num_a;                                            /**< Count of valid a coefficients (0=FIR, else <=TRAX_CFG_FILTER_MAX_ORDER+1; a[0]==1 implicit so first stored a is a[1]) */
    char     p_name[TRAX_CFG_META_NAME_LEN];                   /**< Filter name (e.g. "10Hz LPF", "60Hz Notch") */
    float    b[TRAX_CFG_FILTER_MAX_ORDER + 1];                 /**< Numerator (input) coefficients; only first num_b entries are valid */
    float    a[TRAX_CFG_FILTER_MAX_ORDER + 1];                 /**< Denominator (output) coefficients starting at a[1]; only first num_a entries are valid */
} TRAX_PACKED;

/*=============================================================================
 ====================ISR METADATA==============================================
 ============================================================================*/

/**
 * @brief ISR metadata
 *
 * Stored in .trax_isr ELF section (flash or NOLOAD depending on config).
 * Uses inline strings for zero-copy transmission when TRAX_CFG_META_STORAGE == TRAX_META_IN_FLASH.
 */
struct trax_isr_meta_t {
    uint16_t id;                                /**< Trace ID (TID) */
    uint16_t priority;                          /**< ISR priority */
    char p_name[TRAX_CFG_META_NAME_LEN];        /**< ISR name */
} TRAX_PACKED;

/*=============================================================================
 ====================STREAM METADATA===========================================
 ============================================================================*/

/**
 * @brief Stream channel binding — links a channel to an existing VAR
 *
 * @c offset_seconds is the **absolute physical time** at which this channel
 * is sampled, measured from the sequence's nominal timestamp
 * (start_time + k × sample_period).  It is expressed
 * in seconds and is **independent of the stream sample rate** — it is
 * set by the acquisition hardware (e.g. ADC engine clock, FPGA pipeline
 * latency, channel-to-channel skew), not by 1 / @c sample_freq_hz.
 *
 * Examples
 *   - Simultaneous sampling (DDC I/Q, simultaneous-sample ADCs),
 *     negligible start latency:
 *         offset_seconds = 0.0 for every channel.
 *   - Sequential ADC scan starting T_START after the trigger,
 *     25 ADC-clock cycles per channel at 16 MHz:
 *         offset_seconds = T_START + N * 25.0 / 16.0e6   (1.5625 µs per channel)
 *     This offset stays valid even if the stream sample rate is later
 *     changed from 1 kHz to 10 kHz — the ADC engine clock did not change.
 *
 * Each offset is absolute from the sequence timestamp — NOT a delta from
 * the previous channel.  Channel 0 is typically 0.0, but may carry the
 * acquisition-start latency (trigger delay, sample-and-hold, conversion
 * time) when that matters.
 */
struct trax_stream_channel_binding_t {
    uint16_t var_id;            /**< Reference to existing VAR TID (0 = unused) */
    double   offset_seconds;    /**< Absolute physical sample-time offset from
                                 *   the sequence timestamp, in seconds.
                                 *   Independent of sample_freq_hz; derived
                                 *   from acquisition hardware clocks. */
} TRAX_PACKED;

/**
 * @brief Maximum channels per ADC_GROUP stream
 */
#define TRAX_STREAM_MAX_CHANNELS    24

/**
 * @brief Stream description length
 */
#define TRAX_STREAM_DESC_LEN    32

/**
 * @brief ADC_GROUP stream metadata
 *
 * Stored in .trax_stream_adc ELF section.
 * For grouped ADC channels with per-channel timing (e.g., power analysis).
 * Each channel references an existing VAR by ID.
 * Size: 284 bytes
 */
struct trax_stream_adc_group_meta_t {
    uint16_t id;                                /**< Stream TID */
    uint8_t  channel_count;                     /**< Number of channels (1-24) */
    uint8_t  byte_order;                        /**< TRAX_BYTE_ORDER_LITTLE / _BIG */
    struct trax_rational_t sample_freq_hz;      /**< Sample frequency (rational Hz) */
    struct trax_stream_channel_binding_t p_channels[TRAX_STREAM_MAX_CHANNELS]; /**< Channel-to-VAR bindings */
    char p_desc[TRAX_STREAM_DESC_LEN];     /**< Stream description */
    /* sequence_size_bytes is NOT stored here — it is derived at runtime
     * (on both firmware and host) by summing TRAX_DATA_TYPE_SIZE(data_type)
     * across all p_channels' bound vars.  Keeps the wire layout minimal
     * and eliminates a redundant field that must be kept in sync with the
     * channel list. */
} TRAX_PACKED;

/**
 * @brief ROTOR stream metadata — rotary-encoder position stream
 *
 * Stored in .trax_stream_rotor ELF section.
 *
 * A ROTOR stream is a single-channel position stream with explicit
 * encoder geometry: @c counts_per_rev declares how many counts (as they
 * appear in the data — i.e. after any quadrature decoding) make up ONE
 * mechanical shaft revolution.  The host derives shaft angle, RPM and
 * direction from this contract instead of inferring "one revolution"
 * from the bound VAR's min/max display range.
 *
 * The stream transport (blocks, sequence indexing, start-anchor timing)
 * is identical to the ADC group — only the metadata differs.
 * Size: 68 bytes (66-byte payload + 2-byte pad). Each TRAX_STREAM_ROTOR_DEFINE
 * is its own ELF input section; GNU ld ALIGN(4)s those fragments. A 66-byte
 * record left a 2-byte hole (often 0xFF) before the next id, so a second
 * rotor parsed as TID 0xFFFF and Traxcope rejected START_TRACE.
 */
struct trax_stream_rotor_meta_t {
    uint16_t id;                                /**< Stream TID */
    uint8_t  channel_count;                     /**< Always 1 (the position channel) */
    uint8_t  byte_order;                        /**< TRAX_BYTE_ORDER_LITTLE / _BIG */
    struct trax_rational_t sample_freq_hz;      /**< Sample frequency (rational Hz) */
    uint32_t counts_per_rev;                    /**< Encoder counts per mechanical
                                                 *   revolution as present in the
                                                 *   data stream (post-quadrature) */
    struct trax_stream_channel_binding_t p_channels[1]; /**< Position VAR binding */
    char p_desc[TRAX_STREAM_DESC_LEN];      /**< Stream description */
    uint16_t reserved;                          /**< Pad to 4-byte multiple (ELF + wire) */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_stream_rotor_meta_t) == 68,
                   "trax_stream_rotor_meta_t must be 68 bytes (bump host WireStreamRotor in lockstep)");

/**
 * @brief Stream runtime state metadata
 *
 * Dynamic metadata section: captures current state of each defined stream.
 * Updated by TRAX_STREAM_START/STOP macros. Serialized and sent in the
 * START_TRACE metadata response so the scope knows about streams that
 * were already running before connection.
 *
 * One entry per defined stream (TRAX_CFG_STREAM_CNT total).
 * Size: 12 bytes per entry.
 */
struct trax_stream_runtime_meta_t {
    uint16_t stream_id;        /**< Stream TID (e.g., TRAX_TID_STREAM_ADC = 0x3000) */
    uint8_t  is_active;        /**< 1 = running, 0 = stopped */
    uint8_t  reserved;         /**< Reserved for alignment */
    uint32_t start_tick_overflow_cntr;  /**< Tick overflow counter at stream start */
    uint32_t start_timepacked;  /**< Packed timestamp [tick][timer] at stream start */
} TRAX_PACKED;

/**
 * @brief State-machine runtime state (current state per SM)
 *
 * Sent in SESSION_START (late-join: SM lanes start from the live state,
 * not the metadata-declared initial state) and in STREAM_GAP resync
 * frames (transitions during the gate were suppressed at the streaming
 * gate; this snapshot lets the host correct each SM's current state at
 * the gap boundary instead of rendering the stale pre-gap state forever).
 *
 * current_state == TRAX_SM_STATE_UNKNOWN (0xFF) means no
 * TRAX_SM_SET_STATE has fired since boot — the host keeps its current
 * knowledge (the metadata initial state, or the last observed state).
 *
 * 4 bytes so any entry count keeps the section word-aligned.
 */
struct trax_sm_runtime_meta_t {
    uint16_t sm_index;         /**< SM index (SM TID - TRAX_TID_RANGE_SM_START) */
    uint8_t  current_state;    /**< Last state set via TRAX_SM_SET_STATE; 0xFF = unknown */
    uint8_t  reserved;         /**< Zero — future flags */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_sm_runtime_meta_t) == 4,
                   "trax_sm_runtime_meta_t must be 4 bytes (bump host WireSmRuntimeMeta in lockstep)");


/*=============================================================================
 ====================RTOS WIRE METADATA========================================
 ============================================================================*/

/** @brief Max task name length in wire format */
#define TRAX_RTOS_TASK_NAME_LEN  16

/**
 * @brief RTOS task metadata wire format (fixed-size, appended to START_TRACE)
 *
 * Each active task in the probe's task table is serialized as one of these
 * entries and appended after stream_runtime[] in the START_TRACE frame.
 * Tasks created during tracing are sent as separate TRAX_TID_TASK_CREATE frames.
 *
 * The current_stack_hwm_words, flags and overflow_* fields are part of
 * the "session-start memory snapshot" pattern: they let the host show
 * truthful per-task stack HWM, "this task already overflowed before you
 * connected", and the *exact* time at which that overflow happened —
 * immediately on a mid-flight reconnect, without waiting for any live
 * frame.  The (overflow_tick_overflow_cntr, overflow_timepacked) pair
 * uses the same encoding as start_tick_overflow_cntr/start_timepacked
 * in the session header, so the host reconstructs it through the same
 * timer-config codepath used for live frames.
 */
struct trax_rtos_task_wire_t {
    uint32_t handle;                          /**< Task handle (TCB pointer) */
    uint32_t stack_size;                      /**< Stack size in words */
    uint8_t  priority;                        /**< Current priority */
    uint16_t current_stack_hwm_words;         /**< uxTaskGetStackHighWaterMark()
                                                   at session start (0 = unknown) */
    uint8_t  p_reserved;                      /**< Padding to 4-byte align p_name */
    char     p_name[TRAX_RTOS_TASK_NAME_LEN]; /**< Task name (null-padded) */
    uint32_t flags;                           /**< TRAX_TASK_FLAG_* bits latched
                                                   since boot (e.g. OVERFLOWED) */
    uint32_t overflow_tick_overflow_cntr;     /**< trax_timebase.tick_overflow_cntr
                                                   captured the first time
                                                   vApplicationStackOverflowHook
                                                   fired for this task.  0 when
                                                   no overflow has been latched. */
    uint32_t overflow_timepacked;             /**< Packed [tick][timer] captured
                                                   alongside the cntr above.  Pair
                                                   (cntr, packed) decodes to the
                                                   exact source-time of the first
                                                   overflow.  Both are 0 when not
                                                   latched. */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_rtos_task_wire_t) == 40,
                   "trax_rtos_task_wire_t must be 40 bytes");

/** @brief Max RTOS object name length in wire format */
#define TRAX_RTOS_OBJ_NAME_LEN  16

/**
 * @brief RTOS object metadata wire format (fixed-size, appended after rtos_task[] in START_TRACE)
 *
 * Each active object in the probe's object table is serialized as one of these
 * entries. Objects created during tracing are sent as separate TRAX_TID_OBJ_CREATE frames.
 */
struct trax_rtos_object_wire_t {
    uint32_t handle;                         /**< Object handle (Queue_t pointer) */
    uint32_t length;                         /**< Queue: uxLength; Sem: maxCount; Mutex: 1 */
    uint32_t item_size;                      /**< Queue: uxItemSize; Sem/Mutex: 0 */
    uint8_t  obj_type;                       /**< queueQUEUE_TYPE_* (0-5) */
    uint8_t  p_reserved[3];                  /**< Padding to 4-byte alignment */
    char     p_name[TRAX_RTOS_OBJ_NAME_LEN]; /**< Object name (null-padded) */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_rtos_object_wire_t) == 32,
                   "trax_rtos_object_wire_t must be 32 bytes");

/*=============================================================================
 ====================STATE MACHINE WIRE METADATA===============================
 ============================================================================*/

/** @brief SM name length (both SM name and state names) */
#define TRAX_SM_NAME_LEN  32

/**
 * @brief Maximum number of states per state machine (user-configurable)
 *
 * Override in trax_config.h before including trax_sm.h if more states are needed.
 * Flash cost: 36 + TRAX_SM_MAX_STATES * 40 bytes per SM definition.
 */
#ifndef TRAX_SM_MAX_STATES
#define TRAX_SM_MAX_STATES  8
#endif

/**
 * @brief State machine metadata wire header (fixed-size, 36 bytes)
 *
 * On the wire, each SM is: header (36 B) + state_count * state_entry (40 B each).
 */
struct trax_sm_meta_wire_t {
    uint16_t sm_index;                 /**< SM index (0-based, matches TID offset) */
    uint8_t  state_count;              /**< Number of states in this SM */
    uint8_t  initial_state_index;      /**< Initial state (0-based) */
    char     p_name[TRAX_SM_NAME_LEN]; /**< SM name (null-padded) */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_sm_meta_wire_t) == 36,
                   "trax_sm_meta_wire_t must be 36 bytes");

/**
 * @brief State machine state entry (40 bytes, 4-byte aligned)
 *
 * Used both in the flash struct and on the wire.
 * Color: 0x00RRGGBB, 0 = UI auto-assigns from palette.
 */
struct trax_sm_state_entry_t {
    uint8_t  state_index;              /**< State index (0-based within parent SM) */
    uint8_t  reserved[3];              /**< Padding to align color to 4-byte boundary */
    uint32_t color;                    /**< 0x00RRGGBB or 0 (auto) */
    char     p_name[TRAX_SM_NAME_LEN]; /**< State name (null-padded) */
} TRAX_PACKED;

TRAX_STATIC_ASSERT(sizeof(struct trax_sm_state_entry_t) == 40,
                   "trax_sm_state_entry_t must be 40 bytes");

/**
 * @brief State machine flash struct (header + inline state array)
 *
 * Placed in .trax_sm linker section. On the wire, only the header +
 * state_count entries are transmitted (not the full TRAX_SM_MAX_STATES array).
 */
struct trax_sm_wire_t {
    uint16_t sm_index;
    uint8_t  state_count;
    uint8_t  initial_state_index;
    char     p_name[TRAX_SM_NAME_LEN];
    struct trax_sm_state_entry_t p_states[TRAX_SM_MAX_STATES];
} TRAX_PACKED;


/*=============================================================================
 ====================GLOBAL VARIABLES==========================================
 ============================================================================*/

/*=============================================================================
 ====================GLOBAL FUNCTION DECLERATION===============================
 ============================================================================*/

#endif /* TRAX_META_TYPE_H_ */
