/** 
  * @file    bubble.c
  * @brief   Bubble LED 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 <stdbool.h>
#include <stdlib.h>
#include <string.h>

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

#include "../bsp/led.h"

#include "ble.h"

#include "bubble.h"


/*--------------------------------------------------------------------------------------------------
 * MACROS & CONSTANTS
 *------------------------------------------------------------------------------------------------*/


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


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


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static FS_file_t       _bubble_file;
static BUBBLE_config_s _bubble_config = (BUBBLE_config_s) {
    .brightness_step = BUBBLE_BRIGHTNESS_STEP_DEFAULT,
    .reserved_0      = 0xFF,
    .reserved_1      = 0xFF,
    .reserved_2      = 0xFF
};


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
static void _bubble_config_read (void);
static void _bubble_config_write (void);


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

    if (LED_init () < ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to initialize LEDs.\r\n");
        return;
    }

    /* Initialize filesystem. */
    if (FS_init () < ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to initialize filesystem.\r\n");
        return;
    }

    /* Fetch configuration settings stored for bubble. */
    _bubble_config_read ();
}

void BUBBLE_brightness_set (uint8_t step, bool save_settings)
{
    if (step > BUBBLE_BRIGHTNESS_STEPS)
    {
        PRINT (ERROR, "Invalid brightness step.\r\n");
        return;
    }

    /* Set brightness step. */
    _bubble_config.brightness_step = step;

    if (save_settings)
    {
        /* Write new settings to file. */
        _bubble_config_write ();

        /* Notify changes to BLE subscriber. */
        BLE_notify (BLE_CHAR_BUBBLE_BRIGHTNESS, &step, sizeof (step));
    }
}

uint8_t BUBBLE_brightness_get (void)
{
    return _bubble_config.brightness_step;
}

void BUBBLE_enable (bool en)
{
    float duty;

    if (en)
    {
        /* 100 for 100% max duty cycle.  BUBBLE_BRIGHTNESS_STEPS + 1 extra step for off. */
        duty = (float) GAMMA_corrected_get ((uint32_t) _bubble_config.brightness_step,
                                            (uint32_t) (BUBBLE_BRIGHTNESS_STEPS + 1),
                                            (uint32_t) 100,
                                            (double) BUBBLE_BRIGHTNESS_GAMMA_CF);
    }
    else
    {
        duty = 0.0;
    }

    if (LED_pwm_set (LED_BUBBLE, duty) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to set bubble LED brightness.\r\n");
    }
}

void BUBBLE_cli_cmd (int32_t argc, char **argv)
{
    if (argc == 0)
    {
        PRINT (INFO, "BUBBLE UV LED\r\n");
        PRINT (INFO, "-------------------------------------------\r\n");
        PRINT (INFO, "brightness_step (0 - %d) : %d\r\n", BUBBLE_BRIGHTNESS_STEPS, _bubble_config.brightness_step);
    }
    else if (argc == 2)
    {
        if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable"))
        {
            if (!strcmp (argv[1], "true"))
            {
                BUBBLE_enable (true);
            }
            else if (!strcmp (argv[1], "false"))
            {
                BUBBLE_enable (false);
            }
            else
            {
                goto USAGE;
            }
        }
        else if (!strcmp (argv[0], "-s"))
        {
            uint8_t step = (uint8_t) strtoul (argv[1], NULL, 0);

            BUBBLE_brightness_set (step, true);
        }
        else
        {
            goto USAGE;
        }
    }
    else
    {
USAGE:
        PRINT (INFO, "Usage: bubble [OPTION...]\r\n");
        PRINT (INFO, "  -e | --enable        Enables bubble LED\r\n");
        PRINT (INFO, "    <%%b>               true  = enable\r\n");
        PRINT (INFO, "                       false = disable\r\n");
        PRINT (INFO, "  -s                   Sets bubble UV LED brightness step\r\n");
        PRINT (INFO, "    <%%d>               Brightness step [0-%d]\r\n", BUBBLE_BRIGHTNESS_STEPS);
    }
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
static void _bubble_config_read (void)
{
    ERRNO_e         err;
    BUBBLE_config_s cfg;

    err = FS_open_or_create (BUBBLE_FILENAME, &_bubble_file, sizeof (BUBBLE_config_s), FS_FILE_DEPTH_DEFAULT);
    if (err == ERRNO_SUCCESS)
    {
        /* Read file. */
        err = FS_read (_bubble_file, (uint8_t *) &cfg, 0, sizeof (BUBBLE_config_s));

        /* Check file entry is valid. */
        if (err != ERRNO_SUCCESS)
        { 
            /* Write defaults to file. */
            _bubble_config_write ();
            return;
        }

        /* Validate read file. */
        if (cfg.brightness_step > BUBBLE_BRIGHTNESS_STEPS)
        {
            /* Write defaults to file. */
            _bubble_config_write ();
            return;
        }

        /* Update bubble configuration to what was read from flash and validated. */
        _bubble_config = cfg;
    }
    else if (err == ERRNO_SIZE_MISMATCH)
    {
        /* Config has been modified, need to delete old file and write new configuration. */
        err = FS_delete (BUBBLE_FILENAME);

        if (err != ERRNO_SUCCESS)
        {
            /* Something else is going on... */
            PRINT (ERROR, "Failure to delete old bubble file.\r\n");
            return;
        }

        /* Create new file. */
        err = FS_open_or_create (BUBBLE_FILENAME, &_bubble_file, sizeof (BUBBLE_config_s), FS_FILE_DEPTH_DEFAULT);
        if (err != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to create bubble configuration file.\r\n");
        }
        else
        {
            PRINT (INFO, "New bubble file created successfully\r\n");
        }

        /* Write default config to file. */
        _bubble_config_write ();
    }
    else
    {
        /* Write defaults to file. */
        _bubble_config_write ();
    }
}

static void _bubble_config_write (void)
{
    if (FS_write (_bubble_file, (uint8_t *) &_bubble_config) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failed to write bubble configuration to file.\r\n");
    }
}
