#include "globals.h"
#include "lcd.h"
#include "i2c.h"
#include "version.h"
#include "flash.h"
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <errno.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <mhash.h>
#include <glib.h>

#define MAINFILE "/root/flash.copy"
#define BACKUPFILE "/root/flash.bup"

#define PERSIST_ERR_COULDNTREADHDR 1
#define PERSIST_ERR_HDRLENMISMATCH 2
#define PERSIST_ERR_VERSIONMISMATCH 3
#define PERSIST_ERR_COULDNTREADDATA 4
#define PERSIST_ERR_BADCRC32 5
#define PERSIST_ERR_BADARGS 6
#define PERSIST_ERR_COULDNTALLOCATEBUFFER 7
#define PERSIST_ERR_COULDNTSEEK 8
#define PERSIST_ERR_COULDNTWRITE 9
#define PERSIST_ERR_COUNDNTOPENFILE 10

// safety check toggles
#define FROZENSMALLEROK 1
#define FROZENBIGGEROK 0

// flash writing queue bits
#define MAXQUEUEDJOBS 1 // This makes the queue a little bit pointless
// but you might want to queue a few more

typedef struct {
	FlashStruct *mem;
	int addr;
	int numbytes;
} userflashjob;


static bool alive = true;
static GMutex writermutex;
static GCond writerwakeup;
static GCond writerqueueopen;
static GThread* userflashwritethread;
static GAsyncQueue* userflashwritequeue;
//

// crc32 routine

static uint32_t crc32(uint8_t* buf, int count)
{
	MHASH context = mhash_init(MHASH_CRC32B);
	mhash(context, buf, count);
	uint32_t hash;
	mhash_deinit(context, &hash);
	return hash;
}

// header to prepend to stashed objects
typedef struct {
	uint32_t version;
	uint32_t length;
	uint32_t crc32;
} persistencehdr;

static void persistence_printheader(persistencehdr* hdr)
{
	printf("Frozen struct has version %d, is %d bytes long and has the CRC32 0x%08"PRIx32"\n", hdr->version,
	       hdr->length, hdr->crc32);
}

// copy a file from one place to another.. this is not portable, linux only
// this returns true if it is safe to continue. If there was nothing to backup
// that is considered "safe to continue"
bool persistence_copyfile(char* source, char* dest)
{
	mode_t filemode = S_IRUSR | S_IWUSR;

	int src = open(source, O_RDONLY);

	if (src < 0 && errno == ENOENT) { // If the source file doesn't exist, there isn't anything to copy
		close(src);
		return true;
	}

	int dst = open(dest, O_SYNC | O_RDWR | O_CREAT, filemode);

	if (src < 0 || dst < 0) {
		return false;
	}

	struct stat s;
	fstat(src, &s);

	ftruncate(dst, 0);
	sendfile(dst, src, NULL, s.st_size);
	close(src);
	close(dst);
	return true;
}

// store an object
bool persistence_freeze(char* dest, void* data, unsigned int offset, unsigned int len, unsigned int total,
                        uint32_t version)
{

	// don't write past the end of the file..
	if (offset + len > total) {
		errno = PERSIST_ERR_BADARGS;
		return false;
	}

	bool newfile = false;
	uint32_t crc;

	// open the target file with O_SYNC so that write blocks until it's on disk
	// fingers crossed the FS actually does what it's told..
	int fd = open(dest, O_SYNC | O_RDWR);
	if (fd < 0) {
		// this is to catch if the file didn't exist and if it needed to be created
		mode_t filemode = S_IRUSR | S_IWUSR;
		fd = open(dest, O_SYNC | O_RDWR | O_CREAT, filemode);
		if (fd < 0) {
			errno = PERSIST_ERR_COUNDNTOPENFILE;
			return false;
		}
		newfile = true;
	}

	// if this is a new file or we're overwriting everything we can just calculate the CRC from the data passed in
	if (newfile || len == total) {
		// if this a new file we want to write everything irrespective of the offset and len passed in
		if (newfile) {
			offset = 0;
			len = total;
		}
		crc = crc32((uint8_t*) data, total);
	}

	// this is a modification within an existing file so we need to merge the existing data with the
	// new data to calculate the new crc for the file because it seems the struct getting passed in
	// only contains the changed data.
	else if (len != total) {
		// create a buffer for the existing data
		void* payload = malloc(total);
		if (payload == NULL) {
			close(fd);
			errno = PERSIST_ERR_COULDNTALLOCATEBUFFER;
			return false;
		}

		// get the header
		persistencehdr hdr;
		if (read(fd, &hdr, sizeof(persistencehdr)) != sizeof(persistencehdr)) {
			errno = PERSIST_ERR_COULDNTREADHDR;
			free(payload);
			close(fd);
			return false;
		}

		// load the data
		persistence_printheader(&hdr);
		if (read(fd, payload, hdr.length) != hdr.length) {
			errno = PERSIST_ERR_COULDNTREADDATA;
			free(payload);
			close(fd);
			return false;
		}

		// check the existing data isn't already corrupt.
		uint32_t calculatedcrc32 = crc32((uint8_t*) payload, hdr.length);
		if (calculatedcrc32 != hdr.crc32) {
			errno = PERSIST_ERR_BADCRC32;
			free(payload);
			close(fd);
			return false;
		}
		// overlay the payload with the existing data
		memcpy(((char*) payload) + offset, ((char*) data) + offset, len);
		crc = crc32((uint8_t*) payload, total);
		free(payload);
		lseek(fd, 0, SEEK_SET); // rewind
	}

	// build the header
	persistencehdr hdr;
	hdr.version = version;
	hdr.length = total;
	hdr.crc32 = crc;

	persistence_printheader(&hdr);

	// write the data to disk
	ftruncate(fd, sizeof(hdr) + total); // not really needed but if we did have a file thats bigger than it
	// should be put a stop to that

	// write the header
	if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
		errno = PERSIST_ERR_COULDNTWRITE;
		close(fd);
		return false;
	}

	// seek to the offset.. which could mean not seeking at all
	if (lseek(fd, offset, SEEK_CUR) < 0) {
		errno = PERSIST_ERR_COULDNTSEEK; // shouldn't ever happen really because if we're actually
		// seeking the file should already be the total size.
		close(fd);
		return false;
	}

	// write the data out to disk
	if (write(fd, ((char*) data) + offset, len) != len) {
		errno = PERSIST_ERR_COULDNTWRITE;
		close(fd);
		return false;
	}

	// pack up and go home
	close(fd);

	return true;

}

// try to load an object from disk
bool persistence_unfreeze(char* dest, void* result, unsigned int len, uint32_t version)
{

	int fd = open(dest, O_RDONLY);

	// get the header
	persistencehdr hdr;
	if (read(fd, &hdr, sizeof(persistencehdr)) != sizeof(persistencehdr)) {
		errno = PERSIST_ERR_COULDNTREADHDR;
		close(fd);
		return false;
	}

	persistence_printheader(&hdr);

	// check that the length of this frozen object is what we are expecting
	if (hdr.length != len) {

#if FROZENSMALLEROK
		if (hdr.length < len) {
			printf("frozen struct is %d bytes smaller than the requested size\n", len - hdr.length);
			goto hdrlengthok;
		}
#endif

#if FROZENBIGGEROK
		if(hdr.length > len) {
			printf("frozen struct is bigger than the requested size, %d bytes will be truncated\n", hdr.length - len);
			goto hdrlengthok;
		}
#endif

		errno = PERSIST_ERR_HDRLENMISMATCH;
		close(fd);
		return false;
	}
hdrlengthok:

	// check that it's the same version.. the version isn't used at the moment
	// but if you want to change the header at some point it'll be useful
	if (hdr.version != version) {
		errno = PERSIST_ERR_VERSIONMISMATCH;
		close(fd);
		return false;
	}

	// read in the data for the object.. if we couldn't read the amount of data
	// that the header said there was the header is either wrong or the file is truncated.
	char* tempresult = g_malloc(hdr.length);
	if (read(fd, tempresult, hdr.length) != hdr.length) {
		errno = PERSIST_ERR_COULDNTREADDATA;
		close(fd);
		return false;
	}

	// check it's crc32 to make sure it's not corrupt
	uint32_t calculatedcrc32 = crc32(tempresult, hdr.length);
	if (calculatedcrc32 != hdr.crc32) {
		printf("Calculated CRC is 0x%08"PRIx32"\n", calculatedcrc32);
		errno = PERSIST_ERR_BADCRC32;
		close(fd);
		return false;
	}

	close(fd);
	memcpy(result, tempresult, MIN(len, hdr.length));
	g_free(tempresult);
	return true;

}

static int readUserBlock(FlashStruct *mem)
{

	// try to unfreeze the main file
	if (persistence_unfreeze(MAINFILE, mem, sizeof(*mem), 0)) {
		return sizeof(*mem);
	}
	// something went wrong
	else {
		printf("Error unfreezing %d.. trying backup\n", errno);
		// hopefully we can use the backup..
		if (persistence_unfreeze(BACKUPFILE, mem, sizeof(*mem), 0)) {
			// if the backup was good overwrite the main file
			if (!globals.Sys.shutdown_started) {
				globals.Sys.flash_write_in_progress = TRUE;
				persistence_copyfile(BACKUPFILE, MAINFILE);
				globals.Sys.flash_write_in_progress = FALSE;
			}
			return sizeof(*mem);
		}
		// deadend :(
		else {
			printf("Error unfreezing backup %d.\n", errno);
		}
	}
	return 0;
}

void writeUserBlockNow(FlashStruct *mem, int addr, int numbytes)
{
	// *** There is a potential issue here.. if the mainfile is corrupt ***
	// *** and this gets called before readUserBlock then the 			***
	// *** potentially workable backup will be lost .. we could check   ***
	// *** that the main file is valid before backing it up I guess...  ***
	// *** but I don't think this situation should arise.				***

	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
	g_static_mutex_lock (&mutex);

	if (!globals.Flags.flash_writes_suspended) {

		// backup the main copy of the file
		if (!globals.Sys.shutdown_started) {

			globals.Sys.flash_write_in_progress = TRUE;
			bool backup_ok = persistence_copyfile(MAINFILE, BACKUPFILE);
			globals.Sys.flash_write_in_progress = FALSE;

			if (backup_ok && !globals.Sys.shutdown_started) {
				globals.Sys.flash_write_in_progress = TRUE;

				if (!persistence_freeze(MAINFILE, mem, addr, numbytes, sizeof(*mem), 0)) {
					if (errno != PERSIST_ERR_COULDNTWRITE) {
						printf("Error while trying to write, %d. **Write did not happen!!!**\n", errno);
					} else {
						printf("Error while writing data to disk. **File is potentially corrupt!**\n");
					}
				}
				globals.Sys.flash_write_in_progress = FALSE;
			} else {
				printf("Could not backup current file. **Write did not happen!!!**\n");
			}
		}
	}

	g_static_mutex_unlock (&mutex);
}

void writeUserBlock(FlashStruct *mem, int addr, int numbytes)
{
	if (alive) {
		g_mutex_lock(&writermutex);
		if (g_async_queue_length(userflashwritequeue) >= MAXQUEUEDJOBS) {
			//printf("Queue closed, waiting\n");
			g_cond_wait(&writerqueueopen, &writermutex);
		}
		g_mutex_unlock(&writermutex);

		userflashjob* job = g_malloc(sizeof(userflashjob));
		job->mem = g_malloc(sizeof(FlashStruct));
		memcpy(job->mem, mem, sizeof(FlashStruct));
		job->addr = addr;
		job->numbytes = numbytes;
		g_async_queue_push(userflashwritequeue, job);
		g_cond_broadcast(&writerwakeup);
	}
}


static void initFlashValues(FlashStruct *mem)
{
	int i,j,k,m;
	float power_of_ten, power_of_two;

	mem->flash_start=1;
	mem->turn_on_dly=5;

	mem->logic_level_enabled=0;
	mem->ChanKey_logic_level=0;

	strcpy(mem->model_num,"unprogrammed");
	strcpy(mem->serial_num,"00000");

	mem->fully_programmed=Being_Programmed;

	mem->gpib_address=8;
	mem->channels=1;

	mem->web_session_timeout=120;		/* two minutes */
	mem->telnet_session_timeout=600;	/* ten minutes */
	mem->telnet_logon_timeout=30;		/* thirty seconds */

	mem->baud = 1200;
	mem->parity = rs232_parity_none;	// no longer used
	mem->stopbits = 1;			// no longer used
	mem->databits = 8;			// no longer used
	mem->hardhand = 1;
	mem->echo = 1;				// no longer used

	mem->on_off_used=1;

	mem->ampl_ranges_for_ch2_only=0;

	mem->ChanKey_frequency=0;
	mem->ChanKey_delay=0;
	mem->ChanKey_pw=0;
	mem->ChanKey_current_limit=0;
	mem->ChanKey_rise_time=0;
	mem->ChanKey_amplitude=0;
	mem->ChanKey_offset=0;
	mem->ChanKey_Curr_Mon_value=0;
	mem->ChanKey_Curr_Mon_offset=0;
	mem->ChanKey_zout=0;
	mem->ChanKey_hold_setting=0;
	mem->ChanKey_double_pulse=0;
	mem->ChanKey_route=0;
	mem->ChanKey_slew=0;

	mem->ChanKey_func_mode=0;
	mem->ChanKey_polarity=0;
	mem->ChanKey_output_state=0;
	mem->ChanKey_gate_type=0;
	mem->ChanKey_trigger_source=0;
	mem->ChanKey_amp_mode=0;
	mem->ChanKey_gate_level=0;
	mem->ChanKey_load_type=0;
	mem->ChanKey_test_delay_mode=0;
	mem->ChanKey_os_mode=0;
	mem->ChanKey_Burst_Count=0;
	mem->ChanKey_Burst_Time=0;

	mem->vxi_enabled=0;
	mem->self_cal=0;
	mem->self_cal_interval=5;
	mem->self_cal_startups=0;
	mem->self_cal_pause=300;
	mem->self_cal_typical_time_min=6;
	mem->self_cal_typical_time_sec=0;

	mem->prf_limiter=1;

	mem->pcb116c_mon=1;	/* more recent ADC, different reading code */
	mem->warn_even_if_output_off=0;

	strcpy(mem->spec_func_lib,"Not used");
	strcpy(mem->firmware,FW_VERSION);

	mem->enable_avrq_extra_ampls=0;

	for (i=0; i<points_in_range; i++) {
		mem->vcc1_pwl_Vc_norm4095[0][0][0][i]=0;
		mem->vcc1_pwl_amp[0][0][0][i]=0.0;
		mem->vcc2_pwl_Vc_norm4095[0][0][0][i]=0;
		mem->vcc2_pwl_amp[0][0][0][i]=0.0;
	}
	mem->vcc1_pwl_Vc_norm4095[0][0][0][1]=dac_max;
	mem->vcc1_pwl_amp[0][0][0][1]=10.0;
	mem->vcc2_pwl_Vc_norm4095[0][0][0][1]=dac_max;
	mem->vcc2_pwl_amp[0][0][0][1]=25;

	for (i=0; i<max_channels; i++) {
		power_of_ten=1.0;
		power_of_two=24.0e-9;
		for (j=0; j<timing_ranges; j++) {
			for (k=0; k<timing_polarities; k++) {
				for (m=0; m<points_in_range; m++) {
					mem->slew_pwl_time[i][j][k][m]=0.0;
					mem->slew_pwl_Vc_norm4095[i][j][k][m]=0;

					int temp_int_pw_dly, temp_int_prf;

					if (m==0) {
						/* these values have been determined by experiment */
						temp_int_pw_dly=dac_max;
						temp_int_prf=dac_max;

						mem->period_pwl_time[i][j][k][m]=(47e-9*power_of_ten)+41e-9;
						mem->pw_pwl_time[i][j][k][m]=(6e-9*power_of_ten)+5e-9;
						mem->delay_pwl_time[i][j][k][m]=(6e-9*power_of_ten)+10e-9;
						mem->burst_pwl_time[i][j][k][m]=(22e-9*power_of_ten)+25e-9;
					} else if (m==1) {
						temp_int_pw_dly=dac_max/2.15;
						temp_int_prf=dac_max/2.15;

						mem->period_pwl_time[i][j][k][m]=(100e-9*power_of_ten)+50e-9;
						mem->pw_pwl_time[i][j][k][m]=(14e-9*power_of_ten)+10e-9;
						mem->delay_pwl_time[i][j][k][m]=(14e-9*power_of_ten)+30e-9;
						mem->burst_pwl_time[i][j][k][m]=(33e-9*power_of_ten)+10e-9;
					} else if (m==2) {
						temp_int_pw_dly=dac_max/4.6;
						temp_int_prf=dac_max/4.6;

						mem->period_pwl_time[i][j][k][m]=(230e-9*power_of_ten)+100e-9;
						mem->pw_pwl_time[i][j][k][m]=(28e-9*power_of_ten)+20e-9;
						mem->delay_pwl_time[i][j][k][m]=(28e-9*power_of_ten)+60e-9;
						mem->burst_pwl_time[i][j][k][m]=(50e-9*power_of_ten)+10e-9;

					} else if (m==3) {
						temp_int_pw_dly=dac_min;
						temp_int_prf=dac_min;

						mem->period_pwl_time[i][j][k][m]=(470e-9*power_of_ten)+160e-9;
						mem->pw_pwl_time[i][j][k][m]=(70e-9*power_of_ten)+40e-9;
						mem->delay_pwl_time[i][j][k][m]=(70e-9*power_of_ten)+90e-9;
						mem->burst_pwl_time[i][j][k][m]=(110e-9*power_of_ten)+10e-9;
					} else {
						temp_int_pw_dly=0;
						temp_int_prf=0;
						mem->pw_pwl_time[i][j][k][m]=0.0;
						mem->delay_pwl_time[i][j][k][m]=0.0;
						mem->period_pwl_time[i][j][k][m]=0.0;
						mem->burst_pwl_time[i][j][k][m]=0.0;
					}

					mem->pw_pwl_Vc_norm4095[i][j][k][m]=temp_int_pw_dly;
					mem->delay_pwl_Vc_norm4095[i][j][k][m]=temp_int_pw_dly;
					mem->burst_pwl_Vc_norm4095[i][j][k][m]=temp_int_pw_dly;
					mem->period_pwl_Vc_norm4095[i][j][k][m]=temp_int_prf;
				}
			}
			power_of_ten*=10.0;
			power_of_two*=2.0;
		}


		power_of_two=20.0e-9;
		for (j=0; j<ampl_ranges; j++) {
			for (k=0; k<ampl_polarities; k++) {
				for (m=0; m<points_in_range; m++) {
					if (m==0) {
						mem->rise_time_pwl_Vc_norm4095[i][j][k][m]=dac_max;
						mem->rise_time_pwl_time[i][j][k][m]=(1e-9+power_of_two);
					} else if (m==1) {
						mem->rise_time_pwl_Vc_norm4095[i][j][k][m]=dac_max/2;
						mem->rise_time_pwl_time[i][j][k][m]=(1e-9+(power_of_two*1.5));
					} else if (m==2) {
						mem->rise_time_pwl_Vc_norm4095[i][j][k][m]=dac_max/4.6;
						mem->rise_time_pwl_time[i][j][k][m]=(1e-9+(power_of_two*3.0));
					} else {
						mem->rise_time_pwl_Vc_norm4095[i][j][k][m]=0;
						mem->rise_time_pwl_time[i][j][k][m]=0.0;
					}
				}
			}
			power_of_two*=2.0;
		}

		for (j=0; j<timing_ranges; j++) {
			for (k=0; k<ampl_polarities; k++) {
				mem->pw_range_pol_tweaks[i][j][k] = 0.0;
			}
		}

		for (j=0; j<10; j++)
			for (k=0; k<5; k++)
				for (m=0; m<2; m++) {
					mem->ampl_pwl_Vc_norm4095[i][k][m][j]=0;
					mem->ampl_pwl_amp[i][k][m][j]=0.0;
				}

		mem->ampl_pwl_Vc_norm4095[i][0][0][1]=dac_max;
		mem->ampl_pwl_amp[i][0][0][1]=100.0;

		for (j=0; j<max_stored_settings; j++) {
			mem->rcl_frequency[i][j]=10000.0;
			mem->rcl_delay[i][j]=0e-9;
			mem->rcl_pw[i][j]=20e-9;
			mem->rcl_amplitude[i][j]=0.0;
			mem->rcl_offset[i][j]=0.0;
			mem->rcl_misc[i][j]=9;
			mem->rcl_misc2[i][j]=0;
			mem->rcl_burst_count[i][j]=1 && !mem->burst_func[i];
			mem->rcl_burst_time[i][j]=500e-9;
			mem->rcl_rise_time[i][j]=50e-9;
			mem->rcl_soft_current_limit[i][j]=0.0;
			mem->rcl_route_primary[i][j]=1;
			mem->rcl_route_secondary[i][j]=1;
			mem->rcl_slew[i][j]=100e6;
			mem->rcl_load[i][j]=50.0;
			mem->rcl_vcc1[i][j]=0.0;
			mem->rcl_vcc2[i][j]=0.0;
			mem->rcl_vlogic[i][j]=0.0;
		}

		for (j=0; j<5; j++)
			for (k=0; k<2; k++) {
				mem->mon_vi_ratio[i][j][k]=0.050*(j+1);
			}

		mem->load_type_pwl_time[i][0][0][0] = 200;
		mem->load_type_pwl_time[i][0][0][1] = 10000;
		mem->load_type_pwl_Vc_norm4095[i][0][0][0] = dac_max;
		mem->load_type_pwl_Vc_norm4095[i][0][0][1] = dac_max / 60;

		for (j=2; j<10; j++) {
			mem->load_type_pwl_time[i][0][0][j] = 0;
			mem->load_type_pwl_Vc_norm4095[i][0][0][j] = 0;
		}

		mem->slew_pwl_time[i][4][0][0]=80e6;
		mem->slew_pwl_time[i][4][0][1]=240e6;
		mem->slew_pwl_time[i][3][0][0]=40e6;
		mem->slew_pwl_time[i][3][0][1]=120e6;
		mem->slew_pwl_time[i][2][0][0]=20e6;
		mem->slew_pwl_time[i][2][0][1]=60e6;
		mem->slew_pwl_time[i][1][0][0]=10e6;
		mem->slew_pwl_time[i][1][0][1]=30e6;
		mem->slew_pwl_time[i][0][0][0]=5e6;
		mem->slew_pwl_time[i][0][0][1]=15e6;

		mem->slew_pwl_Vc_norm4095[i][0][0][1]=dac_max;
		mem->slew_pwl_Vc_norm4095[i][1][0][1]=dac_max;
		mem->slew_pwl_Vc_norm4095[i][2][0][1]=dac_max;
		mem->slew_pwl_Vc_norm4095[i][3][0][1]=dac_max;
		mem->slew_pwl_Vc_norm4095[i][4][0][1]=dac_max;
	}

	/* special consideration for CH2 delay */
	mem->delay_pwl_time[1][0][0][0]=-0.1e-9;

	for (i=0; i<max_channels; i++) {
		mem->routing_required[i]=0;
		mem->routing_max_pins[i]=16;
		mem->min_ampl[i]=0.0;
		mem->max_ampl[i]=100.0;
		mem->min_offset[i]=0.0;
		mem->max_offset[i]=100.0;
		mem->min_vout[i]=0.0;
		mem->max_vout[i]=100.0;
		mem->min_freq[i]=1.0;
		mem->max_freq[i]=8e6;
		mem->min_pw[i]=25e-9;
		mem->max_pw[i]=1.0;
		mem->min_rise_time[i]=50e-9;
		mem->max_rise_time[i]=500e-9;
		mem->min_soft_current_limit[i]=10.0;
		mem->max_soft_current_limit[i]=530.0;
		mem->max_delay[i]=1.0;
		mem->min_delay[i]=0.0;
		mem->propagation_delay[i]=10.0e-9;
		mem->delay_shrink[i]=40.0e-9;
		mem->ampl_zero_equiv[i]=0.1;

		mem->max_duty_low[i]=110.0;
		mem->max_duty_high[i]=110.0;
		mem->duty_ampl[i]=30.0;

		mem->max_duty_mid1[i]=0.0;
		mem->duty_ampl_mid1[i]=0.0;
		mem->max_duty_mid2[i]=0.0;
		mem->duty_ampl_mid2[i]=0.0;

		mem->min_slew[i]=90e6;
		mem->max_slew[i]=210e6;
		mem->max_high_rl_duty[i]=80.0;
		mem->max_peak_power[i]=0.0;
		mem->max_avg_power[i]=0.0;

		mem->duty_highRL_above_v[i]=110.0;
		mem->duty_highRL_below_v[i]=110.0;

		mem->mon_pw_threshold[i]=-1.0;
		mem->monitor_step[i]=1.0;

		mem->sep_posneg_mon_ratio[i]=0;
		mem->volt_ctrl_pw[i]=0;
		mem->voltage_enabled[i]=1;
		mem->voltage_offset_enabled[i]=0;
		mem->current_enabled[i]=0;
		mem->current_offset_enabled[i]=0;
		mem->switchable_zout[i]=1;
		mem->dc_mode_allowed[i]=1;
		mem->pw_ab_mode_enabled[i]=1;
		mem->double_pulse_allowed[i]=1;
		mem->invert_allowed[i]=1;
		mem->ea_enabled[i]=1;
                mem->ew_enabled[i]=0;
		mem->switchable_load[i]=1;
		mem->monitor_enabled[i]=0;
		mem->use_pos_ampl_data_only[i]=0;
		mem->ampl_min_max_only[i]=0;
		mem->eo_enabled[i]=1;
		mem->ext_amplify_enabled[i]=1;

		mem->zout_min[i]=2;
		mem->zout_max[i]=50;

		for (j=0; j<10; j++) {
			for (k=0; k<5; k++) {
				mem->os_pwl_Vc_norm4095[i][k][0][j]=0;
				mem->os_pwl_amp[i][k][0][j]=0.0;
			}
		}
		mem->os_pwl_Vc_norm4095[i][0][0][1]=dac_max;
		mem->os_pwl_amp[i][0][0][1]=100.0;

		mem->ampl_DAC[i]=0;
		mem->os_DAC[i]=1;
		mem->polarity_xtra_rly[i]=1;
		mem->fixed_pw[i]=0;
		mem->fixed_rise_time[i]=1;
		mem->pcb_203a_rise_time[i]=1;
		mem->ext_amplify_xtra_rly[i]=4;
		mem->ea_xtra_rly[i]=5;
		mem->ew_xtra_rly[i]=5;
		mem->ignore_ampl_polarity[i]=0;
		mem->curr_slew[i]=0;

		mem->distort_X[i]=0.0;
		mem->distort_Y[i]=0.0;
		mem->distort_Z[i]=0.0;
		mem->distort_max_ampl[i]=0.0;
		mem->distort_max_os[i]=0.0;
		mem->ampl_os_ranges_related[i]=0;
		mem->ampl_coupled_to_os[i]=0;

		mem->pulse_width_pol_tweak[i][0]=0.0;
		mem->pulse_width_pol_tweak[i][1]=0.0;
		mem->delay_pol_tweak[i][0]=0.0;
		mem->delay_pol_tweak[i][1]=0.0;

		mem->max_burst_count[i]=1;

		mem->max_burst_duty[i]=50.0;
		mem->min_burst_per[i]=100e-9;
		mem->min_burst_gap[i]=100e-9;
		mem->max_burst_gap[i]=1.0;

		mem->is_func_gen[i]=0;
		mem->burst_func[i]=0;
		mem->freq_dac[i]=7;
		mem->is_monocycle[i]=0;
		mem->monocycle_dac[i]=6;
		mem->rise_time_dac[i]=6;
		mem->slew_dac[i]=6;
		mem->load_type_dac[i]=3;
		mem->output_timer[i]=0;

		mem->current_limit_pulse_mode[i]=220.0;
		mem->current_limit_dc_mode[i]=120.0;
		mem->current_limit_full_scale[i]=501.0;
		mem->current_limit_dac[i]=3;
		mem->hard_current_limit_enabled[i]=0;
		mem->soft_current_limit_enabled[i]=0;

		mem->invert_by_default[i]=pol_norm;

		mem->max_avg_ampl[i]=0.0;

		mem->pol_relay_high_for_pos[i]=1;

		mem->special_pw_range_minimum[i]=0.0;

		mem->pw_shift_below_this_ampl[i]=0.0;
		mem->pw_shift_below_ampl_by[i]=0.0;

		mem->ampl_min_abs_value[i]=0.0;
		mem->ampl_step_size[i]=0.0;

		mem->low_load_type[i]=50.0;
		mem->high_load_type[i]=10000.0;

		mem->fix_pw_dac_val[i]=dac_max/8;

		mem->max_pw_pol[i][0]=0.0;
		mem->max_pw_pol[i][1]=0.0;

		mem->vcc1_max[i]=5.1;
		mem->vcc2_max[i]=24.4;
		mem->vcc2_min[i]=3.0;

		mem->use_high_ampl_ranges_for_high_pw_ranges[i]=0;
		mem->couple_first_N_pw_ranges_to_ampl_ranges[i]=0;

		for (j=0; j<max_attens; j++) {
			mem->attenuators[i][j] = 0.0;
		}

		mem->atten_percent_max_ampl[i] = 0.93;

		mem->force_monotonic_ext_trig_delay[i] = 0;
	}

	mem->relay_delay_in_sec=0.5;
	mem->extended_relay_delay_in_sec=0.5;

	strcpy(mem->aux_error_message,"PRF too high! Output disabled.");


	/* default PW DACs */
	mem->pw_dac[0]=2;		/* channel 1: ONLY used for EXTERNAL voltage-controlled PW */
	/* DAC 4 is normally used for internally controlled PW */
	mem->pw_dac[1]=2;      /* channel 2: varies - normally 2 (for control of PCB 107C or 174) */

	/* default delay DACs */
	mem->delay_dac[0]=5;	/* channel 1: on OP1B board - not to be changed, as a rule */
	mem->delay_dac[1]=6;   /* channel 2: varies (for control of PCB107C) */


	/* originally Second_Dly_Port (for obsolete AVX-DD-A3-PS-TC) */
	mem->I2C_port_for_CH2_delay = Second_PW_Port;	// standard address for PCB 205A now

	mem->flash_end=99;

	for (i=0; i<8; i++) {
		mem->initial_dac_settings[i]=0L;
	}

	mem->copy_max_channels=max_channels;		/* copy to flash, so it can be read by diag:eprom:int? */


	/* avrq tests
	mem->enable_avrq_extra_ampls=1;
	mem->channels=2;
	mem->ChanKey_amplitude=1;
	mem->current_enabled[1]=1;
	mem->voltage_enabled[1]=0;
	mem->fully_programmed=2;
	mem->ampl_DAC[0]=-1;
	mem->ampl_pwl_amp[1][0][0][1]=0.025;
	mem->max_ampl[1]=0.022;
	mem->max_vout[1]=0.022;
	mem->ampl_zero_equiv[1]=0.001;
	*/

}

static gpointer userflashwritethreadfunc(gpointer data)
{

	//printf("userflash write thread start\n");
	while (alive || g_async_queue_length(userflashwritequeue) > 0) { // make sure the last job in the queue gets written
		if (g_async_queue_length(userflashwritequeue) == 0) { // go into a sleep
			g_mutex_lock(&writermutex);
			g_cond_wait(&writerwakeup, &writermutex);
			g_mutex_unlock(&writermutex);
		} else {
			userflashjob* job = (userflashjob*) g_async_queue_pop(userflashwritequeue);
			if (job != NULL ) {
				// process job
				//printf("processing write\n");
				writeUserBlockNow(job->mem, job->addr, job->numbytes);
				g_free(job->mem);
				g_free(job);
			}
			// If someone is waiting for the queue to start accepting
			// jobs again and we are tell them about it..
			if (g_async_queue_length(userflashwritequeue) < MAXQUEUEDJOBS) {
				g_mutex_lock(&writermutex);
				g_cond_broadcast(&writerqueueopen);
				g_mutex_unlock(&writermutex);
			}
		}

	}
	//printf("userflash write thread stop\n");

	return NULL ;
}

void startFlashWriterThread()
{
	alive= true;
	userflashwritequeue = g_async_queue_new();
	userflashwritethread = g_thread_create(userflashwritethreadfunc, NULL, true, NULL);
}

void initFlash(FlashStruct *mem, gboolean reset_to_defaults, int starting_location)
{
	startFlashWriterThread();
	atexit(stopFlashWriterThread);

	int read_size = readUserBlock(mem);

	if (	(read_size == 0) ||
	                (mem->fully_programmed == Not_Programmed) ||
	                (reset_to_defaults &&
	                 (starting_location >= 0) &&
	                 (starting_location < sizeof(*mem)))	) {

		g_print_debug ("initializing flash memory\n");
		LCD_write(0,0,"Initialize Flash Memory ...");

		gchar *message = g_strdup_printf ("Initialize Flash Memory, %d - %d", starting_location, (int) sizeof(*mem));
		LCD_write(0,0,message);
		g_free (message);

		// uninitialized device!
		initFlashValues(mem);

		// save the default Flash config, for nonvolatile persistence
		writeUserBlock(mem, starting_location, sizeof(*mem) - starting_location);

		LCD_write(1,0,"Flash Init, Done!         ");
	}
}

void stopFlashWriterThread()
{
	alive = false;
	g_cond_broadcast(&writerwakeup);
	g_thread_join(userflashwritethread); // block until the write thread is totally finished.
	g_async_queue_unref(userflashwritequeue);
}

void fixFlash(FlashStruct *mem)
{

	int i, fix_initial_constants;
	float composite_min_burst_time[max_channels];

	for (i=0; i<max_channels; i++) {

		composite_min_burst_time[i]=mem->min_burst_gap[i];
		if ((mem->min_burst_per[i] - mem->min_pw[i]) > composite_min_burst_time[i]) {
			composite_min_burst_time[i] = mem->min_burst_per[i] - mem->min_pw[i];
		}

		int j;
		for (j=0; j<max_stored_settings; j++) {
			if (mem->rcl_burst_time[i][j] < composite_min_burst_time[i]) {
				mem->rcl_burst_time[i][j]=composite_min_burst_time[i];
				++fix_initial_constants;
			}
			if (mem->rcl_rise_time[i][j] < mem->min_rise_time[i]) {
				mem->rcl_rise_time[i][j]=mem->min_rise_time[i];
				++fix_initial_constants;
			}
			if (mem->rcl_slew[i][j] < mem->min_slew[i]) {
				mem->rcl_slew[i][j]=mem->min_slew[i];
				++fix_initial_constants;
			}
			if (mem->rcl_soft_current_limit[i][j] < mem->min_soft_current_limit[i]) {
				mem->rcl_soft_current_limit[i][j]=mem->max_soft_current_limit[i];
				++fix_initial_constants;
			}
		}

		// for AVM-6-B in particular
		if ((mem->max_freq[i] >= 5e6) && (mem->fix_pw_dac_val[i] < dac_max/4)) {
			mem->fix_pw_dac_val[i] = dac_max/4;
			++fix_initial_constants;
		}
	}

	if (fix_initial_constants) {
		int eprom_loc;

		eprom_loc = (char *) &(mem->rcl_burst_time) - (char *) &(mem->flash_start);
		writeUserBlock(&globals.Flash, eprom_loc, sizeof(mem->rcl_burst_time));

		eprom_loc = (char *) &(mem->rcl_rise_time) - (char *) &(mem->flash_start);
		writeUserBlock(&globals.Flash, eprom_loc, sizeof(mem->rcl_rise_time));

		eprom_loc = (char *) &(mem->rcl_slew) - (char *) &(mem->flash_start);
		writeUserBlock(&globals.Flash, eprom_loc, sizeof(mem->rcl_slew));

		eprom_loc = (char *) &(mem->rcl_soft_current_limit) - (char *) &(mem->flash_start);
		writeUserBlock(&globals.Flash, eprom_loc, sizeof(mem->rcl_soft_current_limit));
	}

}