#include "MeasureUnit.h"
//#define DEBUG

MeasureUnit::MeasureUnit(uint16_t thermistorCount, 
uint64_t precResistor, 
ThermistorSettings thermistorSettings, 
LTC2439 &adc) : _thermistorCount(thermistorCount), 
_precResistor(precResistor),
_thermistorSettings(thermistorSettings), 
_adc(adc), 
_globalOffset(0), 
_error(OK), 
_state(IDLING),
_channel(0),
_offsetComputeIte(7),
_offsetCounter(7),
_courant{0.0, 0.0},
_triggerLevelOff(false)
{
  //Allocation dynamique des différent tableaux
  _temperatures = (double*) calloc(_thermistorCount, sizeof(double));
  _rOffsetMap = (double*) calloc(_thermistorCount, sizeof(double));
  _resistanceMap = (double*) malloc(_thermistorCount * sizeof(double));
  _rOffsetBuffer = (double*) malloc(_thermistorCount * sizeof(double));

  if(_temperatures == NULL || _rOffsetMap == NULL || _resistanceMap == NULL || _rOffsetBuffer == NULL)
  {
    _error = MALLOC_ERR;
    _temperatures != NULL ? free(_temperatures):(void)_temperatures;
    _rOffsetMap != NULL ? free(_rOffsetMap):(void)_rOffsetMap;
    _resistanceMap != NULL ? free(_resistanceMap):(void)_resistanceMap;
    _rOffsetBuffer != NULL ? free(_rOffsetBuffer):(void)_rOffsetBuffer;

    _temperatures = NULL;
    _rOffsetMap = NULL;
    _resistanceMap = NULL;
    _rOffsetBuffer = NULL;
  }
}

MeasureUnit::~MeasureUnit()
{
  if(_error != MALLOC_ERR)
  {
    free(_temperatures);
    free(_rOffsetMap);
    free(_resistanceMap);
    free(_rOffsetBuffer);
  }
}

void MeasureUnit::run()
{
  switch(_state)
  {
    case MEASURING:
      _adc.startAsyncSample(_channel);
      
      if(_adc.asyncResultAvailable())
      {
        _resistanceMap[_channel] = _adc.convertToVoltage(_adc.getAsyncValue());
       #ifdef DEBUG
       Serial.printf("Voltage %u  : %.2f\n", _channel, _resistanceMap[_channel]);
       #endif
       _channel++;
      }
      
      //Fin de la partie d'acquisition
      if(_channel == _thermistorCount)
      {
        _state = COMPUTING;
      }
      break;
    case COMPUTING :
      //Ici nous calculons les temperatures des thermistances (8 à la fois)
      _courant[0] = _resistanceMap[0] / (double) _precResistor;
      for(int i(0); i < 8; i++)
      {
        //Calcule de delta :
        if(i < 7)
          _resistanceMap[i] = _resistanceMap[i+1] - _resistanceMap[i];
        else
          _resistanceMap[i] = _adc.getVref() - _resistanceMap[i];
        #ifdef DEBUG
        Serial.printf("Debug voltage delta : %u -> %.2f\n",i,_resistanceMap[i]);
        #endif
      }

      _courant[1] = _resistanceMap[8] / (double) _precResistor;
      for(int i(8); i < 16; i++)
      {
        //Calcule de delta :
        if(i < 15)
          _resistanceMap[i] = _resistanceMap[i+1] - _resistanceMap[i];
        else
          _resistanceMap[i] = _adc.getVref() - _resistanceMap[i];
        #ifdef DEBUG
        Serial.printf("Debug voltage delta : %u -> %.2f\n",i,_resistanceMap[i]);
        #endif
      }

      //Calcule de la température (8 à la fois) :
      for(int i(0); i < 8; i++)
      {
        _resistanceMap[i] /= _courant[0];
        _temperatures[i] = computeTemperature(_thermistorSettings.getBeta(), _resistanceMap[i], _thermistorSettings.getRat25());
        _temperatures[i] += _rOffsetMap[i] + _globalOffset;

        #ifdef DEBUG
        Serial.printf("Temperature %u -> %.2f\n", i, _temperatures[i]);
        #endif
      }

      for(int i(8); i < 16; i++)
      {
        _resistanceMap[i] /= _courant[1];
        _temperatures[i] = computeTemperature(_thermistorSettings.getBeta(), _resistanceMap[i], _thermistorSettings.getRat25());
        _temperatures[i] += _rOffsetMap[i] + _globalOffset;

        #ifdef DEBUG
        Serial.printf("Temperature %u -> %.2f\n", i, _temperatures[i]);
        #endif
      }
       _state = MEASUREMENT_READY;
      break;
  }
}

double MeasureUnit::computeTemperature(double beta, double resistance, double rAt25)
{
  return (((25.0+273.15) * beta) / (beta + (25.0+273.15)*log(resistance / rAt25))) - 273.15;
}

void MeasureUnit::setGlobalTempOffset(double offset)
{
  _globalOffset = offset;
}

double MeasureUnit::getGlobalTempOffset()
{
  return _globalOffset;
}

double *MeasureUnit::getROffsetMap()
{
  return _rOffsetMap;
}

boolean MeasureUnit::startTemperatureMeasurement()
{

  if(_state == IDLING)
  {
    _state = MEASURING;
    _channel = 0;
    return true;
  }

  return false;
}

boolean MeasureUnit::isMeasurementReady()
{
  return _state == MEASUREMENT_READY;
}

double *MeasureUnit::getAsyncTemperatures()
{
  double *p(NULL);

  if(_state == MEASUREMENT_READY)
  {
    p = _temperatures;
    _state = IDLING;
  }
  
  return p;
}

void MeasureUnit::init()
{
  
}
