package imseProc.stepperMotor.controllers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.eclipse.swt.widgets.Composite;

import algorithmrepository.exceptions.NotImplementedException;

import imseProc.core.ImagePipeController;
import imseProc.core.ImgPipe;
import imseProc.core.ImgSink;
import imseProc.core.Triggerable;


/** Image sink that hangs meta-data on an image sequence 
 * 
 * Used to drive the god-awful OWIS stepper controller
 * before I decided making my own would be less painful.
 * 
 * */
public class StepperHanger extends ImgPipe implements ImgSink, Triggerable {
		
	private List<StepperCommand> cmds = null;
	private ArrayList<double[]> recData = new ArrayList<double[]>(1024);
	private boolean recActive = false;
	private boolean triggerArmed = false;
		
	private SerialOWISDriver driver = null;
	
	public StepperHanger() {
	}

	@Override
	public ImagePipeController createPipeController(Class interfacingClass, Object args[], boolean asSink) {
		if(interfacingClass == Composite.class){
			ImagePipeController controller = new StepperSWTController(this, (Composite)args[0], (Integer)args[1]);
			controllers.add(controller);
			return controller;
		}
		
		return null;
	}
	
	public boolean isSequenceRunning(){ return driver.isSequenceActive(); }
	public boolean isDriverConnected(){ return driver != null; };
	public boolean isDriverBusy(){ return (driver != null) ? driver.isBusy() : false; };
	public String getDriverStatus(){ return (driver != null) ? driver.getStatus() : "Not connected."; };

	public long getPositionNow(){ return (driver != null) ? driver.getPos() : 0; };
	
	public void gotoPos(long pos){ 
		if(driver != null) 
			driver.gotoPos(pos);
	}

	public void connect() {
		if(driver != null)
			throw new RuntimeException("Already connected");
		driver = new SerialOWISDriver(this, "/dev/ttyS0", 9600);
	};

	public void disconnect() {
		if(driver != null){
			driver.destroy();
			driver = null;
		}
	};
	
	public void driverStatusChanged(){
		updateAllControllers();
	}
	
	/** Called by the driver */
	public void updatePosition(long sampleTimeMS, int pos[]){
		if(recActive){
			double entry[] = new double[pos.length+1];
			entry[0] = (double)sampleTimeMS / 1000.0;
			for(int i=0; i < pos.length; i++)
				entry[i+1] = pos[i];
			recData.add(entry);
		}
	}

	/** Called by the driver */
	public void sequenceStopped() {
		stop();
	}
	
	public void resetHome(){
		stop();
		if(driver != null)
			driver.resetHome();
	}
		
	public void start(List<StepperCommand> cmds){
		if(recActive)
			stop();
				
		if(driver == null)
			return;
		
		if(connectedSource != null){
			if(cmds != null)
				connectedSource.addMetaDataMap(StepperCommand.toMap(cmds, "stepper/commands"));
			if(recData != null)
				connectedSource.setSeriesMetaData("stepper/recPos", recData);
		}

		Collections.sort(cmds);
		
		recData.clear();
		recData.add(new double[]{ (double)System.currentTimeMillis() / 1000.0, (double)driver.getPos() });
		
		recActive = true;
		driver.startSequence(cmds);
		this.cmds = cmds;
	}
	
	public void stop(){
		recActive = false;
		triggerArmed = false;
		if(connectedSource != null){
			if(cmds != null)
				connectedSource.addMetaDataMap(StepperCommand.toMap(cmds, "stepper/commands"));
			if(recData != null)
				connectedSource.setSeriesMetaData("stepper/recPos", recData);
		}	
		if(driver != null && driver.isSequenceActive())
			driver.stopSequence();
	}
	
	public void armTrigger(List<StepperCommand> cmds){
		if(recActive)
			stop();
		
		this.cmds = cmds;
		triggerArmed = true;
	}

	@Override
	public void triggerStart() {
		if(triggerArmed)
			start(cmds);
	}

	@Override
	public void triggerAbort() {
		stop();
	}
	
	@Override
	public void imageChanged(int idx) {  	} // don't care

	@Override
	public boolean isIdle() { throw new NotImplementedException(); }
}
