/** 
  * @file    light_sensor.c
  * @brief   Light Sensor Module
  * @author  Kenny Tran
  * @date    Mar 08 2024
  * @version 0.0.1
  *
  * @section LICENSES
  * Copyright (C) Morgan Advanced Programmable Systems, Inc.
  */


/*--------------------------------------------------------------------------------------------------
 * INCLUDE FILES
 *------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <math.h>

#include "../lib/errno.h"
#include "../lib/print.h"

#include "../bsp/als.h"

#include "light_sensor.h"


/*--------------------------------------------------------------------------------------------------
 * MACROS & CONSTANTS
 *------------------------------------------------------------------------------------------------*/
#define _LIGHT_SENSOR_BRIGHTNESS_MAX    (65535)


/*--------------------------------------------------------------------------------------------------
 * TYPE DEFINITIONS & ENUMERATIONS
 *------------------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------------------
 * PUBLIC CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static float      _light_sensor_brightness_table[LIGHT_SENSOR_BRIGHTNESS_STEPS + 1];
static uint8_t    _light_sensor_step;
static ALS_data_s _light_sensor_data = (ALS_data_s) {
    .visible  = 0,
    .infrared = 0
};


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
static void _light_sensor_brightness_gamma_populate (void);


/*--------------------------------------------------------------------------------------------------
 * PUBLIC FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
void LIGHT_SENSOR_init (void)
{
    PRINT (INFO, "Initializing LIGHT_SENSOR...\r\n");

    if (ALS_init () != ERRNO_SUCCESS)
    {        
        PRINT (ERROR, "Failure to initialize ALS\r\n");
    }

    /* Set gain to 4 */
    if (ALS_gain_set (ALS_GAIN_4) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to set ALS gain\r\n");
    }

    /* Set integration time to 100 ms */
    if (ALS_int_time_set (ALS_INT_TIME_100MS) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to set ALS integration time\r\n");
    }

    /* Set measurement rate to 500 ms */
    if (ALS_measure_rate_set (ALS_MEASURE_RATE_100MS) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to set ALS measurement rate\r\n");
    }

    /* TODO: configure gain and measurement time.  Need to test heuristically to determine. */

    /* Populate light sensor brightness gamma table. */
    _light_sensor_brightness_gamma_populate ();

    /* Enable LTR329 for each reading. */
    if (ALS_enable (true) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to enable ALS\r\n");
    }
}

uint8_t LIGHT_SENSOR_step_get (void)
{
    uint8_t    i;
    bool       new_data = false;
    ALS_data_s reading;
    uint32_t   retries = 0;

    // /* Enable LTR329 for each reading. */
    // if (ALS_enable (true) != ERRNO_SUCCESS)
    // {
    //     PRINT (ERROR, "Failure to enable ALS\r\n");
    // }

    /* Wait for new reading. */
    do {
        if (ALS_new_data_available (&new_data) != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to read ALS status");
        }
        else
        {
            if (new_data)
            {
                break;
            }
        }
        retries++;
    } while (retries < LIGHT_SENSOR_READ_TRIES);

    if (retries >= LIGHT_SENSOR_READ_TRIES)
    {
        return (LIGHT_SENSOR_BRIGHTNESS_STEPS / 2);
    }

    /* Grab reading. */
    if (ALS_read (&reading) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to read ALS data");
    }

    /* Set local vars. */
    _light_sensor_data.visible  = reading.visible;
    _light_sensor_data.infrared = reading.infrared;

    /* Compare with companding table to get step. */
    for (i = 0; i < LIGHT_SENSOR_BRIGHTNESS_STEPS; i++)
    {
        /* NOTE: Currently only looking at visible. */
        if (_light_sensor_data.visible > _light_sensor_brightness_table[i])
        {
            /* +1 since we want interface range 1-10. */
            _light_sensor_step = i + 1;
        }
    }

    // /* Disable LTR329 after reading to conserve power. */
    // if (ALS_enable (false) != ERRNO_SUCCESS)
    // {
    //     PRINT (ERROR, "Failure to disable ALS\r\n");
    // }

    return _light_sensor_step;
}

void LIGHT_SENSOR_cli_cmd (int32_t argc, char **argv)
{
    if (argc == 0)
    {
        /* Perform a reading. */
        LIGHT_SENSOR_step_get ();

        PRINT (INFO, "LIGHT SENSOR\r\n");
        PRINT (INFO, "-------------------------------------------\r\n");
        PRINT (INFO, "Visible + IR : %d\r\n", (_light_sensor_data.visible + _light_sensor_data.infrared));
        PRINT (INFO, "Visible      : %d\r\n", _light_sensor_data.visible);
        PRINT (INFO, "IR           : %d\r\n", _light_sensor_data.infrared);
        PRINT (INFO, "Step         : %d\r\n", _light_sensor_step);
    }
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
static void _light_sensor_brightness_gamma_populate (void)
{
    const uint8_t  n_zeroes   = 1;
    const uint8_t  temp_steps = LIGHT_SENSOR_BRIGHTNESS_STEPS + n_zeroes;
          float    temp_arr[temp_steps + 1];    /* +1 to hold 0th value. */
          uint32_t i;

    /* Using a gamma correction factor of 3.5, the beginning of the curve are zeroes.
       Therefore, we (heuristically) calculate with extra steps and count zeroes (n_zeroes) and
       fill lookup table starting after zeroes. */
    for (i = 0; i <= temp_steps; i++)
    {
        temp_arr[i] = (float) pow (((float) i / (float) temp_steps), LIGHT_SENSOR_BRIGHTNESS_GAMMA_CF) *
                      (float) _LIGHT_SENSOR_BRIGHTNESS_MAX;
    }

    /* We start index at 1 to reserve index 0 for 0 %. */
    _light_sensor_brightness_table[0] = 0.0;

    /* Fill lookup table with the non-zeroes. */
    for (i = 1; i <= LIGHT_SENSOR_BRIGHTNESS_STEPS; i++)
    {
        _light_sensor_brightness_table[i] = temp_arr[i + n_zeroes];
    }
}
