/**
 *   This program, AMController libraries, example sketches (“The Software”) and the related documentation (“The Documentation”) are supplied to you
 *   by the Author in consideration of your agreement to the following terms, and your use or installation of The Software and the use of The Documentation
 *   constitutes acceptance of these terms.
 *   If you do not agree with these terms, please do not use or install The Software.
 *   The Author grants you a personal, non-exclusive license, under author's copyrights in this original software, to use The Software.
 *   Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by the Author, including but not limited to any
 *   patent rights that may be infringed by your derivative works or by other works in which The Software may be incorporated.
 *   The Software and the Documentation are provided by the Author on an "AS IS" basis.  THE AUTHOR MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT
 *   LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE SOFTWARE OR ITS USE AND OPERATION
 *   ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
 *   REPRODUCTION AND MODIFICATION OF THE SOFTWARE AND OR OF THE DOCUMENTATION, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 *   STRICT LIABILITY OR OTHERWISE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

/**
 * Version: 1.0
 *
 * For more information write to fabboco@gmail.com
 **/

#ifndef CONFIGURATION_H
#define CONFIGURATION_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <hardware/flash.h>
#include <hardware/sync.h>

#define FLASH_ID 12393
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) // Flash-based address of the last sector

struct configuration_data
{
    uint16_t id = FLASH_ID;
    float i_dc;
    float v_dc;
    float max_power;
};

typedef struct configuration_data configuration_data;

class Configuration
{
private:
    configuration_data configuration;

public:
    Configuration();
    ~Configuration();

    // Returns the stored voltage dc offset
    float getVdc();

    // Sets the stored voltage dc offset
    void setVdc(float v);

    // Returns the stored current dc offset
    float getIdc();

    // Sets the stored current dc offset
    void setIdc(float v);

    // Returns the maximum power consumtion allowed
    float getMaxPower();
    
    // Sets the maximum power consumtion allowed
    void setMaxPower(float power);
};

Configuration::Configuration()
{
    int flash_address;
    configuration_data *pconfig_data;

    flash_address = XIP_BASE + FLASH_TARGET_OFFSET;
    pconfig_data = (configuration_data *)flash_address;

    if (pconfig_data->id == FLASH_ID)
    {
        printf("Flash already configured\n");

        configuration.i_dc = pconfig_data->i_dc;           // Calibrated current's DC offset
        configuration.v_dc = pconfig_data->v_dc;           // Calibrated voltage's DC offset
        configuration.max_power = pconfig_data->max_power; // Max power allowed
        memcpy(&configuration, pconfig_data, sizeof(configuration_data));
    }
    else
    {
        printf("Flash not configured yet\n");

        configuration_data initial_config_data;
        initial_config_data.i_dc = 1.65;      // Default current's DC offset
        initial_config_data.v_dc = 1.65;      // Default voltage's DC offset
        initial_config_data.max_power = 1100; // Default max power allowed

        uint32_t interrupts = save_and_disable_interrupts();
        flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);

        uint8_t buffer[FLASH_PAGE_SIZE];
        memcpy(buffer, &initial_config_data, sizeof(configuration_data));
        flash_range_program(FLASH_TARGET_OFFSET, buffer, FLASH_PAGE_SIZE);
        restore_interrupts(interrupts);

        this->configuration = initial_config_data;
    }
}

Configuration::~Configuration()
{
}

void Configuration::setVdc(float v)
{
    configuration.v_dc = v;

    configuration_data *pconfig_data;
    pconfig_data = (configuration_data *)XIP_BASE + FLASH_TARGET_OFFSET;
    uint32_t interrupts = save_and_disable_interrupts();
    flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);

    uint8_t buffer[FLASH_PAGE_SIZE];
    memcpy(buffer, &configuration, sizeof(configuration_data));
    flash_range_program(FLASH_TARGET_OFFSET, buffer, FLASH_PAGE_SIZE);
    restore_interrupts(interrupts);
}

float Configuration::getVdc()
{
    return configuration.v_dc;
}

void Configuration::setIdc(float i)
{
    configuration.i_dc = i;

    configuration_data *pconfig_data;
    pconfig_data = (configuration_data *)XIP_BASE + FLASH_TARGET_OFFSET;
    uint32_t interrupts = save_and_disable_interrupts();
    flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);

    uint8_t buffer[FLASH_PAGE_SIZE];
    memcpy(buffer, &configuration, sizeof(configuration_data));
    flash_range_program(FLASH_TARGET_OFFSET, buffer, FLASH_PAGE_SIZE);
    restore_interrupts(interrupts);
}

float Configuration::getIdc()
{
    return configuration.i_dc;
}

void Configuration::setMaxPower(float power)
{
    configuration.max_power = power;

    configuration_data *pconfig_data;
    pconfig_data = (configuration_data *)XIP_BASE + FLASH_TARGET_OFFSET;
    uint32_t interrupts = save_and_disable_interrupts();
    flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);

    uint8_t buffer[FLASH_PAGE_SIZE];
    memcpy(buffer, &configuration, sizeof(configuration_data));
    flash_range_program(FLASH_TARGET_OFFSET, buffer, FLASH_PAGE_SIZE);
    restore_interrupts(interrupts);
}

float Configuration::getMaxPower()
{
    return configuration.max_power;
}

#endif