//
// Created by enik on 02.06.22.
//

#include "can.h"

//#include "Settings.h"

#if CAN_TESTING_LOOPBACK
#define CAN_BASIC_MODE CAN_MODE_SILENT_LOOPBACK
#else
#define CAN_BASIC_MODE CAN_MODE_NORMAL
#endif

#include "can_messenger.h"

extern CAN_HandleTypeDef hcan1;
extern CAN_HandleTypeDef hcan2;

CAN_FilterTypeDef can1Filter;
CAN_FilterTypeDef can2Filter;
CAN_RxHeaderTypeDef can1RX, can2RX;
u8_t can1Data[8] = {0,}, can2Data[8] = {0,};

typedef struct CAN_SPD_VAL {
    u32_t Prescaler;
    u32_t TimeSeg1;
    u32_t TimeSeg2;
} CAN_SPD_VAL;

// PSC - 2 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd1000 = {.Prescaler = 2, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 3 | Time1 - 12 | Time2 - 2
const CAN_SPD_VAL spd800 = {.Prescaler = 3, .TimeSeg1 = CAN_BS1_12TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 4 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd500 = {.Prescaler = 4, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 8 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd250 = {.Prescaler = 8, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 16 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd125 = {.Prescaler = 16, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 20 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd100 = {.Prescaler = 20, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 40 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd50 = {.Prescaler = 40, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 100 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd20 = {.Prescaler = 100, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};
// PSC - 200 | Time1 - 15 | Time2 - 2
const CAN_SPD_VAL spd10 = {.Prescaler = 200, .TimeSeg1 = CAN_BS1_15TQ, .TimeSeg2 = CAN_BS2_2TQ};


/**
 * @brief Set Speed for CAN Bus
 * @param[in] spd #CAN_SPEED enum
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetSpeed(CAN_SPEED spd, CAN_HandleTypeDef *can) {
    //if (HAL_CAN_GetState(can) == HAL_CAN_STATE_LISTENING)
    HAL_CAN_Stop(can);
    can->Instance = (can == &hcan1) ? CAN1 : CAN2; // Can Instance
    can->Init.SyncJumpWidth = CAN_SJW_1TQ; // Default
    can->Init.TimeTriggeredMode = DISABLE; // Default
    can->Init.AutoBusOff = ENABLE; // Default
    can->Init.AutoWakeUp = ENABLE; // Default
    can->Init.AutoRetransmission = DISABLE; // Default
    can->Init.ReceiveFifoLocked = DISABLE; // Default
    can->Init.TransmitFifoPriority = ENABLE; // Default
    can->Init.Mode = CAN_BASIC_MODE; // Set Mode
    switch (spd) {
        case CAN_SPD_1000:
            can->Init.Prescaler = spd1000.Prescaler;
            can->Init.TimeSeg1 = spd1000.TimeSeg1;
            can->Init.TimeSeg2 = spd1000.TimeSeg2;
            break;
        case CAN_SPD_800:
            can->Init.Prescaler = spd800.Prescaler;
            can->Init.TimeSeg1 = spd800.TimeSeg1;
            can->Init.TimeSeg2 = spd800.TimeSeg2;
            break;
        case CAN_SPD_500:
            can->Init.Prescaler = spd500.Prescaler;
            can->Init.TimeSeg1 = spd500.TimeSeg1;
            can->Init.TimeSeg2 = spd500.TimeSeg2;
            break;
        case CAN_SPD_250:
            can->Init.Prescaler = spd250.Prescaler;
            can->Init.TimeSeg1 = spd250.TimeSeg1;
            can->Init.TimeSeg2 = spd250.TimeSeg2;
            break;
        case CAN_SPD_125:
            can->Init.Prescaler = spd125.Prescaler;
            can->Init.TimeSeg1 = spd125.TimeSeg1;
            can->Init.TimeSeg2 = spd125.TimeSeg2;
            break;
        case CAN_SPD_100:
            can->Init.Prescaler = spd100.Prescaler;
            can->Init.TimeSeg1 = spd100.TimeSeg1;
            can->Init.TimeSeg2 = spd100.TimeSeg2;
            break;
        case CAN_SPD_50:
            can->Init.Prescaler = spd50.Prescaler;
            can->Init.TimeSeg1 = spd50.TimeSeg1;
            can->Init.TimeSeg2 = spd50.TimeSeg2;
            break;
        case CAN_SPD_20:
            can->Init.Prescaler = spd20.Prescaler;
            can->Init.TimeSeg1 = spd20.TimeSeg1;
            can->Init.TimeSeg2 = spd20.TimeSeg2;
            break;
        case CAN_SPD_10:
            can->Init.Prescaler = spd10.Prescaler;
            can->Init.TimeSeg1 = spd10.TimeSeg1;
            can->Init.TimeSeg2 = spd10.TimeSeg2;
            break;
        default:
            break;
    }
    if (HAL_CAN_Init(can) != HAL_OK) {
        Error_Handler();
    }
    CAN_FilterTypeDef *filt;
    if (can == &hcan1) {
        filt = &can1Filter;
        filt->FilterBank = 0;
        filt->FilterFIFOAssignment = CAN_RX_FIFO0;
    } else if (can == &hcan2) {
        filt = &can2Filter;
        filt->FilterBank = 14;
        filt->FilterFIFOAssignment = CAN_RX_FIFO1;
        filt->SlaveStartFilterBank = 14;
    } else {
        return;
    }
    filt->FilterMode = CAN_FILTERMODE_IDMASK;
    filt->FilterScale = CAN_FILTERSCALE_32BIT;
    filt->FilterIdHigh = 0x0000;
    filt->FilterIdLow = 0x0000;
    filt->FilterMaskIdHigh = 0x0000;
    filt->FilterMaskIdLow = 0x0000;
    filt->FilterActivation = ENABLE;
    if (HAL_CAN_ConfigFilter(can, filt) != HAL_OK) {
        Error_Handler();
    }
//    HAL_CAN_Start(can);
    volatile HAL_StatusTypeDef status = HAL_CAN_Start(can);
    __NOP();
    volatile HAL_CAN_StateTypeDef st = HAL_CAN_GetState(can);
    if (st != HAL_CAN_STATE_LISTENING){
        __USR_BKPT();
        HAL_GPIO_WritePin(GPIOE, GPIO_PIN_12, GPIO_PIN_SET);
    } else {
    	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_14, GPIO_PIN_SET);
    }
    HAL_CAN_ActivateNotification(can,
                                 ((can == &hcan1) ? CAN_IT_RX_FIFO0_MSG_PENDING : CAN_IT_RX_FIFO1_MSG_PENDING)
                                 | CAN_IT_ERROR | CAN_IT_BUSOFF | CAN_IT_LAST_ERROR_CODE);
}

/**
 * @brief Set Silent mode
 * @param[in] is_silent 1 if silent mode needed
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetMode(bool is_silent, CAN_HandleTypeDef *can) {
    can->Init.Mode = !is_silent ? CAN_MODE_NORMAL : CAN_MODE_SILENT;
}

/**
 * @brief Set Silent Loopback mode
 * @param[in] is_loopback 1 if loopback mode needed
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetLoopback(bool is_loopback, CAN_HandleTypeDef *can) {
    can->Init.Mode = !is_loopback ? CAN_MODE_NORMAL : CAN_MODE_SILENT_LOOPBACK;
}

/**
 * @brief Check if CAN is opened
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 * @return 1 if Opened / 0 if Closed
 */
bool CAN_IsOpened(CAN_HandleTypeDef *can) {
    return HAL_CAN_GetState(can) == HAL_CAN_STATE_LISTENING ? 1 : 0;
}

/**
 * @brief Check if CAN is in silent mode
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 * @return 1 if Silent / 0 if not
 */
bool CAN_IsSilent(CAN_HandleTypeDef *can) {
    return (HAL_CAN_GetState(can) == HAL_CAN_STATE_LISTENING &&
            (can->Init.Mode == CAN_MODE_SILENT ||
             can->Init.Mode == CAN_MODE_SILENT_LOOPBACK)) ? 0 : 1;
}

/**
 * @brief Check if CAN is in silent loopbsck mode
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 * @return 1 if Silent Loopback / 0 if else
 */
bool CAN_IsLoopback(CAN_HandleTypeDef *can) {
    return (can->Init.Mode == CAN_MODE_SILENT_LOOPBACK) ? 1 : 0;
}

/**
 * @brief Set filter mode
 * @param[in] id_only 1 if ID_LIST mode / 0 if ID_MASK mode
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetFilterMode(bool id_only, CAN_HandleTypeDef *can) {
    CAN_FilterTypeDef *filt;
    if (can == &hcan1) {
        filt = &can1Filter;
    } else if (can == &hcan2) {
        filt = &can2Filter;
    } else {
        return;
    }
    filt->FilterMode = id_only ? CAN_FILTERMODE_IDLIST : CAN_FILTERMODE_IDMASK;
}

/**
 * @brief Set ID for filter
 * @warning Not realized
 * @param[in] filt_id Pointer to filter ID array
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetFilterID(u8_t *filt_id, CAN_HandleTypeDef *can) {
    CAN_FilterTypeDef *filt;
    if (can == &hcan1) {
        filt = &can1Filter;
    } else if (can == &hcan2) {
        filt = &can2Filter;
    } else {
        return;
    }
}

/**
 * @brief Set Mask for filter
 * @warning Not realized
 * @param[in] filt_mask Pointer to filter mask
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_SetFilterMask(u8_t *filt_mask, CAN_HandleTypeDef *can) {
    CAN_FilterTypeDef *filt;
    if (can == &hcan1) {
        filt = &can1Filter;
    } else if (can == &hcan2) {
        filt = &can2Filter;
    } else {
        return;
    }
}

/**
 * @brief Common transmit prototype
 * @param[in] id CAN Packet ID
 * @param[in] ext 0 if 11-bit ID / 1 if 29-bit ID
 * @param[in] rtr 0 if standard frame / 1 if remote frame
 * @param[in] len Length of payload
 * @param[in] data Pointer to payload array
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_Transmit_Proto(u32_t id, bool ext, bool rtr, u8_t len, u8_t *data, CAN_HandleTypeDef *can) {
    CAN_TxHeaderTypeDef tx;
    tx.StdId = !ext ? id : 0;
    tx.ExtId = ext ? id : 0;
    tx.RTR = !rtr ? CAN_RTR_DATA : CAN_RTR_REMOTE;
    tx.IDE = !ext ? CAN_ID_STD : CAN_ID_EXT;
    tx.DLC = len;
    tx.TransmitGlobalTime = 0;
    u32_t mb;
    HAL_CAN_AddTxMessage(can, &tx, data, &mb);
}

/**
 * @brief Transmit standard (11-bit) data frame
 * @param[in] id 11-bit frame ID
 * @param[in] data Pointer to payload to be sent
 * @param[in] len Length of the payload to be sent
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_Transmit(u32_t id, u8_t *data, u8_t len, CAN_HandleTypeDef *can) {
    CAN_Transmit_Proto((u32_t) id, 0, 0, len, data, can);
}

/**
 * @brief Transmit extended (29-bit) data frame
 * @param[in] id 29-bit frame ID
 * @param[in] data Pointer to payload to be sent
 * @param[in] len Length of the payload to be sent
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_TransmitExt(u32_t id, u8_t *data, u8_t len, CAN_HandleTypeDef *can) {
    CAN_Transmit_Proto(id, 1, 0, len, data, can);
}

/**
 * @brief Transmit standard (11-bit) remote frame
 * @param[in] id 11-bit frame ID
 * @param[in] len Length of remote frame
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_TransmitRTR(u32_t id, u8_t len, CAN_HandleTypeDef *can) {
    CAN_Transmit_Proto((u32_t) id, 0, 1, len, NULL, can);
}

/**
 * @brief Transmit extended (29-bit) remote frame
 * @param[in] id 29-bit frame ID
 * @param[in] len Length of remote frame
 * @param[in] can Pointer to HAL CAN_HandleTypeDef
 */
void CAN_TransmitExtRTR(u32_t id, u8_t len, CAN_HandleTypeDef *can) {
    CAN_Transmit_Proto((u32_t) id, 1, 1, len, NULL, can);
}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &can1RX, can1Data) == HAL_OK) {
        //can_irq_receive(&can1RX, can1Data, 1);
    }
}

void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &can2RX, can2Data) == HAL_OK) {
        can2_receive_from_irq(&can2RX, can2Data);
        //can_irq_receive(&can2RX, can2Data, 2);
    }
}

