package imseProc.proc.imgFit;

import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import oneLiners.OneLiners;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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 org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

import imseProc.core.IMSEProc;
import imseProc.core.ImagePipeController;
import imseProc.core.ImagePipeControllerROISettable;
import imseProc.core.Img;
import imseProc.core.ImgPipe;
import imseProc.core.ImgSource;
import imseProc.gui.swt.SWTControllerInfoDraw;

/** The table 
 *  
 * @author oliford
 */
public class ImageFitSWTController implements ImagePipeController, SWTControllerInfoDraw, ImagePipeControllerROISettable {
	private final static String maskModeNames[] = new String[]{ "None", "Mask (Orig)", "!Mask (Orig)", "Mask (Fit)", "!Mask (Fit)" };
	
	private Group swtGroup;
	private ImageFitProcessor proc;
	
	private Label statusLabel;

	private Button singleImageCheckbox;
	private Button initCheckbox;
	private Button doFitCheckbox;
	private Button autoUpdateCheckbox;
	private Button updateButton;
	
	private Combo setMaskModeCombo;
	private Combo outputMaskMode;	
	
	private Spinner nKnotsXSpinner;
	private Spinner nKnotsYSpinner;
	
	private Spinner altPulseSpiner;
	private Button loadMaskButton;
	private Button saveMaskButton;
	
	private boolean isSinkController;
		
	public ImageFitSWTController(Composite parent, int style, ImageFitProcessor proc, boolean isSinkController) {
		this.isSinkController = isSinkController;
		this.proc = proc;
		swtGroup = new Group(parent, style);
		swtGroup.setText("FFT Control (" + proc.toShortString() + ")");		
		swtGroup.setLayout(new GridLayout(4, 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 lNX = new Label(swtGroup, SWT.NONE); lNX.setText("nKnots X:");
		nKnotsXSpinner = new Spinner(swtGroup, SWT.NONE);
		nKnotsXSpinner.setValues(5, 2, 100, 0, 1, 10);
		nKnotsXSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		nKnotsXSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		
		Label lNY = new Label(swtGroup, SWT.NONE); lNY.setText("Y:");
		nKnotsYSpinner = new Spinner(swtGroup, SWT.NONE);
		nKnotsYSpinner.setValues(5, 2, 100, 0, 1, 10);
		nKnotsYSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		nKnotsYSpinner.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		
		Label lSMM = new Label(swtGroup, SWT.NONE); lSMM.setText("Set Mask:");
		setMaskModeCombo = new Combo(swtGroup, SWT.CHECK);
		setMaskModeCombo.setItems(new String[]{ "None", "On", "Off" });
		setMaskModeCombo.select(0);
		setMaskModeCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 3, 1));

		singleImageCheckbox = new Button(swtGroup, SWT.CHECK);
		singleImageCheckbox.setText("Single Image");
		singleImageCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		singleImageCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
		
		autoUpdateCheckbox = new Button(swtGroup, SWT.CHECK);
		autoUpdateCheckbox.setText("Auto");
		autoUpdateCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { autoUpdateEvent(event); } });
		autoUpdateCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
		
		initCheckbox = new Button(swtGroup, SWT.CHECK);
		initCheckbox.setText("Fresh Init");
		initCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		initCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
		
		doFitCheckbox = new Button(swtGroup, SWT.CHECK);
		doFitCheckbox.setText("Perform fit");
		doFitCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		doFitCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
				
		Label lMM = new Label(swtGroup, SWT.NONE); lMM.setText("Output Mode");
		outputMaskMode = new Combo(swtGroup, SWT.MULTI | SWT.READ_ONLY);
		outputMaskMode.setItems(maskModeNames);
		outputMaskMode.select(0);
		outputMaskMode.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { settingsChangedEvent(event); } });
		outputMaskMode.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false, 2, 1));

		updateButton = new Button(swtGroup, SWT.PUSH);
		updateButton.setText("Update Now");
		updateButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { forceUpdateEvent(event); } });
		updateButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));

		Label lOP = new Label(swtGroup, SWT.NONE); lOP.setText("Mask from pulse:");		
		altPulseSpiner = new Spinner(swtGroup, SWT.NONE);
		altPulseSpiner.setValues(0, 0, Integer.MAX_VALUE, 0, 1, 10);
		altPulseSpiner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		loadMaskButton = new Button(swtGroup, SWT.PUSH);
		loadMaskButton.setText("Load");
		loadMaskButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { loadMaskButtonEvent(event); } });
		loadMaskButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		saveMaskButton = new Button(swtGroup, SWT.PUSH);
		saveMaskButton.setText("Save");
		saveMaskButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { saveMaskButtonEvent(event); } });
		saveMaskButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		swtGroup.pack();
		update();
	}
		
	private void autoUpdateEvent(Event e){
		proc.setAutoCalc(autoUpdateCheckbox.getSelection());
	}
	
	private void settingsChangedEvent(Event e){
		proc.setModes(initCheckbox.getSelection(), doFitCheckbox.getSelection(),
				singleImageCheckbox.getSelection(), outputMaskMode.getSelectionIndex());
		proc.setNKnots(nKnotsXSpinner.getSelection(), nKnotsYSpinner.getSelection());		
		
	}
	
	private void forceUpdateEvent(Event e){
		proc.invalidate();
		proc.calc();
	}
	
	private void loadMaskButtonEvent(Event e) {
		proc.loadMask(altPulseSpiner.getSelection());
	}
	
	private void saveMaskButtonEvent(Event e) {
		proc.saveMask();
	}

	@Override
	public void generalControllerUpdate() {
		if(swtGroup.isDisposed())
			return;
		IMSEProc.ensureFinalSWTUpdate(swtGroup.getDisplay(), this, new Runnable() {
			@Override
			public void run() { update(); }
		});
	}
	
	private void update(){
		boolean autoUpdate = proc.getAutoCalc();
		if(autoUpdateCheckbox.getSelection() != autoUpdate)
			autoUpdateCheckbox.setSelection(autoUpdate);
		
		int stat = proc.getCalcProgress();
		statusLabel.setText((stat < 0) ? "Idle" : (stat + " / " + proc.getConnectedSource().getNumImages()));
		
	}

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

	@Override
	public void fixedPos(int x, int y) {	
		if(setMaskModeCombo.getSelectionIndex() <= 0)
			return;
		
		Img img = proc.getSelectedImageFromConnectedSource();
		if(img == null)
			return;
		
		int w = img.getWidth();
		int h = img.getHeight();
		double radius = 0.1;
		
		proc.setMaskCircle((double)x/w, (double)y/h, radius, setMaskModeCombo.getSelectionIndex() == 1);
				
	}

	@Override
	public void setRect(int x0, int y0, int width, int height) {
		if(setMaskModeCombo.getSelectionIndex() <= 0)
			return;
		
		Img img = proc.getSelectedImageFromConnectedSource();
		if(img == null)
			return;
		
		int w = img.getWidth();
		int h = img.getHeight();
		
		proc.setMaskRect((double)x0/w, (double)y0/h, ((double)x0 + width)/w, ((double)y0 + height)/h, setMaskModeCombo.getSelectionIndex() == 1);
				
	}
			
	@Override
	public void drawOnImage(GC gc, double scale, int imageWidth, int imageHeight, boolean asSource) {
	/*	
		HashMap<String, double[]> pointsMap = proc.getPointsMap();
		
		int r = Math.min(imageWidth, imageHeight) / 50;
		
		double remapped[][] = proc.getBackConvertedXY();
		
		int i=0;
		for(Entry<String,double[]> entry : pointsMap.entrySet()){
			String pointName = entry.getKey();
			double pointData[] = entry.getValue();
				
			if(tableItemEditing != null && tableItemEditing.getText(0).equals(pointName)){
				gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_MAGENTA));
			}else{
				gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));				
			}
			
			if(pointData[0] >= 0 && pointData[1] >= 0 && 
				pointData[0] < imageWidth && pointData[1] < imageHeight){
					
				gc.drawOval(
						(int)((pointData[0] - r) * scale), 
						(int)((pointData[1] - r) * scale), 
						(int)(r*2 * scale), 
						(int)(r*2 * scale));
			}
			
			if(remapped != null && remapped.length > i && remapped[i] != null) {
				gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_RED));			
				gc.drawOval(
						(int)((remapped[i][0] - r) * scale), 
						(int)((remapped[i][1] - r) * scale), 
						(int)(r*2 * scale), 
						(int)(r*2 * scale));
			}
			
			i++;
		}
		*/
	}
	
	@Override
	public void destroy() {
		swtGroup.dispose();
		proc.controllerDestroyed(this);
		//proc.destroy(); //and actively kill it, for good measure
	}

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

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