///
/// Simple WiFi Configuration features
///
#ifndef WIFICONFIGURATION_H
#define WIFICONFIGURATION_H

#include <ESP8266WiFi.h>

#define CFG_NAMESIZE 32
#define CFG_SSIDSIZE 32
#define CFG_PASSSIZE 64
#define CFG_UPDURLSIZE 64
#define CFG_NTPSSIZE 64

/// Provides a handy interface for WiFi configuration and user NV data.
///
/// Because the WiFi needs to have its configuration managed through
/// non-volatile memory.
/// 
/// @code
/// WiFiConfiguration config;
/// ...
/// config.load();
/// String ssid = config.getSSID();
/// String pass = config.getPassword();
/// ...
/// WiFi.begin(ssid.c_str(), pass.c_str());
/// 
/// @endcode
///
class ConfigManager {
public:

	/// Data structure that is used for 'factory reset' of this node,
	/// where all setup data is erased. This reset state can include
	/// specific settings, such as the device name (as it will appear
	/// on the network, and the default software update server, and so on.
	typedef struct {
		const char * name;		///< the device name
		const char * UPDURL;	///< the URL of a SW update server
		const char * NTPURL;	///< The URL of an NTP server
	} ConfigDefaults_t;

	/// load checks the version of the image for validity.
	///
	typedef enum {
		MATCHING,		///< The current version matches the expected version
		IS_OLDER,		///< [Newer firmware] detected an older configuration
		IS_NEWER,		///< detected a configuration that is newer than this app knows.
		IS_BROKEN,		///< format check failed, this image is bad.
	} FirmwareCheck_t;

	/// Instantiate the WiFi Configuration Manager, which also manages
	/// user non-volatile storage.
	///
	/// This creates a RAM cache (typically 512B), where information is
	/// held. This is then saved to non-volatile (save), or overwritten 
	/// from non-volatile (load).
	///
	/// Instantiation does not automatically load from non-volatile.
	///
	/// @param[in] defaults is an optional pointer to the default information.
	///
	ConfigManager(const ConfigDefaults_t * defaults = NULL);
	
	/// Destructor, which is usually not used in an embedded system.
	///
	~ConfigManager();
	
	/// loads the information from non-volatile storage into RAM
	///
	/// @returns status of the firmware check. @see FirmwareCheck_t
	///
	FirmwareCheck_t load();
	
	/// get the current firmware version
	///
	/// specific software may be able to upgrade (or even downgrade) the
	/// non-volatile data storage format.
	///
	/// @returns the format version number (only useful if load is not broken.
	///
	uint8_t getFormatVersion();
	
	
	/// saves the information to non-volatile.
	///
	void save();
	
	/// Resets the RAM information to factory defaults, completely wiping 
	/// all data, so it cannot be extracted. 
	///
	/// To complete the wipe, be sure to issue to the save command.
	///
	void factoryReset();
	
	/// get the name of this device, as it will appear on the network.
	///
	/// @returns string of the name.
	///
	String getName();
	
	/// set the name of this device, to a maximum length of CFG_NAMESIZE.
	///
	/// @param[in] _name is the name to assign it.
	///
	void setName(String _name);
	
	/// get the ssid that this node is assigned to.
	///
	/// @returns the SSID, or a pointer to NULL if none set.
	///
	String getSSID();
	
	/// set the ssid that this node is assigned to.
	///
	/// @param[in] _ssid to assign this device to, to a maximum length of CFG_SSIDSIZE
	///
	void setSSID(String _ssid);
	
	/// get the passcode that this node will use with the joined ssid.
	///
	/// @returns the passcode.
	///
	String getPassword();
	
	/// set the passcode that this node uses.
	///
	/// @param[in] passcode to assign this device to, to a maximum length of CFG_PASSSIZE
	///
	void setPassword(String _password);
	
	/// get the URL that this node will use for software updates
	///
	/// @returns the url.
	///
	String getURL();

	/// set the URL that this node will use for software updates
	///
	/// @param[in] _url to assign this device to, to a maximum length of CFG_UPDURLSIZE
	///
	void setURL(String _url);
	
	/// get the URL of the NTP server that this node will use for setting the clock
	///
	/// @returns the url.
	///
	String getNTPServerName();

	/// set the URL of the NTP server that this node will use for software updates
	///
	/// @param[in] _url to assign this device to, to a maximum length of CFG_NTPSSIZE
	///
	void setNTPServerName(String _url);
	
	/// get a pointer to the portion of the data that will be saved to non-volatile
	///
	/// This points to the block of memory that is for user settings. The user code 
	/// will typically create a structure, and overlay it on this location.
	///
	/// The size is fixed at compile time, but may be queried with getUserDataSize()
	/// to ensure that there is no buffer overrun.
	///
	///	@returns the pointer to the memory for the user to use.
	///
	uint8_t * getUserDataHandle();
	
	/// get the size of the user data block that can be saved to non-volatile.
	///
	///	@returns the size (in bytes) of the user space.
	///
	int getUserDataSize();
	
private:

	#define EEROM_SIZE 512

	// Change this when it is required to force a reinitialization
	// 
	#define EXPECTED_FMT 0x04        

	#define SYSTEMSIZE (2 + CFG_NAMESIZE + CFG_SSIDSIZE + CFG_PASSSIZE + CFG_UPDURLSIZE + CFG_NTPSSIZE)
	#define USERSIZE (EEROM_SIZE - SYSTEMSIZE)

	typedef struct {
		uint8_t format[2];					// Format of this data structure, and ~format check
		struct info {
			char name[CFG_NAMESIZE];		// Network Discoverable Name of this node
			char ssid[CFG_SSIDSIZE];		// SSID of the saved network [max length defined by standard]
			char pass[CFG_PASSSIZE];		// PASScode of the saved network
			char UPDURL[CFG_UPDURLSIZE];		// URL of the trusted server with code updates
			char NTPURL[CFG_NTPSSIZE];		// Name or IP of the NTP Server
		} info;
		uint8_t userData[USERSIZE];			// User data here and to the EEROM_SIZE end.
	} NVConfig_04_t;			// This layout matches EXPECTED_FMT == 04

	NVConfig_04_t Configuration;		// the instantiation of the Configuration
	
	const ConfigDefaults_t * defaults;
};

#endif // !WIFICONFIGURATION_H
