package com.interpress_project.modernshare.client.controller.command;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.eclipse.swt.widgets.Display;

import com.interpress_project.modernshare.client.controller.command.exceptions.CommandException;
import com.interpress_project.modernshare.client.events.LogEvent;
import com.interpress_project.modernshare.client.events.LogEventSource;
import com.interpress_project.modernshare.client.events.LogEventType;
import com.interpress_project.modernshare.client.view.ViewManager;
import com.interpress_project.modernshare.ipcommon.SystemBase;

/**
 * Represents a singleton.
 * @stereotype Singleton factory
 */
public class SVNCommandManager {
	private final SystemBase sb = SystemBase.getInstance();
	private final LogEventSource logEventSource = LogEventSource.getInstance();
	private CommandWorkerThread thread = null;
	private BlockingQueue<ISVNCommand> queue;

	/**
	 * Holds singleton instance
	 */
	private static SVNCommandManager instance = null;

	/**
	 * prevents instantiation
	 */
	private SVNCommandManager() {
		queue = new LinkedBlockingQueue<ISVNCommand>();
	}

	/**
	 * Returns the singleton instance.
	 @return	the singleton instance
	 */
	static public SVNCommandManager getInstance() {
		if (instance == null) {
			instance = new SVNCommandManager();
		}
		return instance;
	}

	/**
	 * addCommand
	 * @param command
	 */
	public void addCommand(ISVNCommand command) {
		try {
			queue.put(command);
			sb.getLogger().debug("Command registered in QUEUE.");
		}
		catch (InterruptedException ex) {
			sb.getLogger().error("InterruptedException registering a command.: ", ex);
		}
	}

	/**
	 * execCommand
	 * @param command
	 * @throws CommandException 
	 */
	public void execCommand(final ISVNCommand command) throws CommandException {
		sb.getLogger().debug("Enter sync execCommand(" + command.getClass().getName() + ")");
		command.execute();
		sb.getLogger().debug("Leave sync execCommand(" + command.getClass().getName() + ")");
	}

	/**
	 * start
	 */
	public void start() {
		if (thread != null) {
			return;
		}
		thread = new CommandWorkerThread(queue);
		thread.setDaemon(true);
		thread.start();

		sb.getLogger().debug("CommandManager started.");
	}

	/**
	 * stop
	 */
	public void stop() {
		if (thread != null) {
			thread.terminate();
			thread = null;
		}
		sb.getLogger().debug("CommandManager terminated.");
	}

	/**
	 * dispose
	 */
	public void dispose() {
		stop();
		queue.clear();
		sb.getLogger().debug("CommandManager disposed.");
	}

	/**
	 * CommandWorkerThread
	 * @author ys
	 */
	private class CommandWorkerThread extends Thread {
		private BlockingQueue<ISVNCommand> queue;
		private boolean bRunning;

		/**
		 * WorkerThread
		 */
		public CommandWorkerThread(BlockingQueue<ISVNCommand> queue) {
			this.queue = queue;
			this.bRunning = true;
		}

		/**
		 * run
		 */
		public void run() {
			sb.getLogger().debug("CommandWorkerThread() started.");

			while (bRunning) {
				final ISVNCommand command;
				try {
					// Timeout 6 seconds.
					command = (ISVNCommand) queue.poll(6, TimeUnit.SECONDS);
				}
				catch (InterruptedException e) {
					continue;
				}
				if (command == null) {
					continue;
				}

				sb.getLogger().debug("Take task from queue.");

				Display disp = ViewManager.getInstance().getShell().getDisplay();
				disp.syncExec(new Runnable() {
					public void run() {
						try {
							command.execute();
						}
						catch (CommandException ex) {
							logEventSource.fireEvent(new LogEvent(this, ex.getCause().getMessage(), LogEventType.ERROR, ex));
						}
					}
				});
			}
			sb.getLogger().debug("CommandWorkerThread() terminated.");
		}

		/**
		 * terminate
		 */
		public void terminate() {
			bRunning = false;
			interrupt();
		}
	}
}
