package imseProc.stepperMotor.controllers;



import java.util.ArrayList;
import java.util.List;

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.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.ImgPipe;

public class StepperSWTController implements ImagePipeController {
	
	private Group swtGroup;	
	private StepperHanger proc;
	
	private Button connectButton;
	private Button disconnectButton;
	private Label statusLabel;
	private Spinner targetSpinner;
	private Button gotoButton;
	
	private Table cmdTable;
	private TableEditor cmdTableEditor;
	private Button resetButton;
	private Button startButton;
	private Button armButton;
	private Button stopButton;
	
	public StepperSWTController(StepperHanger proc, Composite parent, int style) {
		this.proc = proc;
		
		swtGroup = new Group(parent, style);
		swtGroup.setText("Stepper Motor Controller (Hanger)");
		swtGroup.setLayout(new GridLayout(4, false));
		
		Label lS = new Label(swtGroup, SWT.NONE);
		lS.setText("Status:");
		
		statusLabel = new Label(swtGroup, SWT.NONE);
		statusLabel.setText("init");
		statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 3, 0));
		
		Label lPrt = new Label(swtGroup, SWT.NONE);
		lPrt.setText("Port:");
		
		connectButton = new Button(swtGroup, SWT.PUSH);
		connectButton.setText("Connect");
		connectButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { connectButtonEvent(event); } });
		connectButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 0));
		
		disconnectButton = new Button(swtGroup, SWT.PUSH);
		disconnectButton.setText("Disconnect");
		disconnectButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { disconnectButtonEvent(event); } });
		disconnectButton.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 1, 0));
		
		Label lSpace = new Label(swtGroup, SWT.NONE); lSpace.setText("");
		
		Label lPos = new Label(swtGroup, SWT.NONE); lPos.setText("Target:");
		
		targetSpinner = new Spinner(swtGroup, SWT.None);
		targetSpinner.setValues(0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 100, 1000);
		targetSpinner.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 0));
		
		gotoButton = new Button(swtGroup, SWT.PUSH);
		gotoButton.setText("Go");
		gotoButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { gotoButtonEvent(event); } });
		gotoButton.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false, 1, 0));
		
		cmdTable = new Table(swtGroup, SWT.NONE);
		cmdTable.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, true, 4, 0));
		cmdTable.setHeaderVisible(true);
		cmdTable.setLinesVisible(true);
		String colNames[] = new String[]{ "Time", "Axis", "End Pos", "Speed" ,"Ramp", "End Speed"  };
		TableColumn cols[] = new TableColumn[colNames.length];
		for(int i=0; i < colNames.length; i++){
			cols[i] = new TableColumn(cmdTable, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
			cols[i].setText(colNames[i]); 
			cols[i].pack();
		}
		
		cmdTableEditor = new TableEditor (cmdTable);
		cmdTableEditor.horizontalAlignment = SWT.LEFT;
		cmdTableEditor.grabHorizontal = true;
		cmdTable.addListener(SWT.MouseDown, new Listener() { @Override public void handleEvent(Event event) { tableMouseDownEvent(event); } });

		TableItem item = new TableItem(cmdTable, SWT.NONE);
		item.setText(new String[]{ "0", "0", "144000", "3000", "10000", "1" });
		addEmptyRow();

		
		resetButton = new Button(swtGroup, SWT.PUSH);
		resetButton.setText("Home/Reset");
		resetButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { resetButtonEvent(event); } });
		resetButton.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false, 1, 0));
		
		startButton = new Button(swtGroup, SWT.PUSH);
		startButton.setText("Start");
		startButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { startButtonEvent(event); } });
		startButton.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false, 1, 0));
		
		armButton = new Button(swtGroup, SWT.PUSH);
		armButton.setText("Arm Trigger");
		armButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { armButtonEvent(event); } });
		armButton.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false, 1, 0));
		
		stopButton = new Button(swtGroup, SWT.PUSH);
		stopButton.setText("Stop");
		stopButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { stopButtonEvent(event); } });
		stopButton.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false, 1, 0));

		
	}
	
	
	/** Copied from 'Snippet123'  Copyright (c) 2000, 2004 IBM Corporation and others. 
	 * [ org/eclipse/swt/snippets/Snippet124.java ] */
	private void tableMouseDownEvent(Event event){
		 
		Rectangle clientArea = cmdTable.getClientArea ();
		Point pt = new Point (event.x, event.y);
		int index = cmdTable.getTopIndex ();
		while (index < cmdTable.getItemCount ()) {
			boolean visible = false;
			final TableItem item = cmdTable.getItem (index);
			for (int i=0; i<cmdTable.getColumnCount (); i++) {
				Rectangle rect = item.getBounds (i);
				if (rect.contains (pt)) {
					final int column = i;
					final Text text = new Text(cmdTable, SWT.NONE);
					Listener textListener = new Listener () {
						public void handleEvent (final Event e) {
							switch (e.type) {
							case SWT.FocusOut:
								item.setText(column, Integer.toString(OneLiners.mustParseInt(text.getText())));
								checkTable();
								text.dispose ();
								break;
							case SWT.Traverse:
								switch (e.detail) {
								case SWT.TRAVERSE_RETURN:
									item.setText(column, Integer.toString(OneLiners.mustParseInt(text.getText())));
									checkTable();
									//FALL THROUGH
								case SWT.TRAVERSE_ESCAPE:
									text.dispose ();
									e.doit = false;
								}
								break;
							}
						}
					};
					text.addListener (SWT.FocusOut, textListener);
					text.addListener (SWT.Traverse, textListener);
					cmdTableEditor.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++;
		}
	}
	
	/** Checks if last line is non-zero speed, if so, adds one */
	private void checkTable(){
		//remove any zero speed entries not at the end
		for(int i=0; i < (cmdTable.getItemCount()-1); i++){
			TableItem item = cmdTable.getItem(i);
			int speed = OneLiners.mustParseInt(item.getText(3));
			if(speed <= 0){
				cmdTable.remove(i);
				i--;
			}
		}

		TableItem item = cmdTable.getItem(cmdTable.getItemCount()-1);
		int speed = Integer.parseInt(item.getText(3));
		if(speed > 0){
			addEmptyRow();
		}
	}
	
	private void startButtonEvent(Event event) {
		List<StepperCommand> cmds = buildCommands();
		proc.start(cmds);
	}
	
	private void resetButtonEvent(Event event) {
		proc.resetHome();
	}
	
	private List<StepperCommand> buildCommands() {
		ArrayList<StepperCommand> cmds = new ArrayList<StepperCommand>();
		
		for(TableItem item : cmdTable.getItems()){
			StepperCommand cmd = new StepperCommand();
			cmd.time = Integer.parseInt(item.getText(0));
			cmd.axis = Integer.parseInt(item.getText(1));
			cmd.position = Integer.parseInt(item.getText(2));
			cmd.speed = Integer.parseInt(item.getText(3));
			cmd.rampDuration = Integer.parseInt(item.getText(4));
			cmd.rampStartSpeed = Integer.parseInt(item.getText(5));
			cmds.add(cmd);
		}
		
		return cmds;
	}

	private void stopButtonEvent(Event event) {
		proc.stop();
	}
	
	private void armButtonEvent(Event event) {
		List<StepperCommand> cmds = buildCommands();
		proc.armTrigger(cmds);
	}
	
	private void addEmptyRow(){
		int nextTime = 0;
		if(cmdTable.getItemCount() >= 1){
			TableItem lastItem = cmdTable.getItem(cmdTable.getItemCount()-1);
			int time = OneLiners.mustParseInt(lastItem.getText(0));;			
			int pos2 = OneLiners.mustParseInt(lastItem.getText(2));
			int speed = OneLiners.mustParseInt(lastItem.getText(3));
			
			int pos1 = 0; //assume it started at 0
			if(cmdTable.getItemCount() >= 2){
				TableItem penultItem = cmdTable.getItem(cmdTable.getItemCount()-2);
				pos1 = OneLiners.mustParseInt(penultItem.getText(2));
			}
				
			nextTime = time + ((pos2 - pos1) * 1000) / speed;
						
		}
		TableItem item = new TableItem(cmdTable, SWT.NONE);
		
		item.setText(new String[]{ Integer.toString(nextTime), "0", "0", "-1", "0", "0" });
	}
	
	private void connectButtonEvent(Event event){
		proc.connect();
	}
	
	private void disconnectButtonEvent(Event event){
		proc.disconnect();
	}
	
	private void gotoButtonEvent(Event event) {
		proc.gotoPos(targetSpinner.getSelection());
	}

	@Override
	public void destroy() {
		swtGroup.dispose();
		proc.controllerDestroyed(this);
	}

	@Override
	public void generalControllerUpdate() {
		if(swtGroup.isDisposed()){ proc.controllerDestroyed(this); return; }
		IMSEProc.ensureFinalSWTUpdate(swtGroup.getDisplay(), this, new Runnable() {
			@Override
			public void run() {
				String status = proc.getDriverStatus();
				statusLabel.setText("pos = " + proc.getPositionNow() + ", " + status);
				
			}
		});
	}

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