
package org.sikuli.slides.sikuli;

import java.awt.Dimension;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import org.sikuli.api.DesktopScreenRegion;
import org.sikuli.api.ScreenRegion;
import org.sikuli.api.visual.Canvas;
import org.sikuli.api.visual.ScreenRegionCanvas;
import org.sikuli.slides.core.SikuliAction;
import org.sikuli.slides.listeners.tutorials.Observer;
import org.sikuli.slides.uis.TutorialConrollerUI;
import org.sikuli.slides.utils.Constants;
import org.sikuli.slides.utils.MyScreen;
import org.sikuli.slides.utils.UserPreferencesEditor;
import org.sikuli.slides.utils.Constants.NavigationStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Tutorial worker that processes users' interaction events when running the slides in tutorial mode.
 * This includes observing the controller UI to switch to the previous and next step in the tutorial mode. 
 * 
 * @author Khalid Alharbi
 *
 */

public class TutorialWorker extends SwingWorker<Boolean, Integer> implements Observer{
	private static final Logger logger = (Logger) LoggerFactory.getLogger(TutorialWorker.class);
	private UserPreferencesEditor prefsEditor = new UserPreferencesEditor();
	private static AtomicInteger currentStep=null;
	private List<SikuliAction> tasks=null;
	private final JLabel currentSlideLabel;
	private TutorialConrollerUI observable;
	
	public TutorialWorker(List<SikuliAction> tasks,JLabel currentSlideLabel,TutorialConrollerUI observable){
		this.tasks=tasks;
		this.currentSlideLabel=currentSlideLabel;
		currentStep=new AtomicInteger(-1);
		this.observable=observable;
		this.observable.register(this);
	}
	
	@Override
	protected Boolean doInBackground(){
		// Update the status. It sends data chunks to the process() method.
		publish(currentStep.get()+2);
		// run the first step
		doNextStep();
		
		return true;
	}

	@Override
	protected void done(){
	}
	@Override
	protected void process(List<Integer> stepsChunks){
		for(Integer step:stepsChunks){
			currentSlideLabel.setText((step).toString());
		}
	}
	
	private void doNextStep(){
		if(hasNextStep()){
			publish(currentStep.incrementAndGet()+1);
			tasks.get(currentStep.get()).doSikuliAction();
			doNextStep();
		}
	}
	
	/**
	 * Returns true if the presentation slides have more forward steps
	 * @return true if the presentation slides have more forward steps
	 */
	private boolean hasNextStep(){
		if(currentStep.get()<tasks.size()-1){
			return true;
		}
		else{
			logger.info("There are no next steps to move forward to.");
			showCompleteNotification();
			return false;
		}
	}

	/**
	 * Returns true if the presentation slides have more backward steps
	 * @return true if the presentation slides have more backward steps
	 */
	private boolean hasPreviousStep(){
		if(currentStep.get()>0){
			return true;
		}
		else{
			logger.info("There are no previous steps to go back to.");
			return false;
		}
	}

	@Override
	public void update(NavigationStatus navigationStatus) {
		// Don't skip steps when wait action is being running
		//TODO: disable UI next, previous buttons instead of using this flag
		if(Constants.IsWaitAction){
			return;
		}
		
		if(navigationStatus.equals(NavigationStatus.NEXT)){
			if(hasNextStep()){
				Constants.IsNextStep=true;
			}
		}
		else if(navigationStatus.equals(NavigationStatus.PREVIOUS)){
			if(hasPreviousStep()){
				Constants.IsPreviousStep=true;
				currentStep.decrementAndGet();
				currentStep.decrementAndGet();
			}
		}
	}
	
	private void showCompleteNotification() {
		Dimension screenDimension = MyScreen.getScreenDimensions();
		int x = (int) (screenDimension.getWidth() / 2.0);
		int y = (int) (screenDimension.getHeight() / 2.0);
		ScreenRegion fullScreenRegion = new DesktopScreenRegion(Constants.ScreenId,
				200,200,x, y);
		Canvas canvas = new ScreenRegionCanvas(new DesktopScreenRegion(Constants.ScreenId));
		canvas.addLabel(fullScreenRegion, 
				"Great job! You have successfully completed this tutorial.")
				.withFontSize(prefsEditor.getInstructionHintFontSize());
		canvas.display(prefsEditor.getLabelDisplayTime());
	}
}

@@ -52,24 +52,52 @@
 .Dimension;%0A
+import java.util.ArrayList;%0A
 import java.
@@ -103,24 +103,24 @@
 .util.List;%0A
-
 import java.
@@ -421,24 +421,81 @@
 kuliAction;%0A
+import org.sikuli.slides.listeners.tutorials.Observable;%0A
 import org.s
@@ -1180,16 +1180,28 @@
 Observer
+, Observable
 %7B%0A%09priva
@@ -1532,16 +1532,100 @@
 rvable;%0A
+%09private ArrayList%3CObserver%3E observers;%0A%09private NavigationStatus navigationStatus;%0A
 %09%0A%09publi
@@ -1855,16 +1855,16 @@
 rvable;%0A
-
 %09%09this.o
@@ -1889,16 +1889,62 @@
 (this);%0A
+%09%09this.observers = new ArrayList%3CObserver%3E();%0A
 %09%7D%0A%09%0A%09@O
@@ -4382,13 +4382,426 @@
 ime());%0A
+%09%09navigationStatus = NavigationStatus.DONE;%0A%09%09notifyObserver();%0A%09%7D%0A%09%0A%09@Override%0A%09public void register(Observer observer) %7B%0A%09%09observers.add(observer);%0A%09%7D%0A%0A%09@Override%0A%09public void unregister(Observer observer) %7B%0A%09%09int index = observers.indexOf(observer);%0A%09%09observers.remove(index);%0A%09%7D%0A%0A%09@Override%0A%09public void notifyObserver() %7B%0A%09%09for(final Observer observer : observers)%7B%0A%09%09%09observer.update(navigationStatus);%0A%09%09%7D%0A
 %09%7D%0A%7D%0A

add notify tutorial ui when steps are completed