/**
 *   Copyright 2007 Y.Murakamin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package net.murakamin.sticker;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.Date;

import net.murakamin.sticker.commands.StickerCommand;
import net.murakamin.sticker.commands.exception.CommandExecutionException;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.xmlrules.DigesterLoader;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Sticker application entry point
 * 
 * @author Y.Murakamin
 */
public class Sticker implements ErrorHandler
{

	/**
	 * True when Sticker starts by debug mode
	 */
	private static boolean debug = false;

	public static final String VERSION = Messages.getString("sticker.version"); //$NON-NLS-1$

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

	/**
	 * The debug mode value of the Sticker configuration file is returned.
	 * 
	 * @return The debug mode value of the Sticker configuration file is
	 *         returned.
	 */
	public static boolean isDebug()
	{
		return Sticker.debug;
	}

	/**
	 * main procedure
	 * 
	 * @param args
	 *            command line arguments
	 */
	public static void main(final String[] args) throws Exception
	{

		Sticker instance = new Sticker();

		try
		{
			// command line parse
			CommandLineOption opts = instance.parseCommandLineOption(args);

			if (opts.isLogPropertyFileSet())
			{
				PropertyConfigurator.configure(opts.getLogPropertyFile()
				        .toURI().toURL());
			}

			instance.doProcess(opts.getConfigFile(), opts.getHsqldbDataFile());
			System.exit(0);
		} catch (ParseException pe)
		{
			instance.showHelp(pe);
		} catch (Exception e)
		{
			System.out.println(e);
		}
	}

	/**
	 * The debug mode value of the Sticker configuration file is set.
	 * 
	 * @param debug
	 *            The debug mode value of the Sticker configuration file is set.
	 */
	static void setDebug(final boolean debug)
	{
		Sticker.debug = debug;
	}

	/**
	 * The command line option object that the command line purser uses is
	 * generated.
	 * 
	 * @return command line option object
	 */
	private Options createCommandLineOptions()
	{
		Options options = new Options();

		Option opt = null;

		opt = new Option(
		        Messages.getString("sticker.help.cmd.short.help"), Messages.getString("sticker.help.cmd.long.help"), false, Messages.getString("sticker.help.cmd.helpmessage")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		options.addOption(opt);

		opt = new Option(
		        Messages.getString("sticker.help.cmd.short.log"), Messages.getString("sticker.help.cmd.long.log"), true, Messages.getString("sticker.help.cmd.logmessage")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		options.addOption(opt);

		opt = new Option(
		        Messages.getString("sticker.help.cmd.short.serialize"), Messages.getString("sticker.help.cmd.long.serialize"), true, Messages.getString("sticker.help.cmd.serializemessage")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		options.addOption(opt);

		return options;
	}

	/**
	 * Sticker main process
	 * 
	 * @param config
	 *            sticker configuration file object
	 * @param dataFile
	 *            HSQLDB storage directory object
	 * @throws Exception
	 */
	private void doProcess(final File config, final File dataFile)
	        throws Exception
	{

		long start = 0;
		StickerCommand stickerTag = null;
		CommandRunner runner = null;
		try
		{

			// configuration Digester and parse configuration file
			Digester digester = DigesterLoader.createDigester(new InputSource(
			        Sticker.class.getResourceAsStream(Messages
			                .getString("sticker.digesterrulepath")))); //$NON-NLS-1$
			digester.setErrorHandler(this);
			digester.setSchema(Sticker.class.getResource(
			        Messages.getString("sticker.schemapath")).toString()); //$NON-NLS-1$
			digester.setValidating(true);
			digester.setSchemaLanguage(Messages
			        .getString("sticker.schemalanguage")); //$NON-NLS-1$
			
			stickerTag = (StickerCommand) digester.parse(config);

			Sticker.setDebug(stickerTag.isDebug());

			// commands execute start.
			start = System.currentTimeMillis();
			this.log.info(Messages.getString("sticker.log.info.start")
			        + new SimpleDateFormat("  yyyy/MM/dd kk:mm:ss")
			                .format(new Date(start)));
			this.log.info(Messages.getString("sticker.log.info.engineVersion")
			        + Sticker.VERSION);

			if (dataFile == null)
			{
				// HSQLDB inmemory mode
				runner = new CommandRunnerImpl();
				stickerTag.execute(runner);
			} else
			{
				// HSQLDB inprocess mode
				runner = new CommandRunnerImpl(dataFile);
				stickerTag.execute(runner);
			}

			long stop = System.currentTimeMillis();
			this.log.info(Messages.getString("sticker.log.info.success")
			        + new SimpleDateFormat("  yyyy/MM/dd kk:mm:ss")
			                .format(new Date(stop)) + "(" + (stop - start)
			        / 1000 + "sec" + ")");

		} catch (Exception e)
		{
			String causeCommand = Messages
			        .getString("sticker.log.error.unknown");
			String causeCommandName = Messages
			        .getString("sticker.log.error.unknown");
			String causeException = StringUtils.EMPTY;

			if (e instanceof CommandExecutionException)
			{
				causeCommand = ((CommandExecutionException) e)
				        .getCauseCommand().toString();
				causeCommandName = ((CommandExecutionException) e)
				        .getCauseCommand().getClass().getName();
				causeException = e.getCause() != null ? e.getCause()
				        .getMessage() : StringUtils.EMPTY;
			}

			this.log.error(Messages.getString("sticker.log.error.error")
			        + e.getMessage() + SystemUtils.LINE_SEPARATOR + ":"
			        + causeException + SystemUtils.LINE_SEPARATOR
			        + Messages.getString("sticker.log.error.causeCommand")
			        + causeCommandName + SystemUtils.LINE_SEPARATOR
			        + Messages.getString("sticker.log.error.causeDescription")
			        + SystemUtils.LINE_SEPARATOR + causeCommand);

			long stop = System.currentTimeMillis();
			this.log.info(new SimpleDateFormat("  yyyy/MM/dd kk:mm:ss")
			        .format(new Date(stop))
			        + "(" + (stop - start) / 1000 + "sec" + ")");

			System.exit(1);
		} finally
		{
			if (stickerTag != null)
			{
				// database connection close process;
				stickerTag.postChildCommandExecute(runner);
			}
		}
	}

	/**
	 * handled error event from Digester parser
	 * 
	 * @param exception
	 */
	public void error(final SAXParseException exception) throws SAXException
	{
		System.exit(1);
	}

	/**
	 * handled fatal error event from Digester parser
	 * 
	 * @param exception
	 */
	public void fatalError(final SAXParseException exception)
	        throws SAXException
	{
		System.exit(1);
	}

	/**
	 * The command line character string is analyzed.
	 * 
	 * @param args
	 *            arguments
	 * @return CommandLineOption object
	 * @throws ParseException
	 * @throws MalformedURLException
	 */
	private CommandLineOption parseCommandLineOption(final String[] args)
	        throws ParseException, MalformedURLException
	{

		CommandLineOption result = null;
		Options opt = this.createCommandLineOptions();
		CommandLineParser parser = new BasicParser();
		CommandLine cmd = parser.parse(opt, args);

		if (cmd.getArgs().length == 0)
		{
			throw new ParseException(Messages
			        .getString("sticker.exception.config_not_found")); //$NON-NLS-1$
		}

		result = new CommandLineOption(new File(cmd.getArgs()[0]));

		if (cmd.hasOption(Messages.getString("sticker.help.cmd.long.log")))
		{
			result.setLogPropertyFile(new File(cmd.getOptionValue(Messages
			        .getString("sticker.help.cmd.long.log"))));
		}

		if (cmd
		        .hasOption(Messages
		                .getString("sticker.help.cmd.long.serialize")))
		{
			result.setHsqldbDataFile(new File(cmd.getOptionValue(Messages
			        .getString("sticker.help.cmd.long.serialize"))));
		}

		if (cmd.hasOption(Messages.getString("sticker.help.cmd.short.help")))
		{
			throw new ParseException(StringUtils.EMPTY);
		}

		return result;
	}

	/**
	 * show sticker help
	 * 
	 * @param pe
	 * @throws IOException
	 */
	private void showHelp(final ParseException pe) throws IOException
	{

		StringBuffer footer = new StringBuffer();

		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(SystemUtils.LINE_SEPARATOR);

		footer.append(Messages.getString("sticker.help.serialize1"));
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.serialize2"));
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(SystemUtils.LINE_SEPARATOR);

		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.helpmessage.footer1")); //$NON-NLS-1$
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.helpmessage.footer2")); //$NON-NLS-1$
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.helpmessage.footer3")); //$NON-NLS-1$
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.helpmessage.footer4")); //$NON-NLS-1$
		footer.append(SystemUtils.LINE_SEPARATOR);
		footer.append(Messages.getString("sticker.help.helpmessage.footer5")); //$NON-NLS-1$

		HelpFormatter formatter = new HelpFormatter();
		formatter
		        .printHelp(
		                Messages.getString("sticker.help.helpmessage.usage"), //$NON-NLS-1$
		                Messages
		                        .getString("sticker.help.helpmessage.versioninfo") + Sticker.VERSION, this //$NON-NLS-1$
		                        .createCommandLineOptions(), footer.toString(),
		                true);

	}

	/**
	 * handled warning event from Digester parser
	 * 
	 * @param exception
	 */
	public void warning(final SAXParseException exception) throws SAXException
	{
		// NOTE:exit?
		// System.exit(1);
	}
}
