/** 
  * @file    pin.c
  * @brief   Pin 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 "pin.h"


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


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


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


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static FS_file_t    _pin_file;
static PIN_config_s _pin_config = (PIN_config_s) {
    .brightness_step = PIN_BRIGHTNESS_STEP_DEFAULT,
    .reserved_0      = 0xFF,
    .reserved_1      = 0xFF,
    .reserved_2      = 0xFF
};


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
static void _pin_config_read (void);
static void _pin_config_write (void);


/*--------------------------------------------------------------------------------------------------
 * PUBLIC FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
void PIN_init (void)
{
    PRINT (INFO, "Initializing PIN...\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 pin. */
    _pin_config_read ();
}

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

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

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

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

uint8_t PIN_brightness_get (void)
{
    return _pin_config.brightness_step;
}

void PIN_enable (bool en)
{
    float duty;

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

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

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

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


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
static void _pin_config_read (void)
{
    ERRNO_e      err;
    PIN_config_s cfg;

    err = FS_open_or_create (PIN_FILENAME, &_pin_file, sizeof (PIN_config_s), FS_FILE_DEPTH_DEFAULT);
    if (err == ERRNO_SUCCESS)
    {
        /* Read file. */
        err = FS_read (_pin_file, (uint8_t *) &cfg, 0, sizeof (PIN_config_s));

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

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

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

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

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

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

static void _pin_config_write (void)
{
    if (FS_write (_pin_file, (uint8_t *) &_pin_config) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failed to write pin configuration to file.\r\n");
    }
}
