#include "vehicle.h"

#include "main.h"
#include "state.h"

#include "stm32g4xx_hal_def.h"
#include "stm32g4xx_hal_fdcan.h"

FDCAN_HandleTypeDef* can;

void handle_as_mission_fb(FDCAN_RxHeaderTypeDef* header, uint8_t* data);

void vehicle_init(FDCAN_HandleTypeDef* handle) {
  can = handle;

  // RX Filter
  FDCAN_FilterTypeDef filter;
  filter.IdType = FDCAN_STANDARD_ID;
  filter.FilterIndex = 0;
  filter.FilterType = FDCAN_FILTER_MASK;
  filter.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  filter.FilterID1 = CAN_ID_AS_MISSION_FB;
  filter.FilterID2 = filter.FilterID1;
  if (HAL_FDCAN_ConfigFilter(can, &filter) != HAL_OK) {
    Error_Handler();
  }

  // Global filter: Filter all remote frames with STD & EXT ID, reject non
  // matching frames with STD & EXT ID

  // TODO: What does that mean?
  if (HAL_FDCAN_ConfigGlobalFilter(can, FDCAN_REJECT, FDCAN_REJECT,
                                   FDCAN_FILTER_REMOTE,
                                   FDCAN_FILTER_REMOTE) != HAL_OK) {
    Error_Handler();
  }

  // Start FDCAN module
  if (HAL_FDCAN_Start(can) != HAL_OK) {
    Error_Handler();
  }

  // Enable RX interrupt
  if (HAL_FDCAN_ActivateNotification(can, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) !=
      HAL_OK) {
    Error_Handler();
  }
}

void vehicle_send_mission_select(Mission mission) {
  // TODO: Automatically resend this until it's acknowledged
  FDCAN_TxHeaderTypeDef header;
  header.Identifier = CAN_ID_MISSION_SELECT;
  header.IdType = FDCAN_STANDARD_ID;
  header.TxFrameType = FDCAN_DATA_FRAME;
  header.DataLength = FDCAN_DLC_BYTES_1;
  header.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  header.BitRateSwitch = FDCAN_BRS_OFF;
  header.FDFormat = FDCAN_CLASSIC_CAN;
  header.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  header.MessageMarker = 0;

  uint8_t data[1];
  data[0] = mission;
  if (HAL_FDCAN_AddMessageToTxFifoQ(can, &header, data) != HAL_OK) {
    Error_Handler();
  }
}

/* BEGIN CAN MESSAGE HANDLERS { */

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef* handle,
                               uint32_t interrupt_flags) {
  if (!(interrupt_flags & FDCAN_IT_RX_FIFO0_NEW_MESSAGE)) {
    return;
  }

  static FDCAN_RxHeaderTypeDef header;
  static uint8_t data[64];

  if (HAL_FDCAN_GetRxMessage(can, FDCAN_RX_FIFO0, &header, data) != HAL_OK) {
    Error_Handler();
  }

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

  switch (header.Identifier) {
  case CAN_ID_AS_MISSION_FB:
    handle_as_mission_fb(&header, data);
    break;
  }
}

void handle_as_mission_fb(FDCAN_RxHeaderTypeDef* header, uint8_t* data) {
  Mission mission_fb = data[0];
  if (mission_fb == MISSION_NONE) {
    return;
  }

  stw_state.view_state.ami.current_mission = mission_fb;

  stw_state.view = VIEW_AMI;
}

/* } END CAN MESSAGE HANDLERS */
