/*
 * CAN_Communication.c
 *
 *  Created on: Apr 26, 2022
 *      Author: max
 */

#include "CAN_Communication.h"

#include "SPI_Communication.h"
#include "SoC_Estimation.h"

#include "stm32g4xx_hal_fdcan.h"

#include <string.h>

canFrame framebuffer[CANFRAMEBUFFERSIZE] = {0};
uint8_t framebufferwritepointer;
uint8_t framebufferreadpointer;

int32_t shuntcurrent = 0;
int32_t shuntvoltage1 = 0;
int32_t shuntvoltage2 = 0;
int32_t shuntvoltage3 = 0;
int32_t shuntbusbartemp = 0;
int32_t shuntpower = 0;
int32_t shuntampereseconds = 0;
int32_t shuntenergy = 0;

uint32_t shuntlastmessage = 0;

uint8_t currentlap = 0;
uint8_t TSTargetState = 0;

void CAN_Init(FDCAN_HandleTypeDef* hcan) {
  HAL_FDCAN_Stop(hcan);

  framebufferreadpointer = 0;
  framebufferwritepointer = 0;

  FDCAN_FilterTypeDef fdfilter = {0};

  fdfilter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  fdfilter.FilterID1 = 0x000; // Range start
  fdfilter.FilterID2 = 0x000; // Range stop
  fdfilter.FilterIndex = 0;

  fdfilter.FilterType = FDCAN_FILTER_MASK;
  fdfilter.IdType = FDCAN_STANDARD_ID;

  HAL_FDCAN_ConfigFilter(hcan, &fdfilter);

  HAL_FDCAN_Start(hcan);

  HAL_FDCAN_ActivateNotification(hcan, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
  // hcan->Instance->CCCR |= FDCAN_CCCR_ASM;
}

uint8_t CAN_Receive(FDCAN_HandleTypeDef* hcan) {
  uint32_t ecount = hcan->Instance->ECR;
  while (framebufferreadpointer != framebufferwritepointer) {
    framebufferreadpointer++;

    if (framebufferreadpointer >= CANFRAMEBUFFERSIZE) {
      framebufferreadpointer = 0;
    }

    canFrame rxFrame = framebuffer[framebufferreadpointer];

    switch (rxFrame.FrameID) {
    case SHUNT_CURRENT:
      shuntcurrent = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                     (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_VOLTAGE_1:
      shuntvoltage1 = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                      (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_VOLTAGE_2:
      shuntvoltage2 = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                      (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_VOLTAGE_3:
      shuntvoltage3 = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                      (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_BUSBAR_TEMP:
      shuntbusbartemp = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                        (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_POWER:
      shuntpower = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                   (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_ENERGY:
      shuntenergy = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                    (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case SHUNT_AMPERE_SECONDS:
      shuntampereseconds = (rxFrame.data[2] << 24) | (rxFrame.data[3] << 16) |
                           (rxFrame.data[4] << 8) | (rxFrame.data[5]);
      shuntlastmessage = framebuffer[framebufferreadpointer].timestamp;
      break;
    case CHARGER_START_CHARGING:
      TSTargetState = TS_CHARGING;
      break;
    case AUTOBOX_INFO:
      currentlap = rxFrame.data[0] >> 2;
      TSTargetState = rxFrame.data[0] & 0x01;
      break;
    }
  }

  return 0;
}

uint8_t CAN_Transmit(FDCAN_HandleTypeDef* hcan, uint16_t frameid,
                     uint8_t* buffer, uint8_t datalen) {
  FDCAN_TxHeaderTypeDef txheader = {0};

  txheader.Identifier = frameid;
  txheader.IdType = FDCAN_STANDARD_ID;
  txheader.TxFrameType = FDCAN_DATA_FRAME;
  txheader.DataLength = ((uint32_t)datalen) << 16;
  txheader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  txheader.BitRateSwitch = FDCAN_BRS_OFF;
  txheader.FDFormat = FDCAN_CLASSIC_CAN;
  txheader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  txheader.MessageMarker = 0;
  if (HAL_FDCAN_GetTxFifoFreeLevel(hcan) > 0) {
    HAL_FDCAN_AddMessageToTxFifoQ(hcan, &txheader, buffer);
    return 0;
  }

  return 1;
}

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef* handle,
                               uint32_t interrupt_flags) {

  FDCAN_RxHeaderTypeDef rxFrameHeader;
  uint8_t data[8];
  framebufferwritepointer++;

  if (framebufferwritepointer >= CANFRAMEBUFFERSIZE) {
    framebufferwritepointer = 0;
  }

  if (!(interrupt_flags & FDCAN_IT_RX_FIFO0_NEW_MESSAGE)) {
    return;
  }

  if (HAL_FDCAN_GetRxMessage(handle, FDCAN_RX_FIFO0, &rxFrameHeader, data) !=
      HAL_OK) {
    framebuffer[framebufferwritepointer].error = 1;
  } else {
    framebuffer[framebufferwritepointer].error = 0;
  }

  if (rxFrameHeader.IdType != FDCAN_STANDARD_ID) {
    return;
  }

  framebuffer[framebufferwritepointer].FrameID =
      (int16_t)(rxFrameHeader.Identifier);
  framebuffer[framebufferwritepointer].length =
      (uint8_t)(rxFrameHeader.DataLength >> 16);

  for (int i = 0; i < framebuffer[framebufferwritepointer].length; i++) {
    framebuffer[framebufferwritepointer].data[i] = data[i];
  }

  framebuffer[framebufferwritepointer].timestamp = HAL_GetTick();
}

void HAL_FDCAN_ErrorCallback(FDCAN_HandleTypeDef* hcan) {}

void CAN_SendAbxStatus(FDCAN_HandleTypeDef* hcan) {
  uint8_t buffer[4];
  buffer[0] = ctrltsstate.currentTSState | (1 << 7);
  buffer[1] = 160;
  buffer[2] = (uint8_t)(shuntvoltage1 / 2000);
  buffer[3] = current_soc;
  CAN_Transmit(hcan, AMS_STATUS_ID, buffer, 4);
}

void CAN_SendAMSPanic(FDCAN_HandleTypeDef* hcan, ErrorFlagHandler* errorflags) {
  uint8_t buffer[8];
  buffer[0] = errorflags->errorcode;
  memcpy(&buffer[1], errorflags->errorargs, 7);
  CAN_Transmit(hcan, AMS_PANIC_ID, buffer, 8);
}

uint8_t slavelognum = 0;
uint8_t framelognum = 0;

void CAN_SendLoggingFrame(FDCAN_HandleTypeDef* hcan) {

  uint8_t buffer[8];

  for (int i = 0; i < AMS_LOGGING_FRAMES_PER_LOOP; i++) {
    buffer[0] = ((slavelognum << 4) | framelognum);

    switch (framelognum) {
    case 0:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellVoltages[0],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellVoltages[1],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellVoltages[2],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellVoltages[3],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellVoltages[4],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellVoltages[5],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellVoltages[6],
                                     BATTERY_VOLTAGE_TYPE);
      break;

    case 1:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellVoltages[7],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellVoltages[8],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellVoltages[9],
                                     BATTERY_VOLTAGE_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellTemps[0],
                                     BATTERY_TEMP_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellTemps[1],
                                     BATTERY_TEMP_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellTemps[2],
                                     BATTERY_TEMP_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellTemps[3],
                                     BATTERY_TEMP_TYPE);
      break;

    case 2:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellTemps[4],
                                     BATTERY_TEMP_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellTemps[5],
                                     BATTERY_TEMP_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellTemps[6],
                                     BATTERY_TEMP_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellTemps[7],
                                     BATTERY_TEMP_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellTemps[8],
                                     BATTERY_TEMP_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellTemps[9],
                                     BATTERY_TEMP_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellTemps[10],
                                     BATTERY_TEMP_TYPE);
      break;

    case 3:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellTemps[11],
                                     BATTERY_TEMP_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellTemps[12],
                                     BATTERY_TEMP_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellTemps[13],
                                     BATTERY_TEMP_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellTemps[14],
                                     BATTERY_TEMP_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellTemps[15],
                                     BATTERY_TEMP_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellTemps[16],
                                     BATTERY_TEMP_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellTemps[17],
                                     BATTERY_TEMP_TYPE);
      break;

    case 4:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellTemps[18],
                                     BATTERY_TEMP_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellTemps[19],
                                     BATTERY_TEMP_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellTemps[20],
                                     BATTERY_TEMP_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellTemps[21],
                                     BATTERY_TEMP_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellTemps[22],
                                     BATTERY_TEMP_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellTemps[23],
                                     BATTERY_TEMP_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellTemps[24],
                                     BATTERY_TEMP_TYPE);
      break;

    case 5:
      buffer[1] = CAN_convert_logval(slaves[slavelognum].cellTemps[25],
                                     BATTERY_TEMP_TYPE);
      buffer[2] = CAN_convert_logval(slaves[slavelognum].cellTemps[26],
                                     BATTERY_TEMP_TYPE);
      buffer[3] = CAN_convert_logval(slaves[slavelognum].cellTemps[27],
                                     BATTERY_TEMP_TYPE);
      buffer[4] = CAN_convert_logval(slaves[slavelognum].cellTemps[28],
                                     BATTERY_TEMP_TYPE);
      buffer[5] = CAN_convert_logval(slaves[slavelognum].cellTemps[29],
                                     BATTERY_TEMP_TYPE);
      buffer[6] = CAN_convert_logval(slaves[slavelognum].cellTemps[30],
                                     BATTERY_TEMP_TYPE);
      buffer[7] = CAN_convert_logval(slaves[slavelognum].cellTemps[31],
                                     BATTERY_TEMP_TYPE);
      break;
    }

    CAN_Transmit(hcan, AMS_LOGGING_ID, buffer, 8);

    framelognum++;
    if ((framelognum >= AMS_LOGGING_NUM_FRAMES)) {
      framelognum = 0;
      slavelognum++;
    }
    if ((slavelognum >= NUMBEROFSLAVES)) {
      slavelognum = 0;
      framelognum = 0;
    }
  }
}

uint8_t CAN_convert_logval(uint16_t value, uint8_t type) {
  if (type == BATTERY_VOLTAGE_TYPE) {
    return value >> 8;
  } else if (type == BATTERY_TEMP_TYPE) {
    return value >> 4;
  }
}