#include "stm32l1xx_hal.h"
#include "app/thermo/tsensor_ll.h"
#include "app/thermo/tsensor.h"
#include "core/csect.h"
#include "core/config.h"

// THERMO_SENSOR_KEEP_INITIALIZED
// Do not turn off (=1) the sensor periferal driver btw measures
#define THERMO_SENSOR_KEEP_INITIALIZED  1

// THERMO_SENSOR_AVG_WINDOW
// Digital exp-filter window size
#define THERMO_SENSOR_AVG_WINDOW        24   

// THERMO_SENSOR_INTERVAL
// Temperature measuring interval, ms
#define THERMO_SENSOR_INTERVAL          CONFIG_TSENSOR_INTERVAL   

static tTSensorTemp vThermoSensor_AVGTemp = THERMO_SENSOR_INVALID_TEMP_VALUE;
static size_t vThermoSensor_AVGPoints = 0;
static uint32_t vThermoSensor_LastTick = 0;
static uint32_t vThermoSensor_Interval = THERMO_SENSOR_INTERVAL;

#if THERMO_SENSOR_KEEP_INITIALIZED
static bool vThermoSensor_bPowered = false;
#endif

static void ThermoSensor_UpdateAVGTemp( tTSensorTemp new_value ) ;   
static void ThermoSensor_ResetAvgTemp();
static bool ThermoSensor_Serve();
static bool ThermoSensor_GetReady();
static tTSensorTemp ThermoSensor_GetAvgTemp();
static void ThermoSensor_SetInterval(size_t ms);

const sThermoSensorHandle_t ThermoSensor = {
    .GetReady = ThermoSensor_GetReady,
    .ResetTempAVG = ThermoSensor_ResetAvgTemp,
    .ServeSensor = ThermoSensor_Serve,
    .GetTempAVG = ThermoSensor_GetAvgTemp,
    .SetSensorInterval = ThermoSensor_SetInterval
};

static bool ThermoSensor_GetReady()
{
   bool bReady = false;

   __DI__
         bReady =  ( vThermoSensor_AVGPoints > 0 ) 
                && ( vThermoSensor_AVGTemp != THERMO_SENSOR_INVALID_TEMP_VALUE );
   __EI__

  return bReady;
}

static void ThermoSensor_SetInterval(uint32_t ms)
{
    __DI__ vThermoSensor_Interval = ms; __EI__ 
}

static void ThermoSensor_ResetAvgTemp()
{
    DI();
    vThermoSensor_AVGTemp = THERMO_SENSOR_INVALID_TEMP_VALUE;
    vThermoSensor_AVGPoints = 0;
    EI();
}

static tTSensorTemp ThermoSensor_GetAvgTemp()
{
    tTSensorTemp value;
    __DI__ value = vThermoSensor_AVGTemp; __EI__
    return value;
}

static bool ThermoSensor_Serve()
{
    bool bServe = false;

    __DI__ bServe = (HAL_GetTick() - vThermoSensor_LastTick > vThermoSensor_Interval); __EI__

    if( bServe )
    {
        #if THERMO_SENSOR_KEEP_INITIALIZED
        if( !vThermoSensor_bPowered )
        {
            vThermoSensor_bPowered = I2CTempSensor.Init();
        }

        if( vThermoSensor_bPowered )
        {
            tTSensorTemp temp_value;
            if( I2CTempSensor.GetTemp( &temp_value ) )
            {            
                ThermoSensor_UpdateAVGTemp( temp_value );
            }
            else
            {
                ThermoSensor_ResetAvgTemp();
            }
        }
        #else
        if( I2CTempSensor.Init() )
        {
            tTSensorTemp temp_value;
            if( I2CTempSensor.GetTemp( &temp_value ) )
            {            
                ThermoSensor_UpdateAVGTemp( temp_value );
            }
            else
            {
                ThermoSensor_ResetAvgTemp();
            }
            
            I2CTempSensor.DeInit();         
        }
        else
        {
            ThermoSensor_ResetAvgTemp();
        }
        #endif

        __DI__ vThermoSensor_LastTick = HAL_GetTick(); __EI__
    }

    return bServe;
}

// ThermoSensor_UpdateAVGTemp
// Performs averaging with exponential digital filter
static void ThermoSensor_UpdateAVGTemp( tTSensorTemp new_value ) 
{
    DI();
    if( (THERMO_SENSOR_INVALID_TEMP_VALUE) == vThermoSensor_AVGTemp )
    {
        vThermoSensor_AVGTemp = new_value;
        vThermoSensor_AVGPoints = 1;
    }
    else
    {
        vThermoSensor_AVGTemp = ( new_value + ( vThermoSensor_AVGTemp * vThermoSensor_AVGPoints ) ) / ( vThermoSensor_AVGPoints + 1 );

        if( vThermoSensor_AVGPoints < (THERMO_SENSOR_AVG_WINDOW) )
            vThermoSensor_AVGPoints++;    
        
        // Now @tmp is 16-bit value: [BBBBBBBBBB000000]
        // So, lets perform averaging: 
        //           n * AVG[i-1] + tmp
        //  AVG[i] = -------------------  
        //              n+1
        // where
        //  @AVG[i-1] is averaged temperature before operation,
        //  @AVG[i] is averaged temperature after operation,
        //  @n is the filter window size (=24),
        //  @tmp is momentary measured temperature, [BBBBBBBBBB000000].
        //
        // So after averaging, bits 5..0 will be filled up.
        // Since the sensor has a 0.25*C accuracy, the @tmp should be divided
        // by 4 to get the value in degrees. But after averaging the whole accuracy
        // is changed due to the bits 5..0 are filled up. So, we need to divide
        // the value not 4, but 256 times to get value in degrees:
        //  /4  - sensor accuracy
        //  /64 - averaging accuracy (6 bits, 2^6=64)
        // So, 4*64 = /256 of degree.
    }
    EI();
    
}
