package net.murakamin.sticker;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import net.murakamin.sticker.commands.ChildCommandExceptionHandler;
import net.murakamin.sticker.commands.Command;
import net.murakamin.sticker.commands.ExceptionCommand;
import net.murakamin.sticker.commands.ExecutableCommand;
import net.murakamin.sticker.commands.ParentCommand;
import net.murakamin.sticker.commands.StickerCommand;
import net.murakamin.sticker.commands.enums.PostChildCommandExecutionType;
import net.murakamin.sticker.commands.enums.PreChildCommandExecutionType;
import net.murakamin.sticker.commands.exception.CommandExecutionException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class CommandRunnerImpl implements CommandRunner
{

	protected static boolean COMMAND_COMPLETE = true;
	protected static boolean CONTINUE_PROCESSING = false;
	private final StickerContext context;

	private final ConnectionPool pool;

	private final File dataDir;

	private ExceptionCommand exceptionCommand;

	private boolean exceptionCommandSearched = false;

	private final Log log = LogFactory.getLog(Command.class);

	private boolean processExecute = true;

	/**
	 * The command specified by the argument is retrieved.
	 * 
	 * @param commands
	 *            Command storage buffer
	 * @param target
	 *            Command class that retrieves it
	 * @return All command objects corresponding by retrieval
	 */
	public static List<ExecutableCommand> findCommand(final Commands commands,
	        final Class<? extends ExecutableCommand> target)
	{
		List<ExecutableCommand> result = new ArrayList<ExecutableCommand>();

		for (ExecutableCommand cmd : commands)
		{
			if (cmd instanceof ParentCommand)
			{
				result.addAll(CommandRunnerImpl.findCommand(
				        ((ParentCommand) cmd).getChildlen(), target));
			}

			if (target.equals(cmd.getClass()))
			{
				result.add(cmd);
			}
		}

		return result;
	}

	public CommandRunnerImpl() throws Exception
	{
		this.context = new StickerContextImpl();
		this.pool = new ConnectionPoolImpl();
		this.dataDir = null;

		this.initialize();

	}

	public CommandRunnerImpl(final File dataDirectoryPath) throws Exception
	{
		this.context = new StickerContextImpl();
		this.pool = new ConnectionPoolImpl();
		this.dataDir = dataDirectoryPath;

		this.initialize();
	}

	public ConnectionPool getConnectionPool()
	{
		return this.pool;
	}

	public StickerContext getStickerContext()
	{
		return this.context;
	}

	private void initialize() throws Exception
	{
		if (Sticker.isDebug())
		{
			Logger.getLogger(Command.class).setLevel(Level.DEBUG);
		}

		if (this.dataDir == null)
		{
			((ConnectionPoolImpl) this.pool).connectToHsqlDb();
		} else
		{
			((ConnectionPoolImpl) this.pool).connectToHsqlDb(this.dataDir);
		}
	}

	public void run(final Command target) throws Exception
	{
		// Do nothing
	}

	public void run(final ParentCommand target) throws Exception
	{
		// search exception command
		if ((this.exceptionCommandSearched == false))
		{
			List<ExecutableCommand> result = CommandRunnerImpl.findCommand(
			        (target).getChildlen(), ExceptionCommand.class);
			if (result.size() > 0)
			{
				this.exceptionCommand = (ExceptionCommand) result.get(0);
			}
			this.exceptionCommandSearched = true;
		}

		// exception command process skip
		if (target instanceof ExceptionCommand)
		{
			return;
		}

		try
		{
			Commands childlen = target.getChildlen();
			if (target.preChildCommandExecute(this) == PreChildCommandExecutionType.CHILD_EXECUTE)
			{
				do
				{
					for (ExecutableCommand child : childlen)
					{
						if (!this.processExecute)
						{
							target.postChildCommandExecute(this);
							break;
						}
						child.execute(this);
					}
				} while (target.postChildCommandExecute(this) == PostChildCommandExecutionType.REPEAT_EXECUTE);
			}
		} catch (Exception e)
		{
			// child command exception handle
			if (target instanceof ChildCommandExceptionHandler)
			{
				try
				{
					((ChildCommandExceptionHandler) target)
					        .handleException(this);
				} catch (Exception ex)
				{
					throw new CommandExecutionException(target, ex);
				}
			}

			// exception block execute
			if (target instanceof StickerCommand)
			{
				if (this.exceptionCommand != null)
				{
					for (ExecutableCommand child : this.exceptionCommand
					        .getChildlen())
					{
						try
						{
							child.execute(this);
						} catch (Exception ex)
						{
							// TODO:Do nothing ?
						}
					}
				}
			}

			throw e;
		}
	}

	public void stopCommandExecution()
	{
		this.processExecute = false;
	}

}
