/**
    This header and its corresponding source file is responsbile for managing a simple flash storage 
    solution for remembering the most recent (and only the most recent) PWM channel data.
*/
#ifndef SIMPLE_FLASH_STORAGE_H
#define SIMPLE_FLASH_STORAGE_H

#include "nrf_fstorage_sd.h"

#define NRF52840_PAGE_SIZE 4096	    //from infocenter
#define NUM_CHANNELS 6		    

/** 
    START_ADDR must be the first address of a page, and the address chosen should not be
    overlapping with program, softdevice, or mesh data addresses
*/
#define START_ADDRESS 0x0005a000
#define NUM_PAGES 2
#define END_ADDRESS ((START_ADDRESS + NUM_PAGES * NRF52840_PAGE_SIZE) - 1)
#define ENTRY_SIZE (sizeof(flash_entry_t))

typedef uint8_t level_t;

extern nrf_fstorage_t fstorage;

/** Structure that is to be written into flash memory */
typedef struct {
    uint16_t label; //For now, 0xFFFF means unwritten, 0xFACE means written to, is gonna be completely changed later to display more meaningful information later
    level_t channel_vals[NUM_CHANNELS];
} flash_entry_t;

/*******************************************************************************************************
    Description of how the flash storage is managed:
    
    The address ranges in which this flash storage manager code is responsible for is defined by START_ADDR 
    and NUM_PAGES. For example, if START_ADDR is 0x0003d000 and NUM_PAGES is 3, then the address range that
    the flash memory manages is from 0x0003d000 to 0x0003ffff (three pages worths of flash address locations).

    An address pointer (named first_empty_addr) to the first empty page is constantly maintained throughout 
    the lifetime of the program. 

    Upon initialization, the program searches for the first unwritten location and initializes 
    first_empty_addr to that memory address. When incoming data is stored (data is stored
    in the form of the struct flash_entry_t), it stores the incoming data into the location pointed by 
    first_empty_addr. After the write operation is complete, first_empty_addr is increment to point 
    to the next unwritten address. When we want to retrieve the data, we read the last written-to address
    location which is almost always the address pointed by first_empty_addr - ENTRY_SIZE.


    ILLUSTRATION of how this works (in this example we assume each data entry is 8 bytes):


			   |			|
	Retrieve operations|			| first_empty_addr points here
	are read from here |			| Storage operations writes to here
			   V		        V
    +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+
    |0x3d000| |0x3d004| |0x3d008| |0x3d00c| |0x3d010| |0x3d014| |0x3d018| |0x3d01c|
    |written| |written| |written| |written| | empty | | empty | | empty | | empty |
    +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+

    
					       |		    |
			Retrieve operations    |		    | first_empty_addr will point here after storing
			will be read from here |		    | The next storage operation will write to here
					       V		    V
    +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+
    |0x3d000| |0x3d004| |0x3d008| |0x3d00c| |0x3d010| |0x3d014| |0x3d018| |0x3d01c|
    |written| |written| |written| |written| |written| |written| | empty | | empty |
    +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ +-------+

    When we fill out all available addressses, upon storing the next entry, there will be an erase operation
    of all the flash addresses in which this program is responsible for and first_empty_addr will be reset to the
    starting address before storing the aforementioned entry
********************************************************************************************************/

/**
    Retrieve the stored PWM value from flash memory

    @param[out]  values     The stored level values retrieved from flash memory
    @retval NRF_SUCCESS     The level values were successfully retrieved, otherwise returns an error code, see nrf_error.h
*/
ret_code_t flash_retrieve_data(level_t * values);

/**
    Stores the given PWM value into flash memory. Cleans up data automatically if there is no
    space for the new entry.

    @param[in]  values     The level values to be stored into flash memory
    @retval NRF_SUCCESS    The level values were successfully stored into flash, otherwise returns an error code, see nrf_error.h
*/
ret_code_t flash_store_data(const level_t * values);

/**
    Clears the flash storage data that this program manages. 
    Is called by flash_store_data automatically if there is no room but can also be explicitly called by any application using
    this flash storage manager

    @retval NRF_SUCCESS    The flash is successfully cleaned, otherwise returns an error code, see nrf_error.h
*/
ret_code_t flash_clean_data(void);


/**
    Gets the address pointed by first_empty_addr. Useless outside of some specific testing

    @returns	first_empty_addr
*/
uint32_t flash_addr_pointer(void);

/**
    Initializes this simple flash storage management module, and allows for writes and reads towards the 
    section of flash memory set by START_ADDR and END_ADDR. See the general description above and the source
    file for more details about the implementation

    It is the responsbility of the caller to initalize the softdevice and logging modules
    before initializing the flash storage module
*/
void flash_init(void);

#endif