
#include <stdlib.h>

#include "main.h"
#include "tx_api.h"
#include "app_azure_rtos.h"
#include "tx_execution_profile.h"

#if defined(TX_EXECUTION_PROFILE_ENABLE)

extern unsigned FRC(void);

extern void ThreadEnter(thread_trace* trace);
extern void ThreadExit(thread_trace* trace);

extern TX_THREAD    tx_app_thread;

const signed        ThreadTraceSize = THREAD_TRACE_SIZE;
thread_trace        ThreadTrace[THREAD_TRACE_SIZE];
signed              ThreadTraceHead;
signed              ThreadTraceTail;
unsigned char       ThreadTraceFreeze;
static uint8_t      ThreadTraceFrozen;

unsigned            RTOS_Tstart;
unsigned            RTOS_Tmax;
unsigned            RTOS_Tused;
unsigned            RTOS_Calls;
static unsigned     RTOS_Tsave;
static uint8_t      RTOS_Started;


static thread_trace* AppendTrace(void)
{
  signed          I = ThreadTraceTail;

  ThreadTraceTail = (ThreadTraceTail + 1) % ThreadTraceSize;
  if (ThreadTraceTail == ThreadTraceHead) ThreadTraceHead = (ThreadTraceHead + 1) % ThreadTraceSize;
  return &ThreadTrace[I];
}

static thread_trace* GetLastTrace(void)
{
  signed          I = ThreadTraceTail;

  if (I == 0)
    I = ThreadTraceSize - 1;
  else
    I--;
  return &ThreadTrace[I];
}

void tx_reset_timing(unsigned* Tlast, unsigned* Tnow)
{
  TX_THREAD*      thrd = tx_thread_identify();
  TX_THREAD*      t;

  t = &tx_app_thread;
  do {
    t->tx_thread_execution_time_total.Tmax = 0;
    t->tx_thread_execution_time_total.Tused = 0;
    t->tx_thread_execution_time_total.Calls = 0;
    t->tx_thread_execution_time_total.Percent = 0;
    t = t->tx_thread_created_next;
  } while (!(t == 0 || t == &tx_app_thread));
  RTOS_Tmax = 0;
  RTOS_Tused = 0;
  RTOS_Calls = 0;
  if (thrd != 0)
  {
    (*Tlast) = RTOS_Tstart;
    (*Tnow) = thrd->tx_thread_execution_time_total.T;
    RTOS_Tstart = (*Tnow);
  }
}

VOID _tx_execution_initialize(VOID)
{
}

VOID _tx_execution_thread_enter(VOID)
{
  TX_THREAD*      thrd = tx_thread_identify();
  thread_trace*   trace;
  unsigned        T = FRC();
  unsigned        Tfrc;

  if (thrd != 0)
  {
    if (RTOS_Started)
    {
      RTOS_Calls++;
      Tfrc = T - RTOS_Tsave;
      if (Tfrc > RTOS_Tmax) RTOS_Tmax = Tfrc;
      RTOS_Tused += Tfrc;
    }

    thrd->tx_thread_execution_time_total.T = T;

    if (ThreadTraceFreeze)
    {
      ThreadTraceFrozen = 1;
    }
    else
    {
      ThreadTraceFrozen = 0;
      trace = AppendTrace();
      trace->thrd = thrd;
      trace->Tenter = T;
      trace->Texit = T;
      ThreadEnter(trace);
    }
  }
}

VOID _tx_execution_thread_exit(VOID)
{
  TX_THREAD*      thrd = tx_thread_identify();
  thread_trace*   trace;
  unsigned        T = FRC();
  unsigned        Tfrc;

  if (thrd != 0)
  {
    thrd->tx_thread_execution_time_total.Calls++;
    Tfrc = T - thrd->tx_thread_execution_time_total.T;
    if (Tfrc > thrd->tx_thread_execution_time_total.Tmax) thrd->tx_thread_execution_time_total.Tmax = Tfrc;
    thrd->tx_thread_execution_time_total.Tused += Tfrc;

    if (!ThreadTraceFrozen)
    {
      trace = GetLastTrace();
      if (trace->thrd != thrd) OutputDebug("thrd mismatch");
      trace->Texit = T;
      ThreadExit(trace);
    }

    RTOS_Started = 1;
    RTOS_Tsave = T;
  }
}

VOID _tx_execution_isr_enter(VOID)
{
}

VOID _tx_execution_isr_exit(VOID)
{
}

#endif // defined(TX_EXECUTION_PROFILE_ENABLE)

