/*
 * AMS_HighLevel.c
 *
 *  Created on: 20.07.2022
 *      Author: max
 */

#include "AMS_HighLevel.h"
#include "ADBMS_Abstraction.h"
#include "ADBMS_LL_Driver.h"
#include "TMP1075.h"
#include "can-halal.h"
#include "errors.h"
#include "stm32f3xx_hal.h"

Cell_Module module = {};
uint32_t balancedCells = 0;
bool balancingActive = false;

uint16_t amsuv = 0;
uint16_t amsov = 0;

uint8_t numberofCells = 13;
uint8_t numberofAux = 0;

uint8_t packetChecksumFails = 0;
#define MAX_PACKET_CHECKSUM_FAILS 5

uint8_t deviceSleeps = 0;
#define MAX_DEVICE_SLEEP 3 //TODO: change to correct value
#define MAX_CELL_VOLTAGE 4100 //change to 4200
#define MIN_CELL_VOLTAGE 3100 //change to 3100
amsState currentAMSState = AMSDEACTIVE;
amsState lastAMSState = AMSDEACTIVE;

struct pollingTimes {
  uint32_t S_ADC_OW_CHECK;
  uint32_t TMP1075;
};

struct pollingTimes pollingTimes = {0, 0};

void AMS_Init(SPI_HandleTypeDef* hspi) {
  initAMS(hspi, numberofCells, numberofAux);
  amsov = DEFAULT_OV;
  amsuv = DEFAULT_UV;

  pollingTimes = (struct pollingTimes) {HAL_GetTick(), HAL_GetTick()};

  currentAMSState = AMSIDLE;
}

void AMS_Loop() {

  // On Transition Functions called ones if the State Changed

  if (currentAMSState != lastAMSState) {
    switch (currentAMSState) {
    case AMSIDLE:
      break;
    case AMSDEACTIVE:
      break;
    case AMSCHARGING:
      break;
    case AMSIDLEBALANCING:
      break;
    case AMSDISCHARGING:
      break;
    case AMSWARNING:
      break;
    case AMSERROR:
      break;
    }
    lastAMSState = currentAMSState;
  }

  // Main Loops for different AMS States

  switch (currentAMSState) {
  case AMSIDLE:
    AMS_Idle_Loop();
    break;
  case AMSDEACTIVE:
    break;
  case AMSCHARGING:
    break;
  case AMSIDLEBALANCING:
    AMS_Idle_Loop();
    break;
  case AMSDISCHARGING:
    break;
  case AMSWARNING:
    AMS_Warning_Loop();
    break;
  case AMSERROR:
    break;
  }
}

uint8_t AMS_Idle_Loop() {
  if (!amsWakeUp()) {
    error_data.data_kind = SEK_INTERNAL_BMS_TIMEOUT; //we don't receive data for the wakeup command
    set_error_source(ERROR_SOURCE_INTERNAL);         //so we can't tell if we timed out
  }
  
  packetChecksumFails += amsAuxAndStatusMeasurement(&module);

  if (module.status.SLEEP) {
    deviceSleeps++;
    if (deviceSleeps > MAX_DEVICE_SLEEP) {
      error_data.data_kind = SEK_INTERNAL_BMS_TIMEOUT;
      set_error_source(ERROR_SOURCE_INTERNAL);
    } else {
      amsReset();
    }
  }

  if (module.status.CS_FLT || module.status.SPIFLT || module.status.CMED ||
      module.status.SMED || module.status.VDE || module.status.VDEL ||
     module.status.OSCCHK || module.status.TMODCHK) {
    error_data.data_kind = SEK_INTERNAL_BMS_FAULT;
    set_error_source(ERROR_SOURCE_INTERNAL);
  }

  if (module.status.THSD) {
    error_data.data_kind = SEK_INTERNAL_BMS_OVERTEMP;
    set_error_source(ERROR_SOURCE_INTERNAL);
  }

  packetChecksumFails += amsCellMeasurement(&module);
  packetChecksumFails += amsCheckUnderOverVoltage(&module);

  if (packetChecksumFails > MAX_PACKET_CHECKSUM_FAILS) {
    error_data.data_kind = SEK_INTERNAL_BMS_CHECKSUM_FAIL;
    set_error_source(ERROR_SOURCE_INTERNAL);
  }
  
  int any_voltage_error = 0;
  for (size_t i = 0; i < numberofCells; i++) {
    if (module.cellVoltages[i] < MIN_CELL_VOLTAGE) {
      any_voltage_error = 1;
      error_data.data_kind = SEK_UNDERVOLT;
      error_data.data[0] = i;
      uint8_t* ptr = &error_data.data[1];
      ptr = ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2);
    } else if (module.cellVoltages[i] > MAX_CELL_VOLTAGE) {
      any_voltage_error = 1;
      error_data.data_kind = SEK_OVERVOLT;
      error_data.data[0] = i;
      uint8_t* ptr = &error_data.data[1];
      ptr = ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2);
    }
  }

  if (module.internalDieTemp > 28000) { //TODO: change to correct value
    error_data.data_kind = SEK_INTERNAL_BMS_OVERTEMP;
    uint8_t* ptr = &error_data.data[0];
    ptr = ftcan_marshal_unsigned(ptr, module.internalDieTemp, 2);

    set_error_source(ERROR_SOURCE_INTERNAL);
  } else {
    clear_error_source(ERROR_SOURCE_INTERNAL);
  }

  if (any_voltage_error) {
    set_error_source(ERROR_SOURCE_VOLTAGES);
  } else {
    clear_error_source(ERROR_SOURCE_VOLTAGES);
  }

  mcuDelay(10);

  if ((module.overVoltage | module.underVoltage)) {
    
  }

  return 0;
}

uint8_t AMS_Warning_Loop() { return 0; }

uint8_t AMS_Error_Loop() { return 0; }

uint8_t AMS_Charging_Loop() { return 0; }

uint8_t AMS_Discharging_Loop() { return 0; }

uint8_t AMS_Balancing_Loop() {
  //TODO: implement
  return 0;
}