package simpleScope;


import java.util.Timer;

import java.util.TimerTask;
import oneLiners.OneLiners;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;

import comedi.ComediDataView;
import comedi.ComediRawDataStore;
import comedi.ComediAsyncReader;
import comedi.ComediRawReadBuffer;
import comedi.DataViewChangedNotification;
import comediJNI.ComediDef;

import otherSupport.SettingsManager;

public class SimpleScope implements DataViewChangedNotification {
	static {
		new SettingsManager("minerva", true);
	}
	
	public static final String inputSubDeviceNames[] = new String[]{ "Analogue In", "Digital IO" };	
	public static final int inputSubDeviceVals[] = new int[]{ 0, 2 };
	
	public static final int MAX_CHANS = 4;
	public static final String chanColorNames[] = new String[]{ "Blk", "Grn", "Blu", "Red" };

	private Shell swtShell;
	
	private Label statusLabel;
	
	private Button startButton;
	private Button stopButton;
	
	private Spinner chanSpinners[];
	
	private Spinner nScansSpinner;
	private Spinner bufferLenSpinner;
	private Text freqTextbox;
	
	private Button fixYCheckbox;
	private Spinner yMin;
	private Spinner yMax;
	
	private Spinner xMin;
	private Spinner xMax;
	private Spinner xStep;
	private Spinner xCount;
	
	private String bufferStatus;
	
	private Combo subdeviceCombo;

	private Button saveDataButton;
	
	private ComediAsyncReader comediReader;
	private ComediDataView dataView;
	
	private JFreeChartGraph graph;
	private DoubleBufferDataset dataset;
	
	public SimpleScope(Display display, String args[]) {
		swtShell = new Shell(display, SWT.RESIZE | SWT.DIALOG_TRIM);
		swtShell.setText("Simple Scope");
		
        swtShell.setSize(500, 500);
        swtShell.setLayout(new GridLayout(8, false));
        swtShell.addListener(SWT.Close, new Listener() { @Override public void handleEvent(Event event) { shellClosedEvent(event); }});
       
        Label lS = new Label(swtShell, SWT.NONE); lS.setText("Status:");
        statusLabel = new Label(swtShell, SWT.NONE);
        statusLabel.setText("Init\nInit");
        statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 3, 1));
        
        Label lSD = new Label(swtShell, SWT.NONE); lSD.setText("Subdevice:");
        subdeviceCombo = new Combo(swtShell, SWT.NONE);
        subdeviceCombo.setItems(inputSubDeviceNames);
        subdeviceCombo.select(0);
        subdeviceCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 3, 1));
        
        chanSpinners = new Spinner[MAX_CHANS];
        
        for(int i=0; i < MAX_CHANS; i++){
        	Label lC0 = new Label(swtShell, SWT.NONE); lC0.setText("Chan "+i+"("+chanColorNames[i]+"):");
        	chanSpinners[i] = new Spinner(swtShell, SWT.NONE);
        	chanSpinners[i].setValues(i, -1, 100, 0, 1, 5);
        	chanSpinners[i].setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        }
        
        Label lNS = new Label(swtShell, SWT.NONE); lNS.setText("Scans:");
        nScansSpinner = new Spinner(swtShell, SWT.NONE);
        nScansSpinner.setValues(10000, 1, Integer.MAX_VALUE, 0, 100, 10000);
        nScansSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        
        Label lBL = new Label(swtShell, SWT.NONE); lBL.setText("Buffer Length:");
        bufferLenSpinner = new Spinner(swtShell, SWT.NONE);
        bufferLenSpinner.setValues(10000, 1, Integer.MAX_VALUE, 0, 100, 10000);
        bufferLenSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
      
        Label lF = new Label(swtShell, SWT.NONE); lF.setText("Freq:");
        freqTextbox = new Text(swtShell, SWT.SINGLE);
        freqTextbox.setText("1000");
        freqTextbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        
        Label lB = new Label(swtShell, SWT.NONE); lB.setText("");
        lB.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
        
        Label lYM = new Label(swtShell, SWT.NONE); lYM.setText("Y Min:");
        yMin = new Spinner(swtShell, SWT.NONE);
        yMin.setValues(-10, -20, 20, 3, 1, 10);
        yMin.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        yMin.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});        
        
        Label lYX = new Label(swtShell, SWT.NONE); lYX.setText("Y Max:");
        yMax = new Spinner(swtShell, SWT.NONE);
        yMax.setValues(-10, -20, 20, 3, 1, 10);
        yMax.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        yMax.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});        
        
        fixYCheckbox = new Button(swtShell, SWT.CHECK);
        fixYCheckbox.setText("Rescale Inhibit");
        fixYCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); } });
        fixYCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false, 4, 1));
        
        Label lXM = new Label(swtShell, SWT.NONE); lXM.setText("X Min:");
        xMin = new Spinner(swtShell, SWT.NONE);
        xMin.setValues(-1, -1, Integer.MAX_VALUE, 0, 1000, 10000);
        xMin.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        xMin.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});        
        
        Label lXS = new Label(swtShell, SWT.NONE); lXS.setText("X Step:");
        xStep = new Spinner(swtShell, SWT.NONE);
        xStep.setValues(1, 1, Integer.MAX_VALUE, 0, 1, 10);
        xStep.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        xStep.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});        
        
        Label lXX = new Label(swtShell, SWT.NONE); lXX.setText("X Max:");
        xMax = new Spinner(swtShell, SWT.NONE);
        xMax.setValues(-1, -1, Integer.MAX_VALUE, 0, 1000, 10000);
        xMax.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        xMax.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});
        
        Label lXC = new Label(swtShell, SWT.NONE); lXC.setText("X Count:");
        xCount = new Spinner(swtShell, SWT.NONE);
        xCount.setValues(-1, -1, Integer.MAX_VALUE, 0, 1000, 10000);
        xCount.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
        xCount.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { doUpdateGraph(); }});
        
        startButton = new Button(swtShell, SWT.PUSH);
        startButton.setText("Start");
        startButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { startButtonEvent(event); } });
        startButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false, 3, 1));
        
        stopButton = new Button(swtShell, SWT.PUSH);
        stopButton.setText("Stop");
        stopButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { stopButtonEvent(event); } });
        stopButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false, 3, 1));
        
        saveDataButton = new Button(swtShell, SWT.PUSH);
        saveDataButton.setText("Save Data");
        saveDataButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { saveButtonEvent(event); } });
        saveDataButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false, 2, 1));
        
        dataset = new DoubleBufferDataset("ADCDataSet");
                
        graph = new JFreeChartGraph(swtShell, SWT.NONE, dataset);
        //graph.setData(new double[0], new double[0]);
        graph.getComposite().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 8, 1));
        
        //graph.setData(new double[]{ 1, 2, 3}, new double[]{ 10, 20, 30 });
        
        
        swtShell.pack();        
        swtShell.open();
        
        statusLabel.setText("Init OK");
        
    }
	
	private void startButtonEvent(Event event) {
		
		try{
			int subDev = inputSubDeviceVals[subdeviceCombo.getSelectionIndex()];
			
			int nChans = 0;
			for(int i=0; i < 4; i++){
				int chan = chanSpinners[i].getSelection();
				if(chan >= 0)
					nChans++;
			}
			
			int chans[] = new int[nChans];
			int ranges[] = new int[nChans];
			int j=0;
			for(int i=0; i < 4; i++){
				int chan = chanSpinners[i].getSelection();
				if(chan >= 0){
					chans[j] = chan;
					ranges[j] = 0;
					j++;
				}
			}
						
			String deviceFileName = SettingsManager.defaultGlobal().getProperty("comedi.device", "/dev/comedi0_subd" + subDev);
			int nScans = nScansSpinner.getSelection();
			double freq = OneLiners.mustParseDouble(freqTextbox.getText());
			int aref = ComediDef.AREF_GROUND;
			int periodNS = (int)(1e9 / freq);
			int buffLen = bufferLenSpinner.getSelection();
			
			comediReader = new ComediAsyncReader(deviceFileName, subDev, chans, ranges, aref, nScans, periodNS, buffLen);
			
            statusLabel.setText("Async read started.");

    		if(dataView != null){
    			dataView.abort(false);
    			dataView = null;
    		}
    		
    		dataView = new ComediDataView(comediReader.getStore(), this);
    		dataView.setRestrictions(
    				xMin.getSelection(),
    				xMax.getSelection(),
    				xCount.getSelection(),
    				xStep.getSelection());
    		graph.setDataset(dataset);
    		dataView.startUpdateThread();
    		
		}catch(RuntimeException err){
			err.printStackTrace();
			statusLabel.setText("Async read failed: " + err.getMessage());
			comediReader = null;
		}
		

	}
	
	private void stopButtonEvent(Event event) {
		if(comediReader != null){
			ComediRawDataStore store = comediReader.getStore();
			if(store != null){
				store.dump();
			}
		}
		comediReader.abort(false);
		if(dataView != null)
			dataView.abort(false);
			
	}
	
	private void saveButtonEvent(Event event) {
		if(comediReader == null)
			return;
		ComediDataView fullView = new ComediDataView(comediReader.getStore(), null);
		fullView.setRestrictions(-1, -1, -1, 1);
		fullView.forceUpdate();
		fullView.save("/tmp/comediFullData.bin");
		
	}
		
	private void doUpdateGraph() {
		if(dataView == null)
			return;
		
		dataView.setRestrictions(
				xMin.getSelection(),
				xMax.getSelection(),
				xCount.getSelection(),
				xStep.getSelection());
		
		dataView.forceUpdate();
		
		if(comediReader != null && comediReader.isActive())
			dataView.abort(false);
	}
	
	@Override
	public void dataViewChanged() {
		if(comediReader == null) 
			return;
		ComediRawReadBuffer rawStore = comediReader.getStore();
		synchronized (rawStore) {
			bufferStatus = "Raw: " + rawStore.nChans + " chans. Scans " +
							rawStore.firstScanNum + " - " + (rawStore.nextScanNum-1) + "\n";
		}
		
		synchronized (dataView) {
			int nC = dataView.getNChans();
			int x0 = dataView.getX0();
			int x1 = dataView.getX1();
			int dx = dataView.getDX();
			
			dataset.setData(dataView.getBuffer(), nC, x0, x1, dx);
			
			bufferStatus += "View: " + nC + " chans. Scans " +
					x0 + " - " + x1 + " step "+dx+"\n";
		}
		
		swtShell.getDisplay().asyncExec(new Runnable() {
			@Override
			public void run() {
				dataset.updateGraph();
				
				statusLabel.setText(bufferStatus);		
			}
		});
	}
	
	private void shellClosedEvent(Event event) {
		comediReader.abort(false);		
	}
	
	private void destroy() {
		if(comediReader != null)
			comediReader.abort(false);		
		if(dataView != null)
			dataView.abort(false);
	}
	
	public static void main(String[] args) {
		new SettingsManager("minerva", true);
		
		Display swtDisplay = new Display();
		
		SimpleScope scopeApp = new SimpleScope(swtDisplay, args);
		
		while (swtDisplay.getShells().length > 0) {
	          if (!swtDisplay.readAndDispatch()) {
	        	  //swtDisplay.sleep();
	        	  
	          }
	        }
		
		scopeApp.destroy();
		swtDisplay.dispose();
	}
	
}
