/**
 * \file
 * \brief Module for the battery management.
 * \author Erich Styger, erich.styger@hslu.ch
 * \license SPDX-License-Identifier: BSD-3-Clause
 *
 * Deals with the robot battery.
 */

#include "Platform.h"
#if PL_CONFIG_HAS_BATTERY_ADC
#include "Battery.h"
#include "fsl_adc16.h"
#include "McuShell.h"
#include "McuRTOS.h"
#include "McuUtility.h"

#define BATT_ADC16_BASE          ADC1
#define BATT_ADC16_CHANNEL_GROUP 0U
#define BATT_ADC16_USER_CHANNEL  19U

static adc16_channel_config_t adc16ChannelConfigStruct;

uint8_t BATT_MeasureBatteryVoltage(uint16_t *cvP) {
  #define BAT_V_DIVIDER_UP   62 /* voltage divider pull-up */
  #define BAT_V_DIVIDER_DOWN 30 /* voltage divider pull-down */
  uint32_t milliVolts;
  uint16_t adcValue;

  ADC16_SetChannelConfig(BATT_ADC16_BASE, BATT_ADC16_CHANNEL_GROUP, &adc16ChannelConfigStruct);
  while (0U == (kADC16_ChannelConversionDoneFlag & ADC16_GetChannelStatusFlags(BATT_ADC16_BASE, BATT_ADC16_CHANNEL_GROUP)))
  {
    vTaskDelay(pdMS_TO_TICKS(1)); /* wait */
  }
  adcValue = ADC16_GetChannelConversionValue(BATT_ADC16_BASE, BATT_ADC16_CHANNEL_GROUP);
  /* reference voltage is 3.3V. Battery Voltage is using a voltage divider (R29, 62KOhm pullup to VBat, R30 30kOhm pull down to GND) */
  milliVolts = adcValue*330*(BAT_V_DIVIDER_UP+BAT_V_DIVIDER_DOWN)/BAT_V_DIVIDER_DOWN/0xffff; /* scale it to centi-volt. Do multiplication first to avoid numerical issues */
  *cvP = milliVolts;
  return ERR_OK;
}

static uint8_t BATT_PrintStatus(const McuShell_StdIOType *io) {
  uint8_t buf[32];
  uint16_t cv;

  McuShell_SendStatusStr((unsigned char*)"battery", (unsigned char*)"Battery status\r\n", io->stdOut);
  buf[0] = '\0';
  if (BATT_MeasureBatteryVoltage(&cv)==ERR_OK) {
    McuUtility_strcatNum32sDotValue100(buf, sizeof(buf), cv);
    McuUtility_strcat(buf, sizeof(buf), (uint8_t*)" V\r\n");
  } else {
    McuUtility_strcat(buf, sizeof(buf), (uint8_t*)"ERROR\r\n");
  }
  McuShell_SendStatusStr((unsigned char*)"  Battery", buf, io->stdOut);
  return ERR_OK;
}

static uint8_t PrintHelp(const McuShell_StdIOType *io) {
  McuShell_SendHelpStr((unsigned char*)"battery", (unsigned char*)"Group of battery commands\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
  return ERR_OK;
}

uint8_t BATT_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
  uint8_t res = ERR_OK;

  if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"battery help")==0) {
    *handled = TRUE;
    return PrintHelp(io);
  } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"battery status")==0) {
    *handled = TRUE;
    return BATT_PrintStatus(io);
  }
  return res;
}


void BATT_Init(void) {
  adc16_config_t adc16ConfigStruct;
  /*
    * adc16ConfigStruct.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref;
    * adc16ConfigStruct.clockSource = kADC16_ClockSourceAsynchronousClock;
    * adc16ConfigStruct.enableAsynchronousClock = true;
    * adc16ConfigStruct.clockDivider = kADC16_ClockDivider8;
    * adc16ConfigStruct.resolution = kADC16_ResolutionSE12Bit;
    * adc16ConfigStruct.longSampleMode = kADC16_LongSampleDisabled;
    * adc16ConfigStruct.enableHighSpeed = false;
    * adc16ConfigStruct.enableLowPower = false;
    * adc16ConfigStruct.enableContinuousConversion = false;
    */
   ADC16_GetDefaultConfig(&adc16ConfigStruct);

   adc16ConfigStruct.longSampleMode = kADC16_LongSampleCycle24;
   adc16ConfigStruct.resolution = kADC16_Resolution16Bit;

   ADC16_Init(BATT_ADC16_BASE, &adc16ConfigStruct);
   ADC16_EnableHardwareTrigger(BATT_ADC16_BASE, false); /* Make sure the software trigger is used. */
   if (kStatus_Success == ADC16_DoAutoCalibration(BATT_ADC16_BASE))
   {
       //PRINTF("ADC16_DoAutoCalibration() Done.\r\n");
   }
   else
   {
       //PRINTF("ADC16_DoAutoCalibration() Failed.\r\n");
     for(;;) {}
   }
   adc16ChannelConfigStruct.channelNumber = BATT_ADC16_USER_CHANNEL;
   adc16ChannelConfigStruct.enableInterruptOnConversionCompleted = false;
#if defined(FSL_FEATURE_ADC16_HAS_DIFF_MODE) && FSL_FEATURE_ADC16_HAS_DIFF_MODE
   adc16ChannelConfigStruct.enableDifferentialConversion = false;
#endif /* FSL_FEATURE_ADC16_HAS_DIFF_MODE */

}

void BATT_Deinit(void) {
}

#endif /* PL_CONFIG_HAS_BATTERY_ADC */
