package imseProc.proc.demod;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import imseProc.core.IMSEProc;
import imseProc.core.ImagePipeController;
import imseProc.core.ImagePipeControllerROISettable;
import imseProc.core.ImgPipe;
import imseProc.gui.swt.SWTControllerInfoDraw;
import oneLiners.OneLiners;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
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.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Spinner;

import signals.gmds.GMDSSignal;

import descriptors.gmds.GMDSSignalDesc;

public class DSHDemodSinkSWTController implements ImagePipeController, ImagePipeControllerROISettable, SWTControllerInfoDraw {
	private static final String widgetEventInhibit = "widgetInhibit";
	private static final String noCalibStr = " - None - ";
	
	private Group swtGroup;	
	private Combo swtComponentSelCombo;
	
	private Spinner selectionPulseSpinner;
	private Button selectionLoadButton;
	private Button selectionSaveButton;
	private Button selectionAutoButton;
	
	private Button unwrapPhasesCheckbox;
	private Button autoCalcCheckbox;
	private Combo outputModeCombo;
	private Label statusLabel;
	
	private Spinner mulP0Spinner;
	private Spinner mulPPSpinner;
	private Spinner mulPMSpinner;
	
	private Spinner calibImgNum;
	private Combo calibImgPath;
	private Spinner calibImgAngle;
	
	private Combo interlaceModeCombo;
	
	private DSHDemod proc;
	private boolean asSink;
	
	public DSHDemodSinkSWTController(DSHDemod demodProc, Composite parent, int style, boolean asSink) {
		this.proc = demodProc;
		this.asSink = asSink;
		
		swtGroup = new Group(parent, style);
		swtGroup.setText("iMSE DSH Demodulation");
		swtGroup.setLayout(new GridLayout(5, false));
		swtGroup.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { destroy(); }});
		
		Label lStat = new Label(swtGroup, SWT.NONE); lStat.setText("Status:");
		statusLabel = new Label(swtGroup, SWT.NONE);
		statusLabel.setText("init");
		statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		
		Label lSel = new Label(swtGroup, SWT.NONE); lSel.setText("Select:"); 
		swtComponentSelCombo = new Combo(swtGroup, SWT.NONE);		
		swtComponentSelCombo.setItems(DSHDemod.componentNames);
		swtComponentSelCombo.add("None",0);
		swtComponentSelCombo.select(0);
		swtComponentSelCombo.setEnabled(asSink);
		swtComponentSelCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		
		Label lSels = new Label(swtGroup, SWT.NONE); lSels.setText("Load from pulse:"); 
		selectionPulseSpinner = new Spinner(swtGroup, SWT.NONE);
		selectionPulseSpinner.setValues(0, -1, Integer.MAX_VALUE, 0, 1, 10);
		selectionPulseSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		selectionLoadButton = new Button(swtGroup, SWT.PUSH);
		selectionLoadButton.setText("Load");
		selectionLoadButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
		selectionLoadButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { selectionLoadButtonEvent(event); } });
		
		selectionSaveButton = new Button(swtGroup, SWT.PUSH);
		selectionSaveButton.setText("Save (to current pulse)");
		selectionSaveButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
		selectionSaveButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { selectionSaveButtonEvent(event); } });
		
		selectionAutoButton = new Button(swtGroup, SWT.PUSH);
		selectionAutoButton.setText("Auto Select");
		selectionAutoButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
		selectionAutoButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { selectionAutoButtonEvent(event); } });
		
		
		Label lOM = new Label(swtGroup, SWT.NONE); lOM.setText("Output:"); 
		outputModeCombo = new Combo(swtGroup, SWT.NONE);
		outputModeCombo.setItems(DSHDemod.outputModeNames);
		outputModeCombo.select(DSHDemod.OUTPUT_MODE_POL_px); //default is ADSH pol
		outputModeCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { outputModeComboEvent(event); } });
		outputModeCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		
		Label lIM = new Label(swtGroup, SWT.NONE); lIM.setText("Interlace:"); 
		interlaceModeCombo = new Combo(swtGroup, SWT.CHECK);
		interlaceModeCombo.setItems(DSHDemod.interlaceModeOpts);
		interlaceModeCombo.select(0);
		interlaceModeCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		interlaceModeCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));		
		
		Label lP = new Label(swtGroup, SWT.NONE); lP.setText(""); 
		unwrapPhasesCheckbox = new Button(swtGroup, SWT.CHECK);
		unwrapPhasesCheckbox.setText("Unwrap phase");
		unwrapPhasesCheckbox.setSelection(false);
		unwrapPhasesCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		unwrapPhasesCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
		
		autoCalcCheckbox = new Button(swtGroup, SWT.CHECK);
		autoCalcCheckbox.setText("Auto Update");
		autoCalcCheckbox.setSelection(true);
		autoCalcCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		autoCalcCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
		
		Label lMa = new Label(swtGroup, SWT.NONE); lMa.setText("μ(+,0) %:");
		mulP0Spinner = new Spinner(swtGroup, SWT.NONE);
		mulP0Spinner.setValues(100, 0, 1000, 0, 1, 10);
		mulP0Spinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		mulP0Spinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
		Label lMb = new Label(swtGroup, SWT.NONE); lMb.setText("μ(+,+) %:");
		mulPPSpinner = new Spinner(swtGroup, SWT.NONE);
		mulPPSpinner.setValues(100, 0, 1000, 0, 1, 10);
		mulPPSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		mulPPSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
		Label lMc = new Label(swtGroup, SWT.NONE); lMc.setText("μ(+,-) %:");
		mulPMSpinner = new Spinner(swtGroup, SWT.NONE);
		mulPMSpinner.setValues(100, 0, 1000, 0, 1, 10);
		mulPMSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		mulPMSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
		Label lMd = new Label(swtGroup, SWT.NONE); lMd.setText("Calib Img#:");
		calibImgNum = new Spinner(swtGroup, SWT.NONE);
		calibImgNum.setValues(-1, -1, 10000, 0, 1, 10);
		calibImgNum.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		calibImgNum.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
		Label lMe = new Label(swtGroup, SWT.NONE); lMe.setText("Calib Img Path:");
		calibImgPath = new Combo(swtGroup, SWT.MULTI);		
		loadCalibImgPathHistory();
		calibImgPath.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		calibImgPath.addListener(SWT.Modify, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
		Label lMf = new Label(swtGroup, SWT.NONE); lMf.setText("Calib Angle:");
		calibImgAngle = new Spinner(swtGroup, SWT.NONE);
		calibImgAngle.setValues(2250, 0, 4500, 2, 1, 100);
		calibImgAngle.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 4, 1));
		calibImgAngle.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { configChangeEvent(event); } });
		
	}
	
	private void outputModeComboEvent(Event event){
		if(proc.getOutputMode() != outputModeCombo.getSelectionIndex())			
			proc.setOutputMode(outputModeCombo.getSelectionIndex());
		
	}

	private void selectionLoadButtonEvent(Event event){
		proc.loadSelectionsFromGMDS(IMSEProc.getMetaExp(proc.getConnectedSource()), 
									selectionPulseSpinner.getSelection());		
	}

	private void selectionSaveButtonEvent(Event event){
		proc.saveSelectionsToGMDS(-1);
		
	}
	
	private void selectionAutoButtonEvent(Event event){
		proc.findAutoSelections();
		
	}
	
	private void configChangeEvent(Event event){
		if(event.widget.getData() == widgetEventInhibit)
			return;
		
		proc.setAutoCalc(autoCalcCheckbox.getSelection());
		proc.setPhaseUnwrap(unwrapPhasesCheckbox.getSelection());
		proc.setComponentAdjust(
				mulP0Spinner.getSelection()/100.0,
				mulPPSpinner.getSelection()/100.0,
				mulPMSpinner.getSelection()/100.0 );
				
		String calibPath = calibImgPath.getText();
		proc.setCalibImage(noCalibStr.equals(calibPath) ? null : calibPath, calibImgNum.getSelection(), calibImgAngle.getSelection() /100.0 * Math.PI/180);		
		proc.setInterlaceMode(interlaceModeCombo.getSelectionIndex());		
	}

	@Override
	public void movingPos(int x, int y) { }

	@Override
	public void fixedPos(int x, int y) {
		if(!asSink)return;
		int sel = swtComponentSelCombo.getSelectionIndex();
		if(sel >= 1)
			proc.setCentralFreq(sel - 1, x, y);
		
	}

	@Override
	public void setRect(int x0, int y0, int width, int height) {
		if(!asSink)return;
		int sel = swtComponentSelCombo.getSelectionIndex();
		System.out.println(x0 + ", " + y0 + ", " + width + ", " + height);
		if(sel >= 1)
			proc.setWindow(sel - 1, x0, y0, width, height);
	}


	@Override
	public void drawOnImage(GC gc, double scale, int imageWidth, int imageHeight, boolean asSource) {
		if(!asSink)return;
		int sels[][] = proc.getSelections();
		if(swtGroup.isDisposed() || gc.isDisposed() || sels == null)return;
		
		int cols[] = new int[]{
				SWT.COLOR_BLACK,
				SWT.COLOR_GREEN,
				SWT.COLOR_RED,
				SWT.COLOR_BLUE,
				SWT.COLOR_YELLOW,
		};
		
		for(int i=0; i < DSHDemod.nComps; i++){
			gc.setForeground(swtGroup.getDisplay().getSystemColor(cols[i]));
			gc.setLineWidth(3);
			double xw = sels[i][0] + sels[i][2];
			double yh = sels[i][1] + sels[i][3];
			if(sels[i][0] >= 0 && sels[i][1] >= 0 && xw < imageWidth && yh < imageHeight){
				gc.drawRectangle(
						(int)(sels[i][0] * scale),
						(int)(sels[i][1] * scale),
						(int)(sels[i][2] * scale),
						(int)(sels[i][3] * scale));
				
				if(sels[i][4] > sels[i][0] && sels[i][4] < xw &&
					sels[i][5] > sels[i][1] && sels[i][5] < yh){
					gc.drawLine(
							(int)(sels[i][4] * scale), 
							(int)(sels[i][1] * scale), 
							(int)(sels[i][4] * scale), 
							(int)(yh * scale));
					gc.drawLine(
							(int)(sels[i][0] * scale), 
							(int)(sels[i][5] * scale), 
							(int)(xw * scale), 
							(int)(sels[i][5] * scale));
				}
			}
		}
	}

	@Override
	public Object getInterfacingObject() { return swtGroup; }

	@Override
	public void generalControllerUpdate() {
		if(swtGroup.isDisposed())return;
		
		IMSEProc.ensureFinalSWTUpdate(swtGroup.getDisplay(), this, new Runnable() {
			@Override
			public void run() { update(); }
		});
	}
	
	private void update() {		
		if(swtGroup.isDisposed())return;
		
		outputModeCombo.select(proc.getOutputMode());
		
		int stat = proc.getCalcProgress();
		statusLabel.setText((stat < 0) ? "Idle" : (stat + " / " + proc.getConnectedSource().getNumImages()));
		
		String calibPath = proc.getCalibPath();
		if(calibPath != null)
			setCalibPath(calibPath);
		
	}

	@Override
	public void destroy() {
		swtGroup.dispose();
		proc.controllerDestroyed(this);
		//proc.destroy(); //and actively kill it, for good measure
	}

	@Override
	public ImgPipe getPipe() { return proc;	}

	private void loadCalibImgPathHistory() {
		calibImgPath.removeAll();
		calibImgPath.add(noCalibStr);
		try{
			GMDSSignalDesc desc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "DSHDemod/CalibHistory");
			String strs[] = (String[])IMSEProc.globalGMDS().getSig(desc).getData();
			
			for(String s : strs){
				if(!noCalibStr.equals(s))
					calibImgPath.add(s);
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		calibImgPath.select(0);
	}
	
	private void setCalibPath(String path) {
		try {			
			if(calibImgPath.isFocusControl())
				return;
			
			//get the existing list of calibs
			List<String> paths = new ArrayList<String>(Arrays.asList(calibImgPath.getItems()));
			
			//get rid of the "None" entry(ies)
			ListIterator<String> it = paths.listIterator();		
			boolean alreadyPresent = false;
			while(it.hasNext()){
				String s = it.next();				
				if(path != null && path.equals(s))
					alreadyPresent = true;
				else if(noCalibStr.equals(s))
					it.remove();				
			}
			if(!alreadyPresent && path != null)
				paths.add(path);
			Collections.sort(paths);
			
			//repopulate with the sorted entries
			calibImgPath.setData(widgetEventInhibit);//inbhibit update
			calibImgPath.removeAll();
			calibImgPath.add(noCalibStr);
			for(String s : paths)
				calibImgPath.add(s);
			
			
			//and select the added one
			calibImgPath.select(path == null ? 0 : (paths.indexOf(path)+1) );
			
			calibImgPath.setData(null);//inbhibit update
			 
			GMDSSignalDesc desc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "DSHDemod/CalibHistory");
			GMDSSignal sig = new GMDSSignal(desc, paths);
			sig.writeToCache(IMSEProc.globalGMDS().getCacheRoot());
			
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}
