eel.expose(renderUI);
eel.expose(error)
eel.expose(warning)
eel.expose(message)

// Helper functions

function range(n){
	return [...Array(n).keys()];
}

function onload(){ //Bind events
	// Changing tabs
	tabs = document.querySelector('#tabs').children
	for (var i = 0; i < tabs.length; i++){
		tabs[i].addEventListener("click", function(event){
			eel.set_selected_board(event.srcElement.getAttribute("i"))
		})
	}
	// Box information
	document.querySelector('#piFreq').addEventListener("focusout", function(event){
		eel.set_pi_freq(parseFloat(event.srcElement.value))
	})
	document.querySelector('#remoteTrigg').addEventListener("click", function(event){
		eel.set_remote_trigg(event.srcElement.checked ? 1 : 0)
	})
	// Learning options
	document.querySelector('#enabGnd').addEventListener("click", function(event){
		eel.set_enab_gnd(event.srcElement.checked ? 1 : 0)
	})
	document.querySelector('#Vlearn').addEventListener("focusout", function(event){
		eel.set_Vlearn(parseInt(event.srcElement.value))
	})
	document.querySelector('#start').addEventListener("focusout", function(event){
		eel.set_start(parseInt(event.srcElement.value))
	})
	document.querySelector('#stop').addEventListener("focusout", function(event){
		eel.set_stop(parseInt(event.srcElement.value))
	})
	document.querySelector('#startGnd').addEventListener("focusout", function(event){
		eel.set_start_gnd(parseInt(event.srcElement.value))
	})
	document.querySelector('#stopGnd').addEventListener("focusout", function(event){
		eel.set_stop_gnd(parseInt(event.srcElement.value))
	})
	document.querySelector('#N').addEventListener("focusout", function(event){
		eel.set_N(parseInt(event.srcElement.value))
	})
	document.querySelector('#wfLen').addEventListener("focusout", function(event){
		eel.set_wf_len(parseInt(event.srcElement.value))
	})
	// Locking options
	document.querySelector('#stepMax').addEventListener("focusout", function(event){
		eel.set_max_step(parseInt(event.srcElement.value))
	})
	document.querySelector('#Gain').addEventListener("focusout", function(event){
		eel.set_Gain(parseFloat(event.srcElement.value))
	})
	document.querySelector('#calibrateGain').addEventListener("click", function(event){
		eel.calibrate_gain()
	})
	// Input options
	document.querySelector('#autoSetPga').addEventListener("click", function(event){
		eel.set_auto_set_pga(event.srcElement.checked ? 1 : 0)
	})
	document.querySelector('#inputGain').addEventListener("focusout", function(event){
		eel.set_input_gain(parseInt(event.srcElement.value))
	})
	document.querySelector('#offset').addEventListener("focusout", function(event){
		eel.set_offset(parseInt(event.srcElement.value))
	})
	// Output options
	document.querySelector('#coarseFineRatio').addEventListener("focusout", function(event){
		eel.set_coarse_fine_ratio(parseFloat(event.srcElement.value))
	})
	document.querySelector('#calibrateCoarseFineRatio').addEventListener("click", function(event){
		eel.calibrate_coarse_fine_ratio()
	})
	// Analysis tools
	document.querySelector('#measureResponseFunction').addEventListener("click", function(event){
		eel.measure_response_function()
	})
	document.querySelector('#sdevTime').addEventListener("click", function(event){
		eel.sdev_time()
	})
	document.querySelector('#noisePowerSpectrum').addEventListener("click", function(event){
		eel.noise_power_spectrum()
	})

	// Graphs
	document.getElementById("waveformGraphSelect").addEventListener("change", function(){
		document.querySelector('#waveformGraphAutoupdate').checked = false
		loadGraph()
	})
	document.getElementById("longGraphX").addEventListener("change", function(){
		loadLongGraph()
	})
	document.getElementById("longGraphY").addEventListener("change", function(){
		loadLongGraph()
	})
	document.querySelector('#waveformGraphClear').addEventListener("click", function(event){
		eel.clean_old_files("data/waveforms/", 0)
		loadGraphList()
	})
	document.querySelector('#longGraphClear').addEventListener("click", function(event){
		eel.clean_long_term()
		loadLongGraph()
	})

	// Information for simple usage
	tippy('#boxAdress', {delay: [1000, 0], content: "The i2c adress used for communication with the board. This is automatically obtained.",})
	tippy('#outOfLock', {delay: [1000, 0], content: "The box is out of lock when the output of the board is maximum, and thus fluctuations cannot be stabilized anymore.",})
	tippy('#piFreq', {delay: [1000, 0], content: "Frequency at which the Pi polls the board for new waveforms, typically no more than 10Hz as this can disturb the board.",})
	tippy('#boardFreq', {delay: [1000, 0], content: "Frequency at which the board is currently measuring waveforms (untested).",})
	tippy('#remoteTrigg', {delay: [1000, 0], content: "If true, the board will continously trigger, ignoring the trigger input. For normal operation set to false.",})
	tippy('#enabGnd', {delay: [1000, 0], content: "If true (recommended), the board stabilizes signal-gnd, otherwise it stabilizes signal",})
	tippy('#Vlearn', {delay: [1000, 0], content: "The output voltage (0-4095) when the box is in learn mode. This can be used to select an appropiate input power. Small values will unnecessarily suppress the signal and large values (>3500, see response function) may result in out_of_lock due to the limited range.",})
	tippy('#start', {delay: [1000, 0], content: "Point # where the signal starts being measured. Sampling frequency ~1MHz.",})
	tippy('#stop', {delay: [1000, 0], content: "Point # where the signal stop being measured. Sampling frequency ~1MHz.",})
	tippy('#startGnd', {delay: [1000, 0], content: "Point # where the ground starts being measured. Sampling frequency ~1MHz.",})
	tippy('#stopGnd', {delay: [1000, 0], content: "Point # where the ground stops being measured. Sampling frequency ~1MHz.",})
	tippy('#N', {delay: [1000, 0], content: "Number of waveforms (typ. 1) to sample in each stabilization cycle.",})
	tippy('#wfLen', {delay: [1000, 0], content: "Number of points to sample (1-255)",})
	tippy('#stepMax', {delay: [1000, 0], content: "Maximum step allowed in one stabilization cycle (typ. 200) . Small values may lead to slow response to sharp fluctuations.",})
	tippy('#Gain', {delay: [1000, 0], content: "At every step Vout += ((signal - ground) - Vset) * Gain",})
	tippy('#calibrateGain', {delay: [1000, 0], content: "Automatically select a loop gain based on the response function of the system.",})
	tippy('#autoSetPga', {delay: [1000, 0], content: "When True, the board changes the input gain and offset so that the signal is in an appropiate range (not clipped and going >2000). Set to False to select it manually.",})
	tippy('#inputGain', {delay: [1000, 0], content: "Allowed values: [1, 2, 4, 8, 16, 32, 64, 128]",})
	tippy('#offset', {delay: [1000, 0], content: "Input offset (0-4096). 2000 corresponds to no offset.",})
	tippy('#coarseFineRatio', {delay: [1000, 0], content: "Ratio of the precision in the fine and broad outputs. Typ. 20 from circuit design.",})
	tippy('#calibrateCoarseFineRatio', {delay: [1000, 0], content: "Calibrate the ratio by measuring the response functions of the broad and fine outputs.",})
	tippy('#measureResponseFunction', {delay: [1000, 0], content: "Measure the photodiode signal as a funtion of the output voltage.",})

	setTimeout(loop, 100)

}

j = 0
function loop(){
	if(document.getElementById("waveformGraphSelect") != document.activeElement){
		loadGraphList()
	}
	if(document.querySelector('#longGraphAutoupdate').checked){
		// Don't update this too often
		if(j%10==0){loadLongGraph()}
	}
	j++
	setTimeout(loop, 1000)
}

function renderUI(state){
	//console.log('Updating UI')

	// BOX INFORMATION
	tabs = document.querySelector('#tabs').children
	for (var i = 0; i < tabs.length; i++){
		tabs[i].classList.remove('active')
		if(i>=state.n_boards){
			tabs[i].style.opacity = 0
		}
		else{
			tabs[i].style.opacity = 1
		}
	}
	tabs[state.selected_board].classList.add('active')

	document.querySelector('#boxAddress').value = "0x"+state.box_address.toString(16)

	document.querySelector('#isLearn').checked = false
	document.querySelector('#isLock').checked = false
	if(state.mode == 0) document.querySelector('#isLearn').checked = true
	if(state.mode == 1) document.querySelector('#isLock').checked = true

	document.querySelector('#outOfLock').checked = false
	if(state.out_of_lock == 1) document.querySelector('#outOfLock').checked = true

	document.querySelector('#piFreq').value = state.pi_freq

	document.querySelector('#boardFreq').value = state.board_freq

	document.querySelector('#remoteTrigg').checked = false
	if(state.remote_trigg == 1) document.querySelector('#remoteTrigg').checked = true

	// LEARNING OPTIONS
	document.querySelector('#enabGnd').checked = state.enab_gnd

	document.querySelector('#Vlearn').value = state.Vlearn

	document.querySelector('#start').value = state.start

	document.querySelector('#stop').value = state.stop

	document.querySelector('#startGnd').value = state.start_gnd

	document.querySelector('#stopGnd').value = state.stop_gnd

	document.querySelector('#N').value = state.N

	document.querySelector('#wfLen').value = state.wf_len

	// LOCKING OPTIONS
	document.querySelector('#stepMax').value = state.step_max

	document.querySelector('#Gain').value = state.Gain

	// INPUT OPTIONS
	document.querySelector('#autoSetPga').checked = state.auto_set_pga
	document.querySelector('#inputGain').disabled = state.auto_set_pga==1
	document.querySelector('#offset').disabled = state.auto_set_pga==1

	document.querySelector('#inputGain').value = state.input_gain

	document.querySelector('#offset').value = state.offset

	// OUTPUT OPTIONS
	document.querySelector('#coarseFineRatio').value = state.coarse_fine_ratio

}


async function loadGraphList(){
	files = await eel.listFiles('data/waveforms/')()
	timestamps = files.map(s => parseInt(s.replace('.csv', '')))
	timestamps.sort()
	dates = []
	for(timestamp of timestamps){
		var d = new Date(0); 
		d.setUTCMilliseconds(timestamp*10);
		dates.push(d.toLocaleString() + "." + d.getMilliseconds())
	}

	var list = document.getElementById("waveformGraphSelect")
	index = list.selectedIndex
	list.innerHTML = ""
	for (var i = 0; i < dates.length; i++){
		var element = document.createElement("option")
		element.setAttribute('timestamp', timestamps[i])
		element.innerText = dates[i]
		list.append(element);
	}
	list.selectedIndex = index
	if(document.querySelector('#waveformGraphAutoupdate').checked){
		list.selectedIndex = list.children.length-1
		if(list.children.length-1 != index) loadGraph()
	}
	else if(index>-1){
		list.selectedIndex = index
	}
	else{
		loadGraph()
	}
}

async function loadGraph(){
	option = document.querySelector('#waveformGraphSelect')[document.querySelector('#waveformGraphSelect').selectedIndex]
	timestamp = option.getAttribute('timestamp')
	csv = await eel.loadFile('data/waveforms/'+timestamp+'.csv')()
	document.querySelector('#waveformGraphText').innerText = "Signal: "+csv[8]+" - Gnd: "+csv[9]+" - Signal_std: "+csv[10].toFixed(2)+" - Gnd_std: "+csv[11].toFixed(2)

	var data = [{
	  x: range(csv[12]),
	  y: csv.slice(13),
	  type: 'scatter'
	},{
	  x: [csv[2], csv[2]],
	  y: [0, Math.max(...csv.slice(13))],
	  line: {
    	color: 'red'
      },
	  type: 'scatter',
	  mode: 'lines'
	},{
	  x: [csv[3], csv[3]],
	  y: [0, Math.max(...csv.slice(13))],
	  line: {
    	color: 'red'
      },
	  type: 'scatter',
	  mode: 'lines'
	},{
	  x: [csv[4], csv[4]],
	  y: [0, Math.max(...csv.slice(13))],
	  line: {
    	color: 'green'
      },
	  type: 'scatter',
	  mode: 'lines'
	},{
	  x: [csv[5], csv[5]],
	  y: [0, Math.max(...csv.slice(13))],
	  line: {
    	color: 'green'
      },
	  type: 'scatter',
	  mode: 'lines'
	}];
	var layout = {
	  margin: {
		l: 50,
		r: 10,
		b: 50,
		t: 10,
		pad: 4
	  },
	  xaxis: {
		title: 'Time',
		showgrid: true,
	  },
	  yaxis: {
		title: 'Input voltage',
		showgrid: true
	  },
	  showlegend: false
	};
	Plotly.newPlot('waveformGraph', data, layout);
}

async function loadLongGraph(){
	csv = await eel.loadLongTerm()()
	xIndex = parseInt(document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].value)
	xTitle = document.querySelector('#longGraphX').children[document.querySelector('#longGraphX').selectedIndex].innerText
	yIndex = parseInt(document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].value)
	yTitle = document.querySelector('#longGraphY').children[document.querySelector('#longGraphY').selectedIndex].innerText


	var data = [{
	  x: csv.map(l => l[xIndex]),
	  y: csv.map(l => l[yIndex]),
	  type: 'scatter'
	}]
	if(xIndex==1){
		max = Math.max(...data[0].x)
		data[0].x = data[0].x.map(v => (v-max)/60)
	}

	var layout = {
	  margin: {
		l: 50,
		r: 10,
		b: 50,
		t: 10,
		pad: 4
	  },
	  xaxis: {
		title: xTitle,
		showgrid: true,
	  },
	  yaxis: {
		title: yTitle,
		showgrid: true
	  },
	  showlegend: false
	};
	Plotly.newPlot('longGraph', data, layout);
	
}

function error(txt){
	Toastify({
	  text: "Error: "+txt,
	  gravity: 'bottom',
	  position: 'left',
	  close: true,
	  backgroundColor: "#ef4041",
	  duration: 3000,}).showToast()
}

function warning(txt){
	Toastify({
	  text: "Warning: "+txt,
	  gravity: 'bottom',
	  position: 'left',
	  close: true,
	  backgroundColor: "#e36d25",
	  duration: 3000,}).showToast()
}

function message(txt){
	Toastify({
	  text: txt,
	  gravity: 'bottom',
	  position: 'left',
	  close: true,
	  backgroundColor: "grey",
	  duration: 3000,}).showToast()
}