package imseProc.aqui.proc;

import java.nio.DoubleBuffer;
import java.util.Arrays;
import java.util.LinkedList;

import comedi.ComediDataView;

import imseProc.aqui.AquisitionHanger;
import imseProc.core.IMSEProc;

public class ImageStepSyncProc {
	private AquisitionHanger aquiProc;
	private ImageStepSyncController controller;
	
	private String info = "No calc";
	
	private int expoChan = 0; //camera out connected to ch0
	private int stepChan = 1; //stepper step out connected to ch1
	private double lowThreshold = 1.5, highThreshold = 3.5; //Schmidt trigger like
	
	public ImageStepSyncProc(AquisitionHanger aquiProc, ImageStepSyncController controller) {
		this.aquiProc = aquiProc;
		this.controller = controller;

	}
	
	public void calc(){
		IMSEProc.ensureFinalUpdate(this, new Runnable() { @Override public void run() { doCalc(); } });
	}
	
	private void doCalc(){
		if(aquiProc == null)
			return;
		
		
		info = "Getting data";
		System.out.println(info);
		if(controller != null)
			controller.update();
		
		ComediDataView fullView = aquiProc.getFullDataView();
		
		if(fullView == null || aquiProc.getConnectedSource() == null){
			info = "No data";
			if(controller != null)
				controller.update();			
			return;
		}
		
		info = "Calc started";
		System.out.println(info);
		if(controller != null)
			controller.update();
		
		
		int nChans = fullView.getNChans();
		DoubleBuffer buff = fullView.getBuffer();
		
		int currentImageNo = -1; //exp out starts high and falls at aquisition start 
		int lastImageNo = -2;    //  then goes high again for first image
		int currentStepNo = 0; //change on falling edge of ch1
		
		double stepSum = 0;
		int nInStepSum = 0;
				
		buff.rewind();
		buff.limit(buff.capacity());
		
		LinkedList<Double> avgStepList = new LinkedList<Double>();
		
		double scan[] = new double[nChans];
		int nScans = buff.capacity() / nChans;
		
		buff.get(scan);
		boolean wasHigh[] = new boolean[nChans];
		for(int k=0; k < nChans; k++){				
			wasHigh[k] = scan[k] > (lowThreshold + highThreshold)/2;				
		}
						
		int scanNo = 1;
		while(buff.hasRemaining()){
			buff.get(scan);
				
			boolean isHigh[] = wasHigh.clone();			
			for(int k=0; k < nChans; k++){				
				if(scan[k] < lowThreshold)
					isHigh[k] = false;				
				else if(scan[k] > highThreshold)
					isHigh[k] = true;
				//else leave it as is
			}
			
			
			if(!wasHigh[expoChan] && isHigh[expoChan]){
				//expo rising = camera exposure start
				currentImageNo = lastImageNo + 1;
				
			}else if(wasHigh[expoChan] && !isHigh[expoChan]){ 
				//expo falling = camera exposure end
				if(currentImageNo >= 0){
					avgStepList.add(stepSum / nInStepSum);
				}
				lastImageNo = currentImageNo;
				currentImageNo = -1; //not in image
				stepSum = 0;
				nInStepSum = 0;				
			}
			
			if(wasHigh[stepChan] && !isHigh[stepChan]){ 
				//step falling = Step count
				currentStepNo++;
			}

			if(isHigh[expoChan] && currentImageNo >= 0){ 
				//exposing current step, include in averaging
				stepSum += currentStepNo;
				nInStepSum++;
			}
							
			wasHigh = isHigh;
			
			if(nScans < 1000 || (scanNo % (nScans/100)) == 0){
				info = "Scan: " + scanNo + " of " + nScans +
						", Image#: " + currentImageNo +  
						", Step#: " + currentStepNo;
				
				System.out.println(info);
			
				if(controller != null)
					controller.update();
			}
			scanNo++;
		}
		
		double avgStep[] = new double[avgStepList.size()];
		int i=0;
		for(Double val : avgStepList)
			avgStep[i++] = val;
		
		aquiProc.getConnectedSource().setSeriesMetaData("stepSync/stepNo", avgStep);
		
						 
		controller.update();
	}

	public String getStatus() { return info;	}
	
}
