/** 
  * @file    fiber.c
  * @brief   Fiber LED Module
  * @author  Kenny Tran
  * @date    Jul 18 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 "fiber.h"


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


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


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


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static FS_file_t      _fiber_file;
static FIBER_config_s _fiber_config = (FIBER_config_s) {
    .brightness_step = FIBER_BRIGHTNESS_STEP_DEFAULT,
    .reserved_0      = 0xFF,
    .reserved_1      = 0xFF,
    .reserved_2      = 0xFF
};


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
static void _fiber_config_read (void);
static void _fiber_config_write (void);


/*--------------------------------------------------------------------------------------------------
 * PUBLIC FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
void FIBER_init (void)
{
    PRINT (INFO, "Initializing FIBER...\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 fiber. */
    _fiber_config_read ();
}

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

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

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

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

uint8_t FIBER_brightness_get (void)
{
    return _fiber_config.brightness_step;
}

void FIBER_enable (bool en)
{
    float duty;

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

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

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

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


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
static void _fiber_config_read (void)
{
    ERRNO_e        err;
    FIBER_config_s cfg;

    err = FS_open_or_create (FIBER_FILENAME, &_fiber_file, sizeof (FIBER_config_s), FS_FILE_DEPTH_DEFAULT);
    if (err == ERRNO_SUCCESS)
    {
        /* Read file. */
        err = FS_read (_fiber_file, (uint8_t *) &cfg, 0, sizeof (FIBER_config_s));

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

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

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

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

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

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

static void _fiber_config_write (void)
{
    if (FS_write (_fiber_file, (uint8_t *) &_fiber_config) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failed to write fiber configuration to file.\r\n");
    }
}
