package speiger.src.coreengine.utils.tasks;

import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue;
import speiger.src.collections.objects.queues.ObjectPriorityDequeue;
import speiger.src.collections.objects.utils.ObjectPriorityQueues;
import speiger.src.coreengine.utils.counters.timers.CountdownSync;

public class MainThreadTaskProcessor
{
	ObjectPriorityDequeue<ITask> tasks = ObjectPriorityQueues.synchronize(new ObjectArrayFIFOQueue<>());
	Watchdog watch;
	Thread watchThread;
	boolean running = false;
	long timeout;
	
	public MainThreadTaskProcessor(long timeout, String name) {
		this.timeout = timeout;
		watch = new Watchdog(Thread.currentThread());
		watchThread = new Thread(watch, name+"-Thread-Watchdog");
		watchThread.start();
	}
	
	public void setTimeout(long timeout) {
		this.timeout = timeout;
	}
	
	public void addTask(ITask task) {
		tasks.enqueue(task);
	}
	
	public void finishAllTasks() {
		if(tasks.isEmpty()) return;
		running = true;
		while(!tasks.isEmpty()) {
			try {
				tasks.dequeue().execute();				
			}
			catch(InterruptedException e) {}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
		running = false;
	}
	
	public void update() {
		if(tasks.isEmpty()) return;
		watch.unlock();
		running = true;
		boolean interrupted = false;
		while(!tasks.isEmpty() && !(interrupted |= Thread.interrupted())) {
			ITask task = tasks.dequeue();
			try {
				task.execute();
			}
			catch(InterruptedException e) {
				interrupted = true;
			}
			catch(Exception e) {
				e.printStackTrace();
			}
			if(!task.isFinished()) {
				tasks.enqueue(task);
			}
		}
		running = false;
		if(!interrupted) {
			watchThread.interrupt();
		}
	}
	
	public void kill() {
		if(watchThread != null) return;
		watch.alive = false;
		watchThread.interrupt();
		watchThread = null;
	}
	
	class Watchdog implements Runnable {
		Thread owner;
		boolean alive = true;
		CountdownSync timer = new CountdownSync();
		Object lock = new Object();
		
		public Watchdog(Thread thread) {
			owner = thread;
		}
		
		@Override
		public void run() {
			while(alive) {
				try {
					synchronized(lock) {
						lock.wait();
					}
					timer.sync(timeout);
					if(running) {
						owner.interrupt();
					}
				}
				catch(InterruptedException e) {}
			}
		}
		
		public void unlock() {
			synchronized(lock) {
				lock.notify();	
			}
		}
	}
}
