/*
 * StoredConfig.cpp
 *
 */


#include "StoredConfig.h"

#include "bsp_storable_config.h"
#include "bsk_sprintf.h"
#include "mex.h"
#include "mrk.h"
#include "rtlog.h"

#include "rtg_high_resolution_timer.h"

#include <stdint.h>
#include <string.h>
#include <stddef.h>

#ifdef __MINGW32__
#pragma GCC optimize("-O0")
#endif


#ifdef _TMS320C6X
//DSP has classic NOR memory that does not support partial and infinite storing
static int multiple_storable_data_save(const void* data, unsigned int size, unsigned int offset)
{
	return 0;
}
#else
static int multiple_storable_data_save(const void* data, unsigned int size, unsigned int offset)
{
	return bsp_raw_storable_data_save(data, size, offset);
}
#endif

struct history_index_t
{
	unsigned int begin;
	unsigned int entrysize;
	unsigned int pos;
	unsigned int counter;
	unsigned int start_counter;
	unsigned int spare[3];

	unsigned int pos_check;
};

struct history_entry_t
{
	unsigned int counter;
	char timestamp[32];
	char marker0;
	char marker1;
	char evn_name[10];
	char msg[32];
	char decoder_name[8];
	unsigned int decoder_id;
	unsigned int data_size;
	unsigned int counter_;
	uint32_t data[32];
};

#define k_nvm_marker 0xAFF0CFF0
#define k_nvm_marker_end 0xAFF0CFF0


struct env_var_t
{

	uint32_t marker;
	char name[16];
	unsigned long value;
};

struct env_store_t
{
	enum { k_max_env_vars=64};

	env_var_t vars[k_max_env_vars];
};

class StoredConfigImplementation
{
public:
    enum {
    	k_max_txt_data = 4*1024,
    	k_max_history_entry=256,
    	k_max_history_depth=128,

    	k_minimum_user_data=64*1024,

    	//k_max_env_data=2048,
    };

	struct header_t
	{
		uint32_t marker;
		uint32_t size;
		uint32_t ver_id;
	};

    template<unsigned int S_> struct block_format_t	//Warning: S_ shall be multiple of sizeof(uint32_t)
    {
    	enum { max_size=S_};
    	header_t h;
    	uint8_t raw[S_];
    	uint32_t end_marker;
    };

    typedef block_format_t<StoredConfig::k_max_app_config_size> app_cfg_fmt_t;

    typedef block_format_t<k_minimum_user_data> bin_data_fmt_t;

    typedef block_format_t<sizeof(env_store_t)> env_fmt_t;

    volatile bool env_loaded;
    env_fmt_t env_cache;
    unsigned int env_overrun;

    struct data_fmt_t
    {
    	unsigned char hindex[256];
    	unsigned char hslots[k_max_history_entry*k_max_history_depth];
    	//unsigned char binData[k_max_binary_data];
    	unsigned char gap[256];
    	app_cfg_fmt_t appCfg;
    	unsigned char gap1[256];
    	env_fmt_t env;
    	unsigned char gap2[128];
    	bin_data_fmt_t usrData;
    };

    int disabled;

    char script_buffer[2048];

    //const image_fmt_t* cfg;
    const char* env_start;
    const char* script_start;

    app_cfg_fmt_t appcfg;

    mrk_mutex_t hmutex;

    char timestamp[32];

    unsigned int session_counter;

    history_index_t hindex;
    history_entry_t hentry;

    unsigned int queue_index;
    history_entry_t hqueue[16];

    StoredConfig::timestamp_t ts;

    unsigned long t_read;
    unsigned long t_write;

    void histSetTime(const char* t)
    {
    	mrk_sts_t r=mrk_mutex_acquire(&hmutex, 100);
    	if (r==mrk_ok)
    	{
    		strncpy(timestamp, t, sizeof timestamp);
    		mrk_mutex_release(&hmutex);
    	}
    }

    bool histEnqueue(char marker, const char* name, const char* msg, const char* decoder=0, unsigned int decoder_id=0, const void* data=0, unsigned int data_len=0)
    {
    	if (disabled)
    	{
    		return false;
    	}

		bool ok=false;
    	mrk_sts_t r=mrk_mutex_acquire(&hmutex, 100);
    	if (r==mrk_ok)
    	{
    		if (queue_index<8)
    		{
    			histAdd(hqueue[queue_index], marker, name, msg, decoder, decoder_id, data, data_len);
    			++queue_index;
    			ok=true;
    		}
    		mrk_mutex_release(&hmutex);
    	}
    	return ok;
    }

    void histFlush()
    {
    	if (disabled)
    		return;

    	//mrk_sts_t r=mrk_mutex_acquire(&hmutex, 100);
    	//if (r==mrk_ok)
    	{
    		if (queue_index>8)
    		{
    			rtlog_printf("CFG", rtlog_error, "History BUG!!");
    		}
    		else
    		{
    	    	mrk_sts_t r=mrk_mutex_acquire(&hmutex, 100);
    	    	if (r==mrk_ok)
    	    	{
    	    		unsigned int index=queue_index;
    	    		mrk_mutex_release(&hmutex);
					for(unsigned int i=0; i<index; ++i)
					{
						histAdd(hqueue[i]);
					}
	    	    	mrk_sts_t r=mrk_mutex_acquire(&hmutex, 100);
	    	    	if (r==mrk_ok)
	    	    	{
	    	    		queue_index=0;
	    	    		mrk_mutex_release(&hmutex);
	    	    	}
    	    	}
    		}
    		//mrk_mutex_release(&hmutex);
    	}
    }

    unsigned int hoffset(int n)
    {
    	return 256+k_max_history_entry*n;
    }

    void histClear()
    {
    	if (disabled)
    		return;

    	mrk_mutex_acquire(&hmutex, mrk_wait_forever);
    	memset(&hindex, 0,sizeof hindex);
    	multiple_storable_data_save(&hindex, sizeof hindex, 0);
    	mrk_mutex_release(&hmutex);
    }

    void histAdd(history_entry_t& h, char marker, const char* name, const char* msg, const char* decoder_name=0, unsigned int decoder_di=0, const void* data=0, unsigned int dlen=0)
    {
    	if (disabled)
    		return;

    	rtg_hrt_microseconds_t now=rtg_hrt_to_microseconds_longlong(rtg_hrt_get());

    	memset(&h, 0, sizeof h);
    	h.marker0=marker;
    	h.marker1='-';
    	//memset(&h.evn_name, ' ', sizeof h,evn_name);
    	h.evn_name[0]=0;
    	strncpy(h.evn_name, name, sizeof h.evn_name);
    	h.evn_name[9]='0';
    	++hindex.counter;
    	unsigned int hc=session_counter;
    	++session_counter;
    	hc=hc & 0x0FFFF;
    	hc=hc|(hindex.start_counter<<16);
    	h.counter=hc; //++hindex.counter;
    	h.counter_=h.counter-1;
    	h.data_size=0;
    	strncpy(h.msg, msg, sizeof(h.msg));
    	h.msg[(sizeof h.msg)-1]=0;
    	//bsk_snprintf(h.timestamp, sizeof h.timestamp, "%s %u", timestamp, (unsigned int)(now/1000000));
    	StoredConfig::timestamp_t now_ts;
    	now_ts.raw=ts.raw;

		bsk_snprintf(h.timestamp, sizeof h.timestamp, "%u:%02u:%02u %u-%u-%u %u",
				(unsigned int)now_ts.str.hours, (unsigned int)now_ts.str.minutes, (unsigned int)now_ts.str.seconds,
				(unsigned int)now_ts.str.day, (unsigned int)now_ts.str.month, (unsigned int)now_ts.str.year,
				(unsigned int)(now/1000000));

    	h.decoder_id=decoder_di;
    	if (decoder_name)
    		strncpy(h.decoder_name, decoder_name, sizeof h.decoder_name);
    	if (data && dlen)
    	{
    		unsigned int s=dlen;
    		if (s>sizeof h.data)
    		  s=sizeof h.data;
    		h.data_size=s;
    		memcpy(h.data, data, s);
    	}
    }

    void histAdd(history_entry_t& h)
    {
    	if (disabled)
    		return;

    	//++hindex.counter;
    	unsigned int pos=hindex.pos;
    	if (pos>=k_max_history_depth)
    		pos=0;
    	unsigned int offset=hoffset(pos); //256+pos*sizeof(history_entry_t);
    	unsigned int s=sizeof h;
    	s=(s-(sizeof h.data))+h.data_size;
    	if (s>k_max_history_entry)
    		s=k_max_history_entry;
    	multiple_storable_data_save(&h, s, offset);
    	++pos;
    	if (pos>=k_max_history_depth)
    		pos=0;
    	hindex.pos=pos;
    	hindex.pos_check=pos;
    	multiple_storable_data_save(&hindex, sizeof hindex, 0);

    }

    void histAdd(char marker, const char* name, const char* msg)
    {
    	histAdd(hentry, marker, name, msg);
    	histAdd(hentry);
    }

    void initialize()
    {
    	if (disabled)
    		return;
    	mrk_mutex_register(&hmutex, "HIST");

    	//const void* p=bsp_raw_storable_config_load();
    	memset(script_buffer, 0, sizeof script_buffer);

    	bsp_raw_storable_data_load(&hindex, sizeof hindex, 0);
    	if ((hindex.begin!=0x48483030)	//"HH00"
    			|| (hindex.pos!=hindex.pos_check)
    			|| (hindex.entrysize!=sizeof(history_entry_t)))
    	{
    		memset(&hindex, 0, sizeof hindex);
    		hindex.begin=0x48483030;
    		hindex.entrysize=sizeof(history_entry_t);
        	histAdd('!', "HINIT", "NVM history initialized");
    	}
    	else
    		++hindex.start_counter;
    	//histAdd('!', "START", "power up...");

#if 0
        cfg=reinterpret_cast<const image_fmt_t*>(bsp_raw_storable_config_load());
        const char* p=cfg->txt_data;
        if (strcmp(p, "[ENV]"))
        {
            env_start=&p[5];
            p=env_start;
            for(int i=0; (i<k_max_txt_data) && (p[i]); ++i)
            {
                if (p[i]=='[')
                {

                }
            }
        }
#endif
    }

    int getenv(const char* /*name*/)
    {
        return 0;
    }

    void startConfig(bool also_invalid=false)
    {
    	if (disabled)
    		return;
        const char* p=(const char*)bsp_raw_storable_config_load();
        bool executed=false;
        if (p)
        {
            if ((p[0]==';') && (p[1]=='+'))
                if ((p[2]=='+') || ((p[2]=='-') && also_invalid))
                {
                    memcpy(script_buffer, p, sizeof script_buffer);
                    script_buffer[2039]=0;
                    mex_script_inject(p, 2040);
                    executed=true;
                }
        }
        if (!executed)
        	rtlog_printf("CFG", rtlog_info|rtlog_breaking_news, "start script not executed");
    }

    struct asynch_save_info_t
    {
    	const void* source;
    	unsigned int size;
    	unsigned int version_id;
    	unsigned int offset;

    	volatile int pending;
    };
    asynch_save_info_t asynch_save_info;

    static void __monitor_invokable_init(void) __attribute__((used,noinline));

    static void __monitor_invokable_invalidate(void) __attribute__((used,noinline));
    static void __monitor_invokable_validate(void) __attribute__((used,noinline));

    static void __monitor_invokable_exec(void) __attribute__((used,noinline));
    static void __monitor_invokable_hlist(void) __attribute__((used,noinline));
    static void __monitor_invokable_hflush(void) __attribute__((used,noinline));
    static void __monitor_invokable_hclear(void) __attribute__((used,noinline));

    static void __monitor_invokable_htest(void) __attribute__((used,noinline));

    static void __monitor_invokable_setenv(void) __attribute__((used,noinline));
    static void __monitor_invokable_setenvf(void) __attribute__((used,noinline));

    static void __monitor_invokable_bintest(void) __attribute__((used,noinline));

};

static StoredConfigImplementation autoMonitorable_storedCfg;
#define cx autoMonitorable_storedCfg

void StoredConfigImplementation::__monitor_invokable_init()
{
	cx.initialize();
}

void StoredConfigImplementation::__monitor_invokable_exec()
{
	cx.startConfig(true);
}

void StoredConfigImplementation::__monitor_invokable_invalidate(void)
{
    const char* p=(const char*)bsp_raw_storable_config_load();
    memcpy(autoMonitorable_storedCfg.script_buffer, p, sizeof autoMonitorable_storedCfg.script_buffer);
    autoMonitorable_storedCfg.script_buffer[2]='-';
    bsp_raw_storable_config_save(autoMonitorable_storedCfg.script_buffer, sizeof autoMonitorable_storedCfg.script_buffer);
}

void StoredConfigImplementation::__monitor_invokable_validate(void)
{
    const char* p=(const char*)bsp_raw_storable_config_load();
    memcpy(autoMonitorable_storedCfg.script_buffer, p, sizeof autoMonitorable_storedCfg.script_buffer);
    autoMonitorable_storedCfg.script_buffer[2]='+';
    bsp_raw_storable_config_save(autoMonitorable_storedCfg.script_buffer, sizeof autoMonitorable_storedCfg.script_buffer);
}

void StoredConfigImplementation::__monitor_invokable_hflush(void)
{
	cx.histFlush();
}

void StoredConfigImplementation::__monitor_invokable_hclear(void)
{
	cx.histFlush();
}

void StoredConfigImplementation::__monitor_invokable_hlist(void)
{
	mex_printf("pos=%u, counter=%u\n", cx.hindex.pos, cx.hindex.counter);
	unsigned int nh=cx.hindex.counter;
	if (nh>=k_max_history_depth)
		nh=k_max_history_depth;
	unsigned int pos=cx.hindex.pos;

	for(unsigned int n=0; n<nh; ++n)
	{
	    history_entry_t entry;
	    pos=pos-1;
	    if (pos>=k_max_history_depth)
	    	pos=(k_max_history_depth-1);
    	unsigned int offset=cx.hoffset(pos);
    	bsp_raw_storable_data_load(&entry, sizeof entry, offset);
    	if (entry.counter_!=entry.counter-1)
    	{
        	mex_printf("%u: invalid entry\n", entry.counter);
    	}
    	else
    	{
    		entry.msg[31]=0;
    		unsigned int hc=entry.counter & 0x0FFFF;
    		unsigned int sc=entry.counter>>16;
    		mex_printf("%u/%u: %s: %c-%s: %s\n", sc, hc, entry.timestamp, entry.marker0, entry.evn_name, entry.msg);
    		if (entry.data_size)
    		{
    			unsigned int s=entry.data_size;
    			if (s>sizeof entry.data)
    				s=sizeof entry.data;
    			s=s/sizeof(uint32_t);
    			mex_printf("  DATA: %u, decoder=%s (%u)\n", entry.data_size, entry.decoder_name, entry.decoder_id);
    			const uint16_t* p=reinterpret_cast<const uint16_t*>(&entry.data[0]);
    			for(unsigned int i=0; i<s; i+=2)
    			{
    				if (i>0 && ((i & 0x3)==0))
    					mex_printf("\n");
    				//mex_printf("    0x%8.8X", entry.data[i]);
    				mex_printf("    0x%4.4X 0x%4.4X", p[i], p[i+1]);
    			}
    			mex_printf("\n");
    		}
    	}
	}
}

void StoredConfigImplementation::__monitor_invokable_htest()
{
	static uint32_t data[8];

	for(int i=0; i<(k_max_history_depth/2)+1; ++i)
	{
		data[0]=i;
		data[7]=i;
		StoredConfig::instance().historySaveExt('T', "TEST", "null", 0, data, sizeof data, "inject %d", i);
		StoredConfig::instance().historyFlush();
	}
}

void StoredConfigImplementation::__monitor_invokable_bintest()
{
	struct
	{
		uint32_t data[4];
	} test_struct;
	unsigned int size=sizeof test_struct;

	bool ok=StoredConfig::instance().loadRawBinaryData(&test_struct, size, 1, 0);
	if (!ok)
	{
		mex_printf("First load failed...\n");
		for(int i=0; i<4; ++i)
			test_struct.data[i]=i;
	}
	else
	{
		mex_msg_printf(mex_msg_success, "NVRTEST", "Load OK");
		for(int i=0; i<4; ++i)
		{
			//mex_printf(" %u", test_struct.data[i]);
			++test_struct.data[i];
		}
		//mex_printf("\n");
	}
	ok=StoredConfig::instance().saveRawBinaryData(&test_struct, size, 1, 0);
	if (ok)
	{
		mex_msg_printf(mex_msg_info, "NVRTEST", "Stored data!\n");
	}
	else
		mex_msg_printf(mex_msg_error, "NVRTEST", "Store failed!\n");
}

static unsigned int env_prt_unsign;
static unsigned int env_prt_sign;

static void env_print(unsigned int n)
{
	env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	char state='-';
	if (s.vars[n].marker==1)
		state='?';
	else if (s.vars[n].marker==2)
		state='+';
	unsigned long iv=s.vars[n].value;
	void* p=&s.vars[n].value;
	float fv=*reinterpret_cast<float*>(p);

	mex_printf("%2d: %c %16.16s: 0x%X", n, state, s.vars[n].name, iv);
	if (env_prt_sign)
		mex_printf("(%d)\n", (signed long)iv);
	else if (env_prt_unsign)
		mex_printf("(%u)\n", iv);
	else
		mex_printf("(%f)\n", fv);
}

void StoredConfigImplementation::__monitor_invokable_setenv(void)
{
	static unsigned int n;
	static unsigned long v;
	static unsigned int clr;
	static unsigned int save;
	static const mex_cmd_param_descriptor_t cmd_table[] =
	{
	{ "n", "var index", 0, &n, &mex_mt_uint,0},
	{ "v", "value", 0, &v, &mex_mt_uint,0},
	{ "c", "clear", mex_cmd_qualifier, &clr, 0, 0},
	{ "s", "save", mex_cmd_qualifier, &save, 0, 0},

	{ "u", "unsigned", mex_cmd_qualifier, &env_prt_unsign, 0, 0},
	{ "d", "signed", mex_cmd_qualifier, &env_prt_sign, 0, 0},

    { 0,0,0,0,0,0 }
	};

	auto res = mex_cmd_parse_params(cmd_table, mex_get_raw_command_arguments(), 0);
	if (mex_param_help())
	{
		mex_show_help(cmd_table);
		return;
	}

	env_prt_unsign=mex_param_is_present(&env_prt_unsign);
	env_prt_sign=mex_param_is_present(&env_prt_sign);

	if (res!=mex_ok)
	{
		mex_msg_printf(mex_msg_error, "evnset", "invalid arguments");
		return;
	}
	//env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	if (!mex_param_is_present(&n))
	{
		if (mex_param_is_present(&clr))
		{
			StoredConfig::instance().clearenv_all();
			mex_printf("cleared!\n");
			goto fine_;
		}
		if (mex_param_is_present(&save))
			goto fine_;
		for(int i=1; i<env_store_t::k_max_env_vars; ++i)
		{
			env_print(i);
		}
		return;
	}
	if ((n<1) || (n>=env_store_t::k_max_env_vars))
	{
		mex_printf("Invalid env index %u\n", n);
		return;
	}

	if (mex_param_is_present(&v))
	{
		StoredConfig::instance().setenv_raw(n, &v);
	}

	if (mex_param_is_present(&clr))
	{
		StoredConfig::instance().clearenv_raw(n);
	}

	env_print(n);

fine_:
	if (mex_param_is_present(&save))
	{
		StoredConfig::instance().save_env();
		mex_printf("Saved!\n");
	}

}

void StoredConfigImplementation::__monitor_invokable_setenvf(void)
{
	static unsigned int n;
	static float v;
	static const mex_cmd_param_descriptor_t cmd_table[] =
	{
	{ "n", "var index", mex_cmd_required_value, &n, &mex_mt_uint,0},
	{ "v", "value", mex_cmd_required_value, &v, &mex_mt_float,0},
	};

	auto res = mex_cmd_parse_params(cmd_table, mex_get_raw_command_arguments(), 0);
	if (mex_param_help())
	{
		mex_show_help(cmd_table);
		return;
	}
	if (res!=mex_ok)
	{
		mex_msg_printf(mex_msg_error, "evnsetf", "invalid arguments");
		return;
	}
	StoredConfig::instance().setenv_raw(n, &v);
}

StoredConfig::StoredConfig()
{
	//cx.disabled=1;

    cx.initialize();

	(void)StoredConfigImplementation::__monitor_invokable_bintest;
	(void)StoredConfigImplementation::__monitor_invokable_setenv;
	(void)StoredConfigImplementation::__monitor_invokable_htest;
}

void StoredConfig::initialize()
{
}

void StoredConfig::disable(bool dis)
{
	cx.disabled=dis;
}

StoredConfig& StoredConfig::instance()
{
    static StoredConfig theInstance;
    return theInstance;
}

void StoredConfig::invokeStartupScript(bool exec_if_it_is_invalid) const
{
	cx.startConfig(exec_if_it_is_invalid);
    //rtlog_printf("CFG", rtlog_info|rtlog_breaking_news, "Startup script invoked...");
    //mex_script_inject(0, 0);
    return;
}

bool StoredConfig::loadRawAppConfig(void* raw, unsigned int size, unsigned int version_id) const
{
	bsp_raw_storable_data_load(&cx.appcfg, sizeof cx.appcfg, offsetof(StoredConfigImplementation::data_fmt_t, appCfg));
	if ((cx.appcfg.h.marker==k_nvm_marker) && (version_id==cx.appcfg.h.ver_id) && (size==cx.appcfg.h.size))
	{
		memcpy(raw, &cx.appcfg.raw[0], size);
		return true;
	}
	return false;
}

bool StoredConfig::saveRawAppConfig(const void* raw, unsigned int size, unsigned int version_id)
{
	cx.appcfg.h.marker=k_nvm_marker;
	cx.appcfg.h.size=size;
	cx.appcfg.h.ver_id=version_id;
	unsigned int s=size+(sizeof cx.appcfg.h)+2*sizeof(unsigned int);
	if (size>sizeof cx.appcfg.raw)
		return false;
	memcpy(cx.appcfg.raw, raw, size);
	uint32_t* p=reinterpret_cast<uint32_t*>(cx.appcfg.raw);
	p[1+(size/4)]=k_nvm_marker_end;
	bsp_raw_storable_data_save(&cx.appcfg, s, offsetof(StoredConfigImplementation::data_fmt_t, appCfg));
	return true;
}

bool StoredConfig::loadRawBinaryData(void* destination, unsigned int size, unsigned int version_id, unsigned int offset) const
{
	StoredConfigImplementation::header_t h;
	unsigned int marker_end=0;
	unsigned int nvm_offset=offsetof(StoredConfigImplementation::data_fmt_t, usrData)+offset;

	bool ok=false;
	rtg_hrt_ticks_t start=rtg_hrt_get();

	extern unsigned long rtg_hrt_to_microseconds(rtg_hrt_ticks_t /*ticks*/);

	bsp_raw_storable_data_load(&h, sizeof h, nvm_offset);
	if ((h.marker==k_nvm_marker) && (version_id==h.ver_id) && (size<=h.size))
	{
		nvm_offset+=sizeof h;
		bsp_raw_storable_data_load(&marker_end, sizeof marker_end, nvm_offset+h.size);
		if (marker_end==k_nvm_marker_end)
		{
			bsp_raw_storable_data_load(destination, size, nvm_offset);
			//nvm_offset+=h.size;
			ok=true;
		}
	}

	rtg_hrt_ticks_t stop=rtg_hrt_get();
	unsigned long elapsed=rtg_hrt_to_microseconds(stop-start);
	cx.t_read=elapsed;

	int severity=ok ? rtlog_success : rtlog_warning;
	rtlog_printf("NVM", severity|rtlog_breaking_news, "Bin Loaded: %u bytes in %lu us", size, elapsed);

	return ok;
}

bool StoredConfig::saveRawBinaryDataAsynch(const void* source, unsigned int size, unsigned int version_id, unsigned int offset)
{
	if (size>k_max_binary_size)
	{
		rtlog_printf("NVM", rtlog_warning|rtlog_breaking_news, "Bin Too big: %u bytes", size);
		return false;
	}
	cx.asynch_save_info.source=source;
	cx.asynch_save_info.size=size;
	cx.asynch_save_info.version_id=version_id;
	cx.asynch_save_info.offset=offset;

	cx.asynch_save_info.pending=1;

	rtlog_printf("NVM", rtlog_info|rtlog_breaking_news, "Posted asynch Bin Save: %u bytes", size);

	return true;
}

bool StoredConfig::pendingAsynchSave()
{
	if (cx.asynch_save_info.pending)
	{
		rtlog_printf("NVM", rtlog_info|rtlog_breaking_news, "Saving asynch Bin...");
		saveRawBinaryData(cx.asynch_save_info.source, cx.asynch_save_info.size, cx.asynch_save_info.version_id, cx.asynch_save_info.offset);
		cx.asynch_save_info.pending=0;
		return true;
	}
	return false;
}

bool StoredConfig::saveRawBinaryData(const void* source, unsigned int size, unsigned int version_id, unsigned int offset)
{
	StoredConfigImplementation::header_t h;
	unsigned int marker_end=k_nvm_marker_end;
	unsigned int nvm_offset=offsetof(StoredConfigImplementation::data_fmt_t, usrData)+offset;

	h.marker=k_nvm_marker;
	h.size=size;
	h.ver_id=version_id;
	unsigned int s=size+(sizeof h)+2*sizeof(unsigned int);
	if (s>k_max_binary_size)
	{
		rtlog_printf("NVM", rtlog_warning|rtlog_breaking_news, "Bin Too big: %u bytes", size);
		return false;
	}
	rtg_hrt_ticks_t start=rtg_hrt_get();

	multiple_storable_data_save(&h, sizeof h, nvm_offset);
	nvm_offset+=sizeof h;
	multiple_storable_data_save(source, size, nvm_offset);
	nvm_offset+=h.size;
	multiple_storable_data_save(&marker_end, sizeof marker_end, nvm_offset);

	rtg_hrt_ticks_t stop=rtg_hrt_get();
	unsigned long elapsed=rtg_hrt_to_microseconds(stop-start);
	cx.t_write=elapsed;

	rtlog_printf("NVM", rtlog_info|rtlog_breaking_news, "Bin Saved: %u bytes in %lu us", size, elapsed);
	return true;
}

bool StoredConfig::binaryDataTest(unsigned int /*offset*/)
{
	StoredConfigImplementation::__monitor_invokable_bintest();
	return true;
}

#include <stdarg.h>

void StoredConfig::historyClear()
{
	cx.histClear();
}

void StoredConfig::historySetTimestamp(timestamp_t ts)
{
	cx.ts.raw=ts.raw;
}

void StoredConfig::historyStart(unsigned int id, const char* msg, const char* msg1)
{
	if (msg1==0)
		msg1="?";
	if (msg==0)
		msg="?";

	historySave('!', "START", 0, 0, "[0x%X] v.%s-%s", id, msg, msg1);
}

void StoredConfig::historySave(char htype, const char* hname, const void* /*data*/, unsigned int /*data_size*/, const char* msg, ...)
{
	char buffer[32];
	va_list args;
	va_start(args, msg);
	bsk_vsnprintf(buffer, sizeof buffer, msg, args);
	va_end(args);
	bool ok=cx.histEnqueue(htype, hname, buffer);
#ifndef __MINGW32__
	if (ok)
		rtlog_printf("BITE", rtlog_info|rtlog_breaking_news, "Stored bite event: %s", hname);
	else
		rtlog_printf("BITE", rtlog_warning|rtlog_breaking_news, "Dropped Store bite event: %s", hname);
#else
	(void)ok;
#endif
}

void StoredConfig::historySaveExt(char htype, const char* hname, const char* decoder_name, unsigned int decoder_id, const void* data, unsigned int data_size, const char* msg, ...)
{
	char buffer[32];
	va_list args;
	va_start(args, msg);
	bsk_vsnprintf(buffer, sizeof buffer, msg, args);
	va_end(args);
	bool ok=cx.histEnqueue(htype, hname, buffer, decoder_name, decoder_id, data, data_size);
#ifndef __MINGW32__
	if (ok)
		rtlog_printf("BITE", rtlog_info|rtlog_breaking_news, "Stored bite event: %s", hname);
	else
		rtlog_printf("BITE", rtlog_warning|rtlog_breaking_news, "Dropped Store bite event: %s", hname);
#else
	(void)ok;
#endif
}

void StoredConfig::historyFlush()
{
	cx.histFlush();
}


bool StoredConfig::loaded()
{
	return cx.env_loaded;
}

//int getenv_raw(const char* name, void* value, const void* default_value, type_t* t=0);  //Warning: sizeof(value) shall be at least 4 bytes
//void setenv_raw(const char* name, void* value, type_t t=type_raw);
//void setenv_raw(int index, void* value, type_t t=type_raw);

static inline StoredConfig::type_t mtype(unsigned int m)
{
	return static_cast<StoredConfig::type_t>(m>>8);
}

static inline unsigned int msts(unsigned int m)
{
	return (m & 0xFF);
}

static inline unsigned int mencode(unsigned int sts, StoredConfig::type_t t)
{
	return sts|(static_cast<unsigned int>(t)<<8);
}

//int StoredConfig::getenv_raw(const char* name, void* value, const void* default_value)
//{
//	return getenv_raw_typed(name, value, default_value, 0);
//}
//int StoredConfig::getenv_raw_typed(const char* name, void* value, const void* default_value, type_t* t)

int StoredConfig::getenv_raw(const char* name, void* value, const void* default_value)
{
	type_t t=type_raw;

	if (!cx.env_loaded)
	{
		bsp_raw_storable_data_load(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
		if (!((cx.env_cache.h.marker==k_nvm_marker) && (cx.env_cache.h.ver_id==1) && (cx.env_cache.h.size>=sizeof(env_store_t))))
		{
			//make ready for next save
			cx.env_cache.h.marker=k_nvm_marker;
			cx.env_cache.h.ver_id=1;
			cx.env_cache.h.size=sizeof(env_store_t);
			memset(cx.env_cache.raw, 0, sizeof cx.env_cache.raw);
			//return false;
		}
		cx.env_loaded=true;
	}
	env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	int first_free=0;
	for(int i=1; i<env_store_t::k_max_env_vars; ++i)
	{
		if (s.vars[i].marker==0)
		{
			if (first_free==0)
				first_free=i;
			continue;
		}
		if (!name)
			continue;
		if (strncmp(name, s.vars[i].name, sizeof s.vars[i].name)==0)
		{
			if (msts(s.vars[i].marker)!=2)
			{
				first_free=i;
				break;
			}
			unsigned long* p=reinterpret_cast<unsigned long*>(value);
			*p=s.vars[i].value;
			t=mtype(s.vars[i].marker);
			return i;
		}
	}
	if (first_free)
	{
		if (!name)
			return -first_free;

		unsigned long tmp=0;
		if (default_value)
		{
			tmp=*reinterpret_cast<const unsigned long*>(default_value);
		}
		s.vars[first_free].marker=mencode(1, t);
		strncpy(s.vars[first_free].name, name, sizeof s.vars[first_free].name);
		s.vars[first_free].value=tmp;
	}
	return -first_free;
}

void StoredConfig::save_env()
{
	bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
}

void StoredConfig::setenv_raw(const char* name, void* value)
{
	unsigned long tmp=0;
	int index=getenv_raw(name, &tmp, 0);
	env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	if (index<0)
		index=-index;
	if (index>=env_store_t::k_max_env_vars)
	{
		++cx.env_overrun;
		return;
	}

	if (index>0)
	{
		s.vars[index].marker=2;
		strncpy(s.vars[index].name, name, sizeof s.vars[index].name);
		tmp=*reinterpret_cast<unsigned long*>(value);
		s.vars[index].value=tmp;

		//bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
	}
	else
		++cx.env_overrun;
}

void StoredConfig::setenv_raw(int index, void* value)
{
	unsigned long tmp=0;
	getenv_raw(0, &tmp, 0);
	env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	if (index<0)
		return;
	if (index>=env_store_t::k_max_env_vars)
	{
		++cx.env_overrun;
		return;
	}

	if (index>0)
	{
		if (s.vars[index].marker!=0)
			s.vars[index].marker=2;
		tmp=*reinterpret_cast<unsigned long*>(value);
		s.vars[index].value=tmp;

		//bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
	}
	else
		++cx.env_overrun;
}

void StoredConfig::clearenv_raw(const char* name)
{
	unsigned long tmp=0;
	int index=getenv_raw(name, &tmp, 0);
	if (index<0)
		index=-index;
	if (index>=env_store_t::k_max_env_vars)
	{
		return;
	}
	if (index>0)
	{
		env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
		s.vars[index].marker=0;
		s.vars[index].name[0]=0;
		s.vars[index].value=0;
		//bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
	}
}

void StoredConfig::clearenv_raw(int index)
{
	if (index>=env_store_t::k_max_env_vars)
	{
		return;
	}
	unsigned long tmp=0;
	getenv_raw(0, &tmp, 0);	//to be sure to load the env
	env_store_t& s=*reinterpret_cast<env_store_t*>(&cx.env_cache.raw);
	s.vars[index].marker=0;
	s.vars[index].name[0]=0;
	s.vars[index].value=0;
	//bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
}

void StoredConfig::clearenv_all()
{
	memset(cx.env_cache.raw, 0, sizeof cx.env_cache.raw);
	//bsp_raw_storable_data_save(&cx.env_cache, sizeof cx.env_cache, offsetof(StoredConfigImplementation::data_fmt_t, env));
}
