#include "ad_project.h"

#include <string.h>

#include "ad_debug.h"
#include "ad_board.h"
#include "ad_binary_transport.h"
#include "ad_inverter.h"
#include "ad_modbus.h"
#include "ad_parameter_identification.h"
#include "main.h"

#ifndef AD_PROJECT_AUTO_START_MODE
#define AD_PROJECT_AUTO_START_MODE AD_PARAM_ID_MODE_IDLE
#endif

#ifndef AD_PROJECT_AUTO_START_LOCKED_ROTOR_ALLOWED
#define AD_PROJECT_AUTO_START_LOCKED_ROTOR_ALLOWED 0
#endif

static AD_ProjectState_t g_project_state;
volatile AD_DebugView_t g_ad_debug;

static void project_update_state(void)
{
    const AD_InverterState_t *inverter = AD_Inverter_GetState();

    if (inverter != 0) {
        g_project_state.inverter_initialized = inverter->initialized;
        g_project_state.outputs_allowed_by_build = inverter->outputs_allowed_by_build;
    }

    g_project_state.modbus_initialized = AD_Modbus_GetState()->initialized;
    g_project_state.binary_transport_initialized = AD_BinaryTransport_GetState()->initialized;
    g_project_state.param_id_status = AD_ParamID_GetStatus();
    g_project_state.param_id_faults = AD_ParamID_GetFaults();
}

void AD_Project_Init(void)
{
    memset(&g_project_state, 0, sizeof(g_project_state));

    AD_Board_Init();
    AD_BinaryTransport_Init();
    AD_Modbus_Init(&huart2);

    g_project_state.board_initialized = 1U;
    g_project_state.simulink_initialized = 1U;
    g_project_state.initialized = 1U;

#if AD_PROJECT_AUTO_START_LOCKED_ROTOR_ALLOWED != 0
    AD_ParamID_SetLockedRotorAllowed(1U);
#endif

#if AD_PROJECT_AUTO_START_MODE != AD_PARAM_ID_MODE_IDLE
    AD_Board_StartParamTest((uint8_t)AD_PROJECT_AUTO_START_MODE);
#endif

    project_update_state();
    AD_Debug_Init();
}

void AD_Project_Loop(void)
{
    if (g_project_state.initialized == 0U) {
        return;
    }

    AD_Board_Loop();
    AD_BinaryTransport_Loop();
    AD_Modbus_Loop();
    project_update_state();
    AD_Debug_Update();
}

void AD_Project_EmergencyStop(uint32_t fault_flags)
{
    AD_Board_StopParamTest();
    AD_ParamID_EmergencyStop(fault_flags);
    project_update_state();
    AD_Debug_Update();
}

const AD_ProjectState_t* AD_Project_GetState(void)
{
    return &g_project_state;
}

void AD_Debug_Init(void)
{
    memset((void *)&g_ad_debug, 0, sizeof(g_ad_debug));
    AD_Debug_Update();
}

void AD_Debug_Update(void)
{
    const AD_ProjectState_t *project = AD_Project_GetState();
    const AD_InverterState_t *inverter = AD_Inverter_GetState();
    const AD_BoardAdcSnapshot_t *adc = AD_Board_GetAdcSnapshot();
    const AD_Measurements_t *measurements = AD_ParamID_GetLastMeasurements();
    const AD_MotorParameters_t *parameters = AD_ParamID_GetParameters();
    const AD_ModbusState_t *modbus = AD_Modbus_GetState();
    const volatile AD_ModbusRegisters_t *modbus_regs = AD_Modbus_GetRegisters();
    const AD_BinaryTransportState_t *binary_transport = AD_BinaryTransport_GetState();
    const SimulinkInterface_OutputBus_t *output = SimulinkInterface_GetOutputBus();
    uint32_t status = AD_ParamID_GetStatus();

    if (project != 0) {
        g_ad_debug.project = *project;
    }
    if (inverter != 0) {
        g_ad_debug.inverter = *inverter;
    }
    if (adc != 0) {
        g_ad_debug.adc = *adc;
    }
    if (measurements != 0) {
        g_ad_debug.measurements = *measurements;
    }
    if (parameters != 0) {
        g_ad_debug.motor_parameters = *parameters;
    }
    if (modbus != 0) {
        g_ad_debug.modbus = *modbus;
    }
    if (modbus_regs != 0) {
        g_ad_debug.modbus_regs = *modbus_regs;
    }
    if (binary_transport != 0) {
        g_ad_debug.binary_transport = *binary_transport;
    }
    if (output != 0) {
        g_ad_debug.command = output->command;
    }

    g_ad_debug.param_id_status = status;
    g_ad_debug.param_id_faults = AD_ParamID_GetFaults();
    g_ad_debug.param_id_mode = AD_ParamID_GetMode();
    g_ad_debug.power_stage_allowed = AD_ParamID_IsPowerStageAllowed();
    g_ad_debug.test_running = ((status & AD_PARAM_ID_STATUS_ACTIVE) != 0UL) ? 1U : 0U;
    g_ad_debug.tick_ms = HAL_GetTick();
}
