package imseProc.graph.shapeFit;

import jafama.FastMath;
import imseProc.core.IMSEProc;
import imseProc.graph.GraphUtilProcessor;

import mds.GMDSFetcher;
import oneLiners.OneLiners;

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

import descriptors.gmds.GMDSSignalDesc;

import signals.gmds.GMDSSignal;

public class FuncFitterSWTControl {
	/** For shape fitting */
	private Group shapeGroup;
	private Combo shapeCombo;
	private Label fitInfo;
	private Button autoFitCheckbox;
	private Button fitButton;
	private Button autoInit;
	private Button finalToInitButton;
	
	private Table paramsTable;
	private TableEditor paramsTableEditor;
	
	private Text paramsSaveNameTextbox;
	private Button paramsSaveButton;
	private Combo paramsLoadCombo;
		
	private GraphUtilProcessor proc;
	
	private static final String colNames[] = new String[]{ "Param", "Init", "Final", "Enable" };
	
	public FuncFitterSWTControl(Composite parent, int style, GraphUtilProcessor proc) {
		this.proc = proc;
	
		shapeGroup = new Group(parent, style);
		shapeGroup.setText("Shape Fitting");
		shapeGroup.setLayout(new GridLayout(4, false));
		
		Label lT = new Label(shapeGroup, SWT.NONE); lT.setText("Shape:");  
		shapeCombo = new Combo(shapeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
		shapeCombo.setItems(FuncFitter.funcTypeNames);
		shapeCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { fitSettingsEvent(); } });
		shapeCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		autoFitCheckbox = new Button(shapeGroup, SWT.CHECK);
		autoFitCheckbox.setText("Auto Fit");	
		autoFitCheckbox.setEnabled(true);		
		autoFitCheckbox.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { fitSettingsEvent(); } });
		autoFitCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
		
		fitButton = new Button(shapeGroup, SWT.PUSH);
		fitButton.setText("Fit");
		fitButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { fitbuttonEvent(event); } });
		fitButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));
				
		Label lI = new Label(shapeGroup, SWT.NONE); lI.setText("Init:");  
		autoInit = new Button(shapeGroup, SWT.CHECK);
		autoInit.setText("Auto");	
		autoInit.setEnabled(true);
		autoInit.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { fitSettingsEvent(); } });
		autoInit.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
				
		finalToInitButton = new Button(shapeGroup, SWT.PUSH);
		finalToInitButton.setText("Copy Final");	
		finalToInitButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { finalToInitButtonEvent(event); } });
		finalToInitButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 2, 1));

		Label lIL = new Label(shapeGroup, SWT.NONE); lIL.setText("Load:");  
		paramsLoadCombo = new Combo(shapeGroup, SWT.READ_ONLY | SWT.MULTI);
		paramsLoadCombo.setItems(new String[0]);
		paramsLoadCombo.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { paramsLoadComboEvent(event); } });
		paramsLoadCombo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));

		paramsSaveNameTextbox = new Text(shapeGroup, SWT.NONE);
		paramsSaveNameTextbox.setText("Default");		
		paramsSaveNameTextbox.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 1));

		
		paramsSaveButton = new Button(shapeGroup, SWT.PUSH);
		paramsSaveButton.setText("Save");
		paramsSaveButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { paramsSaveButtonEvent(event); } });
		paramsSaveButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 1));
		
		paramsTable = new Table(shapeGroup, SWT.NONE);
		paramsTable.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, true, 5, 0));
		paramsTable.setHeaderVisible(true);
		paramsTable.setLinesVisible(true);
		TableColumn cols[] = new TableColumn[colNames.length];
		for(int i=0; i < colNames.length; i++){
			cols[i] = new TableColumn(paramsTable, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
			cols[i].setText(colNames[i]); 
			cols[i].pack();
		}
		
		//Wavelength, Phase, Amp, Y0
		for(int i=0; i < 6; i++){
			TableItem item = new TableItem(paramsTable, SWT.NONE);		
			item.setText(0, "P" + i);
			item.setText(1, "0.0");
			item.setText(2, "0.0");
			item.setText(3, "Y");
		}
		
		paramsTableEditor = new TableEditor (paramsTable);
		paramsTableEditor.horizontalAlignment = SWT.LEFT;
		paramsTableEditor.grabHorizontal = true;
		paramsTable.addListener(SWT.MouseDown, new Listener() { @Override public void handleEvent(Event event) { tableMouseDownEvent(event); } });
		  
		fitInfo = new Label(shapeGroup, SWT.NONE);
		fitInfo.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 5, 1));
		
		populateParamsLoadCombo();
	}
	
	private void populateParamsLoadCombo(){
		paramsLoadCombo.setItems(new String[0]);
		
		try{
			String sigsPaths[] = IMSEProc.globalGMDS().dumpTree(IMSEProc.getMetaExp(proc.getConnectedSource()), 0, "graphFitter/", true);
			
			for(String signalPath : sigsPaths){
				paramsLoadCombo.add(signalPath);
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	private void paramsLoadComboEvent(Event event){
		String saveName = paramsLoadCombo.getText();
		
		if(saveName != null && !saveName.isEmpty()){
			GMDSSignalDesc sigDesc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "graphFitter/" + saveName);
			
			GMDSSignal signal = (GMDSSignal)IMSEProc.globalGMDS().getSig(sigDesc);
			double data[][] = (double[][])signal.getData();
			
			shapeCombo.select((int)data[0][0]);
			
			int nP = paramsTable.getItemCount();
			for(int i=0; i < Math.min(nP, data.length-1); i++){
				TableItem item = paramsTable.getItem(i);
				item.setText(1, Double.toString(data[i+1][0]));
				item.setText(2, Double.toString(data[i+1][1]));
				item.setText(3, (data[i+1][2] != 0) ? "Yes" : "No");
			}
		}
		
		fitSettingsEvent();
		
		populateParamsLoadCombo();
		
	}
	
	private void paramsSaveButtonEvent(Event event){
		String saveName = paramsSaveNameTextbox.getText();
		
		int nP = paramsTable.getItemCount();
		double data[][] = new double[nP+1][3]; 
		
		data[0][0] = shapeCombo.getSelectionIndex();
		for(int i=0; i < nP; i++){
			TableItem item = paramsTable.getItem(i);
			data[i+1][0] = OneLiners.mustParseDouble(item.getText(1));
			data[i+1][1] = OneLiners.mustParseDouble(item.getText(2));
			data[i+1][2] = item.getText(3).startsWith("Y") ? 1 : 0;
		}
		
		GMDSSignalDesc sigDesc = new GMDSSignalDesc(0, IMSEProc.getMetaExp(proc.getConnectedSource()), "graphFitter/" + saveName);
		
		GMDSSignal sig = new GMDSSignal(sigDesc, data);
		
		IMSEProc.globalGMDS().writeToCache(sig);
		
		populateParamsLoadCombo();
	}
	
		
	private void fitbuttonEvent(Event event){
		proc.getFitter().invalidate();
		proc.calc();		
	}
	
	private void finalToInitButtonEvent(Event event){
		for(int i=0; i < paramsTable.getItemCount(); i++){
			TableItem item = paramsTable.getItem(i);
			item.setText(1, item.getText(2));
		}
		
		fitSettingsEvent();	
		
		System.out.println(proc.getFitter().getInfoTxt());
	}
	
	
	private void fitSettingsEvent(){
		proc.getFitter().setFuncType(shapeCombo.getSelectionIndex());
		proc.getFitter().setAutoFit(autoFitCheckbox.getSelection());
		proc.getFitter().setEstimateInitParams(autoInit.getSelection());
		
		int nP = paramsTable.getItemCount();
		
		//check names match table, and hence table param count matches
		String names[] = proc.getFitter().getParamNames();
		for(int i=0; i < names.length; i++){
			TableItem item;		
			if(i >= nP){
				item = new TableItem(paramsTable, SWT.NONE);
				item.setText(3, "Yes");
			}else{
				item = paramsTable.getItem(i);
			}
			 
			item.setText(0, names[i]);
		}
		for(int i=(nP-1); i >= names.length; i--){
			paramsTable.remove(i);
		}
		nP = names.length;
			
		
		double initP[] = new double[nP];
		boolean enable[] = new boolean[nP];
		for(int i=0; i < nP; i++){
			TableItem item = paramsTable.getItem(i);
			initP[i] = OneLiners.mustParseDouble(item.getText(1));
			enable[i] = item.getText(3).startsWith("Y");
		}
		proc.getFitter().setInitParams(initP);
		proc.getFitter().setParamEnable(enable);
		
		proc.getFitter().invalidate();
		proc.calc();
	}

	/** Copied from 'Snippet123'  Copyright (c) 2000, 2004 IBM Corporation and others. 
	 * [ org/eclipse/swt/snippets/Snippet124.java ] */
	private void tableMouseDownEvent(Event event){
		 
		Rectangle clientArea = paramsTable.getClientArea ();
		Point pt = new Point (event.x, event.y);
		int index = paramsTable.getTopIndex ();
		while (index < paramsTable.getItemCount ()) {
			boolean visible = false;
			final TableItem item = paramsTable.getItem (index);
			for (int i=0; i<paramsTable.getColumnCount (); i++) {
				Rectangle rect = item.getBounds (i);
				if (rect.contains (pt)) {
					final int column = i;
					final Text text = new Text(paramsTable, SWT.NONE);
					Listener textListener = new Listener () {
						public void handleEvent (final Event e) {
							switch (e.type) {
							case SWT.FocusOut:
								setTableEntry(column, item, text.getText());
								text.dispose ();
								break;
							case SWT.Traverse:
								switch (e.detail) {
								case SWT.TRAVERSE_RETURN:
									setTableEntry(column, item, text.getText());
									//FALL THROUGH
								case SWT.TRAVERSE_ESCAPE:
									text.dispose ();
									e.doit = false;
								}
								break;
							}
						}
					};
					text.addListener (SWT.FocusOut, textListener);
					text.addListener (SWT.Traverse, textListener);
					paramsTableEditor.setEditor (text, item, i);
					text.setText (item.getText (i));
					text.selectAll ();
					text.setFocus ();
					return;
				}
				if (!visible && rect.intersects (clientArea)) {
					visible = true;
				}
			}
			if (!visible) return;
			index++;
		}
	}
	
	private void setTableEntry(int column, TableItem item, String str){
		if(column == 1 || column == 2){
			item.setText(column, Double.toString(OneLiners.mustParseDouble(str)));
		}else if(column == 3){
			item.setText(column, (str.startsWith("Y") || str.startsWith("y")) ? "Yes" : "No");
		}
		fitSettingsEvent();
	}
	
	public void update(){
		fitInfo.setText(proc.getFitter().getInfoTxt());
		
		
		String names[] = proc.getFitter().getParamNames();
		double initP[] = proc.getFitter().getInitParams();
		double finalP[] = proc.getFitter().getFinalParams();
		
		if(initP == null)return;
		
		int nPT = paramsTable.getItemCount();
		for(int i=0; i < names.length; i++){
			TableItem item;
			if(i >= nPT){
				item = new TableItem(paramsTable, SWT.NONE);
				item.setText(3, "Yes");
			}else{
				item = paramsTable.getItem(i);
			}			
			item.setText(0, (i >= names.length) ? ("P" + i) : names[i]);
			item.setText(1, (initP == null || initP.length <= i) ? "0.0" : Double.toString(initP[i]));
			item.setText(2, (finalP == null || finalP.length <= i) ? "0.0" : Double.toString(finalP[i]));
		}
		for(int i=(nPT-1); i >= names.length; i--){
			paramsTable.remove(i);
		}
		
		shapeGroup.getParent().pack();
	}
	
	public Group getGroup(){ return shapeGroup; }
}
