/*
 * 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_frame_validate.c
* @brief          : TraxProbe Frame Validation Implementation
* @version        : 3.0.0
*
* @attention
*   One check executed on every frame before it leaves the MCU:
*
*   1. Transaction counter — must equal (previous + 1) mod 65536.
*      Catches: buffer corruption, double-read, skipped frames, torn writes.
*
*   Timestamps are NOT checked here. The on-target timer wraps by design,
*   and Traxcope reconstructs an absolute timeline using the wrap-aware
*   host decoder, so a producer-side "timestamp went backwards" rule
*   would only mis-fire on legitimate wraps without adding sequence
*   coverage that the transaction-counter check doesn't already provide.
*
*   Stream restart handling:
*     TID_STREAM_START resets validation state automatically.
*     The START_TRACE frame itself is accepted unconditionally, and the
*     next frame after it re-seeds the expected counters (also accepted).
*     This mirrors the host decoder's sync-on-START_TRACE behavior.
******************************************************************************
*/

#include "trax_frame_validate.h"
#include "trax_frame.h"   /* struct trax_frame_t */
#include "trax_tid.h"     /* TID_STREAM_START     */

/* TraxProbe disabled (TRAX_ENABLE==0): whole unit compiles out. Frame validation
 * is unreachable once the producer path is no-op'd. */
#if TRAX_ENABLE

/*=============================================================================
 ====================LOCAL MACRO FUNCTIONS=====================================
 ============================================================================*/

/*=============================================================================
 ====================LOCAL MACRO DEFINITIONS===================================
 ============================================================================*/

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

volatile uint32_t trax_validate_err_trans_counter = 0;
volatile uint32_t trax_validate_err_total         = 0;

volatile uint16_t trax_validate_last_trans_counter = 0;

/* Failure context — latched on every reject for debugger post-mortem */
volatile uint16_t trax_validate_fail_expected = 0;
volatile uint16_t trax_validate_fail_actual   = 0;
const uint32_t *volatile trax_validate_fail_addr = 0;

/*=============================================================================
 ====================LOCAL STUCTURES===========================================
 ============================================================================*/

/*=============================================================================
 ====================LOCAL VARIABLES===========================================
 ============================================================================*/

static bool     initialized = false;  /* false until first frame accepted */
static uint16_t prev_trans  = 0;      /* previous transaction counter     */

/* Validate-once watermark: frames beyond the ring read pointer that have
 * already been validated. Retry walks (transport backpressure keeps the
 * read pointer in place) skip these frames — see the header doc block. */
static uint32_t prevalidated_cnt = 0;

/*=============================================================================
 ====================LOCAL FUNCTION DECLARATION================================
 ============================================================================*/

/*=============================================================================
 ====================GLOBAL FUNCTION IMPLEMANTATION============================
 ============================================================================*/

void trax_frame_validate_init(void)
{
	initialized      = false;
	prev_trans       = 0;
	prevalidated_cnt = 0;

	trax_validate_err_trans_counter  = 0;
	trax_validate_err_total          = 0;
	trax_validate_last_trans_counter = 0;
}

void trax_frame_validate_reset(void)
{
	/* Reset state only — error counters survive across sessions */
	initialized      = false;
	prev_trans       = 0;
	prevalidated_cnt = 0;
}

uint32_t trax_frame_validate_prevalidated(void)
{
	return prevalidated_cnt;
}

void trax_frame_validate_mark_walked(uint32_t frames_walked)
{
	if (frames_walked > prevalidated_cnt) {
		prevalidated_cnt = frames_walked;
	}
}

void trax_frame_validate_consume(uint32_t frames_consumed)
{
	prevalidated_cnt = (frames_consumed < prevalidated_cnt)
	                 ? (prevalidated_cnt - frames_consumed)
	                 : 0u;
}

bool trax_frame_validate(const uint32_t *p_frame_data, uint16_t frame_size32)
{
	(void)frame_size32;   /* reserved for future structural checks */

	const struct trax_frame_t *p_frame = (const struct trax_frame_t *)p_frame_data;

	/* Decode Word 0 with an explicit mask, NOT via the header.fields
	 * bitfields: the writer composes Word 0 with shifts/ORs
	 * (frame_size32 << 16 | trans_counter), and bitfield allocation order
	 * within a storage unit is implementation-defined (C11 6.7.2.1p11).
	 * Shift-based decode matches the writer on every compiler; the
	 * bitfield view only happens to agree on little-endian GCC/Clang. */
	uint16_t trans = (uint16_t)(p_frame->header.raw & 0xFFFFu);
	uint32_t tid   = p_frame->id;

	/* ── Stream restart: START_TRACE resets validation state ───────── */
	/* START_TRACE is accepted unconditionally. Set expected next
	 * transaction counter to 1 so the next frame must have trans_counter == 1.
	 * Rejects stray old-session frames (e.g. trans=5036) after restart. */
	if (tid == TRAX_TID_STREAM_START) {
		prev_trans  = 0;   /* Next frame must have trans_counter == 1 */
		initialized = true;
		trax_validate_last_trans_counter = trans;
		return true;
	}

	/* ── First frame (or first after restart): accept & seed state ── */
	if (!initialized) {
		prev_trans  = trans;
		initialized = true;
		trax_validate_last_trans_counter = trans;
		return true;
	}

	/* ── Check 1: Transaction counter must be previous + 1 ────────── */
	uint16_t expected_trans = prev_trans + 1;   /* wraps at 65535 → 0 */

	if (trans != expected_trans) {
		trax_validate_err_trans_counter++;
		trax_validate_err_total++;
		/* Latch failure context for debugger post-mortem. */
		trax_validate_fail_expected = expected_trans;
		trax_validate_fail_actual   = trans;
		trax_validate_fail_addr     = p_frame_data;
		/* Do NOT update prev_trans — keep the last-known-good value
		 * so the debugger can compare expected vs actual. */
		return false;  /* ← BREAKPOINT HERE */
	}

	/* ── All checks passed — update state ─────────────────────────── */
	prev_trans = trans;
	trax_validate_last_trans_counter = trans;

	return true;
}

/*=============================================================================
 ====================LOCAL FUNCTION IMPLEMANTATION=============================
 ============================================================================*/

#endif /* TRAX_ENABLE */
