//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   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 de.ugoe.cs.autoquest.ui;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;

import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import de.ugoe.cs.autoquest.log4j.Log4JLogger;
import de.ugoe.cs.autoquest.plugin.PluginLoader;
import de.ugoe.cs.autoquest.plugin.AutoQUESTPlugin;
import de.ugoe.cs.autoquest.ui.swt.MainWindow;
import de.ugoe.cs.util.console.CommandExecuter;
import de.ugoe.cs.util.console.Console;
import de.ugoe.cs.util.console.TextConsole;

/**
 * <p>
 * Start-up class of the application.
 * </p>
 * <p>
 * It sets up and starts the {@link Console}.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class Runner {

    public enum UITYPE {
        text, swt
    };
    
    public enum LEVELENUM {
        OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL;
        
        public Level getLevel() {
            switch (this)
            {
                case OFF:
                    return Level.OFF;
                case SEVERE:
                    return Level.SEVERE;
                case ALL:
                    return Level.ALL;
                case CONFIG:
                    return Level.CONFIG;
                case FINE:
                    return Level.FINE;
                case FINER:
                    return Level.FINER;
                case FINEST:
                    return Level.FINEST;
                case INFO:
                    return Level.INFO;
                case WARNING:
                    return Level.WARNING;
                default:
                    throw new AssertionError("reached source code that should be unreachable");
            }
        }
    }

    /**
     * <p>
     * Main method of the application.
     * </p>
     * 
     * @param args
     *            if parameters are defined, they are interpreted as commands for the
     *            {@link Console} and executed before the user can use the console; can be used to
     *            perform batch operations
     */
    public static void main(String[] args) {
        
        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.misc", Runner.class.getClassLoader());
        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.sequences", Runner.class.getClassLoader());
        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.usability", Runner.class.getClassLoader());
        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.commands.usage", Runner.class.getClassLoader());
        CommandExecuter.getInstance().addCommandPackage("de.ugoe.cs.autoquest.ui.swt.commands", Runner.class.getClassLoader());

        PluginLoader pluginLoader = new PluginLoader(new File("lib"));
        pluginLoader.load();

        for (AutoQUESTPlugin plugin : pluginLoader.getPlugins()) {
            for (String commandPackage : plugin.getCommandPackages()) {
                CommandExecuter.getInstance().addCommandPackage(commandPackage, plugin.getClass().getClassLoader());
            }
        }

        OptionParser parser = new OptionParser();
        OptionSpec<LEVELENUM> log4j =
            parser.accepts("log4j", "Allowed values: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL").withRequiredArg()
                .ofType(LEVELENUM.class).defaultsTo(LEVELENUM.INFO);
        OptionSpec<UITYPE> ui =
            parser.accepts("ui", "Allowed values: text, swt").withRequiredArg()
                .ofType(UITYPE.class).defaultsTo(UITYPE.text);
        OptionSpec<LEVELENUM> trace =
            parser.accepts("trace", "Allowed values: OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, ALL").withRequiredArg()
                .ofType(LEVELENUM.class).defaultsTo(LEVELENUM.WARNING);
        OptionSet options = parser.parse(args);

        List<String> startupCommands = options.nonOptionArguments();
        try {
            if(options.valueOf(log4j)!=LEVELENUM.OFF) {
                new Log4JLogger(options.valueOf(log4j).getLevel());
            }

            switch (options.valueOf(ui))
            {
                case text:
                    TextConsole textConsole = new TextConsole(options.valueOf(trace).getLevel());
                    for (String command : startupCommands) {
                        CommandExecuter.getInstance().exec(command);
                    }
                    textConsole.run();
                    break;
                case swt:
                    MainWindow mainWindow = new MainWindow(startupCommands, options.valueOf(trace).getLevel());
                    mainWindow.open();
                    break;
                default:
                    throw new AssertionError("reached source code that should be unreachable");
            }
        }
        catch (OptionException e) {
            System.err.println("Invalid Parameters: " + e.getMessage());
            try {
                parser.printHelpOn(System.out);
            }
            catch (IOException e1) {
                // ignore exception.
            }
        }
    }

}
