package org.wikiwebserver.distribute.se.worker;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.wikiwebserver.distribute.interfaces.RemoteWorker;
import org.wikiwebserver.distribute.interfaces.Task;
import org.wikiwebserver.distribute.interfaces.TaskStateChangeListener;
import org.wikiwebserver.distribute.interfaces.Transferer;
import org.wikiwebserver.distribute.se.worker.task.ExceptionPostTask;

public class RemoteWorkerImpl implements RemoteWorker {
	
    private String error;
	private List<TaskStateChangeListener> taskStateChangeListeners 
		= new LinkedList<TaskStateChangeListener>();

	public void run() {
	    
		int failedAttempts = 0;
		while (failedAttempts < 20) {
			
			try {

			    // Wait until it is time to ask the server for a task
			    long time = System.currentTimeMillis();
			    while (time < ServerCommunicator.getNextTaskCheckTime()) {
			        Thread.sleep(10);
			        time = System.currentTimeMillis();
			    }

				// Get next task from server
				final Task task = ServerCommunicator.getTask();
                error = null;				
				
				// There was no task
				if (task == null) continue;

			
				Runnable runnable = new Runnable() {
					public void run() {
					    
                        fireTaskStateChangeEvent(task);						    
		                task.setStartTime(System.currentTimeMillis());						    
						try {
						    if (task instanceof Runnable) {
						        ((Runnable)task).run();
						    }
						    else if (task instanceof Transferer) {
						        ServerCommunicator.performTransfer(task);
						    }
						} 
						catch (Throwable ex) {
							ex.printStackTrace();
							
							// Transfer the exception back to server
							try {
					            Task exceptionResponse = new ExceptionPostTask(ex);
					            exceptionResponse.setTaskId(task.getTaskId());		
					            ServerCommunicator.performTransfer(task);
							} catch (Exception tex) {}
						}
						task.setComplete(true);
						fireTaskStateChangeEvent(task);
						
					}
				};		
				
				// Run the task
				Thread taskRunner = new Thread(runnable);
				taskRunner.setName("Task " + task.getTaskId());
				taskRunner.setDaemon(true);
				taskRunner.start();
				
				failedAttempts = 0;
								
			} 
			catch (IllegalArgumentException ex) {
			    // Bad configuration
                error = ex.getMessage();
                try { Thread.sleep(100);
                } catch (InterruptedException ex1) {}                
			}
			catch (Throwable ex) {
                
			    failedAttempts ++;
                ex.printStackTrace();
                
                error = ex.getMessage();
                System.err.println(error);
                
                try { Thread.sleep(30000);
                } catch (InterruptedException ex1) {}
            }
		}
	}
	
	public String getError() {
	    return error;
	}

	public void addTaskStateChangeListener(TaskStateChangeListener listener) {
	    synchronized (taskStateChangeListeners) {
	        taskStateChangeListeners.add(listener);
	    }
	}
	
	private void fireTaskStateChangeEvent(Task task) {
	    synchronized (taskStateChangeListeners) {
    		Iterator<TaskStateChangeListener> i = taskStateChangeListeners.iterator();
    		while (i.hasNext()) {
    			TaskStateChangeListener listener = i.next();
    			listener.taskStateChanged(task);
    		}
	    }
	}	
}

