/**
 * @file can.cpp
 *
 * @brief Implementation file for CAN-related functions in the ChainLube application.
 *
 * This file contains the implementation of functions related to CAN (Controller Area Network)
 * communication within the ChainLube application. It includes the initialization of the CAN module,
 * setup of masks and filters, and processing of CAN messages. Additionally, a debug message function
 * is included if CAN debugging is enabled.
 *
 * @author Your Name
 * @date   Date
 */

#include "can.h"

MCP_CAN CAN0(GPIO_CS_CAN);
#ifdef CAN_DEBUG_MESSAGE
#define MAX_DEBUG_RETRIES 100
void sendCANDebugMessage();
#endif

/**
 * @brief Initializes the CAN module, sets masks, and filters based on the configured CAN source.
 *
 * This function initializes the CAN module, sets masks and filters based on the configured CAN source
 * in the application settings, and sets the CAN module in normal mode for communication.
 */
void Init_CAN()
{
    if (CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) != CAN_OK)
        MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, true);

    CAN0.init_Mask(0, 0, 0x07FF0000); // Init first mask...
    CAN0.init_Mask(1, 0, 0x07FF0000); // Init second mask...

    switch (LubeConfig.CANSource)
    {
    case KTM_890_ADV_R_2021:
        CAN0.init_Filt(0, 0, 0x012D0000); // Init first filter...
        break;
    case KTM_1290_SD_R_2023:
        CAN0.init_Filt(0, 0, 0x012D0000); // Init first filter...
        break;
    default:
        break;
    }

    CAN0.setMode(MCP_NORMAL);
}

/**
 * @brief Processes CAN messages and sends a CAN debug message periodically.
 *
 * This function processes CAN messages and sends a CAN debug message periodically based on a time interval.
 */
void CAN_Process()
{
    static uint32_t previousMillis = 0;

    if (millis() - previousMillis >= 100)
    {
        sendCANDebugMessage();
        previousMillis = millis();
    }
}
/**
 * @brief Processes CAN messages to determine the wheel speed based on the configured CAN source.
 *
 * This function reads incoming CAN messages and extracts the rear wheel speed information.
 * The wheel speed is then converted to millimeters per second based on the configured CAN source.
 * The function also monitors the CAN signal for potential issues and triggers diagnostic trouble codes (DTCs).
 *
 * @return The calculated distance traveled in millimeters since the last call.
 */
uint32_t Process_CAN_WheelSpeed()
{
#define FACTOR_RWP_KMH_890ADV 18 // Divider to convert Raw Data to km/h
#define FACTOR_RWP_KMH_1290SD 18 // Divider to convert Raw Data to km/h
    can_frame canMsg;
    static uint32_t lastRecTimestamp = 0;
    uint16_t RearWheelSpeed_raw;
    uint32_t milimeters_to_add = 0;
    uint32_t RWP_millimeter_per_second = 0;

    if (CAN0.readMsgBuf(&canMsg.can_id, &canMsg.can_dlc, canMsg.data) == CAN_OK)
    {

        switch (LubeConfig.CANSource)
        {
        case KTM_890_ADV_R_2021:
            // raw / FACTOR_RWP_KMH_890ADV -> km/h * 100000 -> cm/h / 3600 -> cm/s
            // raw * 500 -> cm/s
            RearWheelSpeed_raw = (uint16_t)canMsg.data[5] << 8 | canMsg.data[6];
            RWP_millimeter_per_second = (((uint32_t)RearWheelSpeed_raw * 1000000) / FACTOR_RWP_KMH_890ADV) / 3600;
            break;
        case KTM_1290_SD_R_2023:
            // raw / FACTOR_RWP_KMH_1290SD -> km/h * 100000 -> cm/h / 3600 -> cm/s
            // raw * 500 -> cm/s
            RearWheelSpeed_raw = (uint16_t)canMsg.data[5] << 8 | canMsg.data[6];
            RWP_millimeter_per_second = (((uint32_t)RearWheelSpeed_raw * 1000000) / FACTOR_RWP_KMH_1290SD) / 3600;
            break;
        default:
            break;
        }

        uint32_t timesincelast = millis() - lastRecTimestamp;
        lastRecTimestamp = millis();

        milimeters_to_add = (RWP_millimeter_per_second * timesincelast) / 1000;
    }

    if (lastRecTimestamp > 1000)
    {
        MaintainDTC(DTC_NO_CAN_SIGNAL, (millis() > lastRecTimestamp + 10000 ? true : false));
    }

    return milimeters_to_add;
}

#ifdef CAN_DEBUG_MESSAGE
/**
 * @brief Sends periodic CAN debug messages for monitoring and diagnostics.
 *
 * This function sends periodic CAN debug messages containing various system information for monitoring and diagnostics.
 * The information includes system status, timestamps, tank percentage, DTC flags, and other relevant data.
 * The debug messages are sent with a configurable multiplexer to broadcast different types of information in each cycle.
 */
void sendCANDebugMessage()
{
#define MAX_DEBUG_MULTIPLEXER 6
    static uint16_t DebugSendFailTimeout = 0;
    static uint8_t debugMultiplexer = 0;
    byte data[8] = {debugMultiplexer, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    uint32_t millisValue = millis();

    switch (debugMultiplexer)
    {
    case 0:
        memcpy(&data[1], &millisValue, sizeof(millisValue));
        memcpy(&data[5], &globals.purgePulses, sizeof(globals.purgePulses));
        break;
    case 1:
        data[1] = (uint8_t)globals.systemStatus;
        data[2] = (uint8_t)globals.resumeStatus;
        data[3] = (uint8_t)globals.requestEEAction;
        data[4] = globals.TankPercentage;
        data[5] = (0x01 & globals.hasDTC) | ((0x01 & globals.measurementActive) << 1);
        break;
    case 2:
        memcpy(&data[1], &globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress));
        memcpy(&data[3], &PersistenceData.tankRemain_microL, sizeof(PersistenceData.tankRemain_microL));
        break;
    case 3:
        memcpy(&data[1], &PersistenceData.writeCycleCounter, sizeof(PersistenceData.writeCycleCounter));
        memcpy(&data[3], &PersistenceData.TravelDistance_highRes_mm, sizeof(PersistenceData.TravelDistance_highRes_mm));
        break;
    case 4:
        memcpy(&data[1], &PersistenceData.odometer_mm, sizeof(PersistenceData.odometer_mm));
        break;
    case 5:
        memcpy(&data[1], &PersistenceData.odometer, sizeof(PersistenceData.odometer));
        break;
    case 6:
        memcpy(&data[1], &PersistenceData.checksum, sizeof(PersistenceData.checksum));
        break;
    default:
        break;
    }

    debugMultiplexer++;
    debugMultiplexer = debugMultiplexer > MAX_DEBUG_MULTIPLEXER ? 0 : debugMultiplexer;

    if (DebugSendFailTimeout < MAX_DEBUG_RETRIES)
    {
        byte sndStat = CAN0.sendMsgBuf(0x7FF, 0, 8, data);
        if (sndStat != CAN_OK)
        {
            Debug_pushMessage("failed sending CAN-DbgMsg: %d, %d\n", sndStat, DebugSendFailTimeout);
            DebugSendFailTimeout++;
        }
    }
    else if (DebugSendFailTimeout == MAX_DEBUG_RETRIES)
    {
        Debug_pushMessage("disable CAN-DbgMsg due to timeout\n");
        DebugSendFailTimeout++;
    }
}
#endif