/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/Tws.java,v 1.3 2004/09/23 16:50:51 pnever Exp $
 * $Revision: 1.3 $
 * $Date: 2004/09/23 16:50:51 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * 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 org.apache.slide.store.tamino.tools;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.apache.slide.store.tamino.tools.config.Config;
import org.apache.slide.store.tamino.tools.config.WebXml;
import org.apache.slide.store.tamino.tools.repairer.Repair;
import org.apache.slide.store.tamino.tools.stores.Stores;
import org.apache.slide.util.Properties;
import org.apache.slide.util.Strings;
import org.apache.slide.util.XAssertionFailed;
import org.apache.slide.util.XException;
import org.apache.slide.util.cli.Abort;
import org.apache.slide.util.cli.Actuals;
import org.apache.slide.util.cli.CommandLine;
import org.apache.slide.util.launcher.Generator;
import org.apache.slide.util.launcher.Tool;
import org.apache.slide.util.os.Catalina;
import org.apache.slide.util.os.Platform;
import org.jaxen.JaxenException;

/** Base class for all tools. **/
public abstract class Tws implements Tool {
    private final String[] cmds;
    private final String name;
    private final String outline;
    private final String[] description;
    private final String usage;
    
    private String namespace;
    
    protected Output out;
    
    public Tws(String[] cmds, String name, String outline, String[] description, String usage) {
        this.cmds = cmds;
        this.name = name;
        this.outline = outline;
        this.description = description;
        this.usage = usage;
        try {
            this.out = Output.create(Output.NORMAL);
        } catch (Abort e) {
            throw new XAssertionFailed(e);
        }
    }
    
    public String getNamespace() {
        if (namespace == null) {
            namespace = readNamespace();
        }
        return namespace;
    }
    
    public static String readNamespace() {
        try {
            // TODO: improved exception handling.
            return new WebXml().getNamespace();
        } catch (XException e) {
            throw new XAssertionFailed("cannot obtain namespace", e);
        } catch (JaxenException e) {
            throw new XAssertionFailed("cannot obtain namespace", e);
        }
    }
    
    private static final String ARG_HELP = "help";
    private static final String ARG_VERSION = "version";
    private static final String ARG_TOOLS = "tools";
    private static final String ARG_MODE = "mode";
    private static final String ARG_NOP = "nop";
    private static final String ARG_COMMAND   = "command";
    private static final String ARG_DIAGNOSE = "diagnose";
    
    private Properties props;
    
    public Catalina getCatalina() throws Abort {
        return Catalina.create(props);
    }
    
    public int run(Map map, String[] args) {
        CommandLine cl;
        
        props = new Properties();
        props.addAll(map);
        cl = createCommandLine();
        try {
            try {
                doRun(cl, args);
                return 0;
            } catch (XException e) {
                throw new Abort(e);
            } catch (IOException e) {
                throw new Abort(e);
            }
        } catch (Abort e) {
            out.quiet.println(indicator("failed", e.getInfo()));
            e.printDetails(out.verbose);
            return -1;
        }
    }
    
    private void doRun(CommandLine cl, String[] args) throws Abort, XException, IOException {
        Actuals actuals;
        Command command;
        String cmd;
        
        actuals = cl.parse(args);
        createOutput(actuals);
        command = generic(actuals);
        if (command == null) {
            cmd = actuals.getString(ARG_COMMAND);
            if (cmd == null) {
                throw new Abort("Command missing. Available commands: " + Strings.join(cmds, ", "));
            }
            command = dispatch(cmd, actuals);
        }
        if (actuals.getSwitch(ARG_NOP)) {
            out.normal.println(indicator("nop", command.getName()));
        } else {
            out.normal.println(indicator("run", command.getName()));
            command.run();
            out.normal.println(indicator("done", ""));
        }
    }

    private void block(String body) {
        final int WIDTH = 75;
        out.quiet.print(Strings.block("  ", body, WIDTH, Platform.CURRENT.lineSeparator));
    }

    public static final String INDENT = ">>>";
    
    private static final String PREFIX = "inodav";
    
    private Command generic(Actuals actuals) throws Abort {
        if (actuals.names().size() == 0 || actuals.getSwitch(ARG_HELP)) {
            return new Command("print help", out) {
                public void run() {
                    int i;
                    
                    out.quiet.println(name.toUpperCase() + " TOOL");
                    block(outline);
                    out.quiet.println("Synopsis:");
                    block(PREFIX + name + " <command> options");
                    out.quiet.println("Description:");
                    for (i = 0; i < description.length; i++) {
                        block(description[i]);
                    }
                    out.quiet.println();
                    out.quiet.println(usage);
                }
            };
        } else if (actuals.getSwitch(ARG_VERSION)) {
            return new Command("print version", out) {
                public void run() throws IOException {
                    out.quiet.println(Env.get().getVersion());
                }
            };
        } else if (actuals.getSwitch(ARG_DIAGNOSE)) {
            return new Command("print diagnose", out) {
                public void run() throws Abort {
                    out.quiet.println("java.home: " + Platform.JAVA_HOME);
                    out.quiet.println("java.version: " + System.getProperty("java.version"));
                    out.quiet.println("catalina.home: " + getCatalina().home);
                }
            };
        } else if (actuals.getSwitch(ARG_TOOLS)) {
            return new Command("list tools", out) {
                final Tws[] tools = createTools();
                public void run() throws Abort {
                    int i;
                    out.quiet.println("Available tools:");
                    for (i = 0; i < tools.length; i++) {
                        out.quiet.print(tools[i].getOutline());
                    }
                    out.quiet.println("Options available for every tool:");
                    out.quiet.println("  -? -help          Prints usage information for the respective tool.");
                    out.quiet.println("  -tools            Prints this message.");
                    out.quiet.println("  -version          Prints version information.");
                    out.quiet.println("  -diagnose         Prints diagnostic information.");
                    out.quiet.println("  -quiet            Prints less progress information.");
                    out.quiet.println("  -verbose          Prints more progress information.");
                    out.quiet.println("  -nop              Checks arguments only, does not execute anything.");
                }
            };
        } else {
            return null;
        }
    }
    
    public String getOutline() {
        final int WIDTH = 20;
        String first = "  " + Strings.rfill(' ', WIDTH - 2, PREFIX + name);
        String prefix = Strings.times(' ', WIDTH);
        String postfix = Platform.CURRENT.lineSeparator;
        return Strings.block(first, prefix, outline, 60, postfix, postfix);
    }
    
    public void createOutput(Actuals actuals) throws Abort {
        String mode;
        
        mode = actuals.getString(ARG_MODE, null);
        if (mode == null) {
            mode = props.get("output.mode");
        } else {
            mode = mode.substring(1);
        }
        out = Output.create(mode);
    }
    
    public CommandLine createCommandLine() {
        CommandLine cl;
        
        cl = commandLine();
        if (cmds != null) {
            cl.addAlternatives(ARG_COMMAND, cmds);
        }
        cl.addLiterals(ARG_HELP, new String[] {"-?", "-help"}, ARG_HELP);
        cl.addSwitch(ARG_VERSION);
        cl.addSwitch(ARG_DIAGNOSE);
        cl.addSwitch(ARG_TOOLS);
        cl.addSwitch(ARG_NOP);
        cl.addAlternatives(ARG_MODE, new String[] { "-" + Output.QUIET, "-" + Output.NORMAL, "-" + Output.VERBOSE } );
        return cl;
    }
    
    private static String indicator(String label, String msg) {
        return "[" + label + "] " + msg;
    }
    
    /**
     * Implementors have to pereform as much argument checking as possible without actually
     * modifying anthing.
     */
    public abstract Command dispatch(String cmd, Actuals actuals) throws Abort, XException, IOException;
    public abstract CommandLine commandLine();
    
    //--
    
    public static void main(String[] args) throws IOException {
        if (args.length < 4 || args.length > 5) {
            throw new IOException("illegal arguments, expected: dir properties jar vmargs nativeLib (jvmopt.d64)");
        }
        File dir = new File(args[0]).getAbsoluteFile();
        if (!dir.isDirectory()) {
            throw new IOException("directory not found: " + dir);
        }
        File props = new File(args[1]).getAbsoluteFile();
        if (!props.isFile()) {
            throw new IOException("launcher properties not found: " + props);
        }
        File jar = new File(args[2]);
        if (!jar.isFile()) {
            throw new IOException("jar not found: " + jar);
        }
        String nativeLib = args[3];
        String jvmopt_d64 = "";
        if (args.length == 5 && args[4].length() > 0) {
            jvmopt_d64 = args[4];
        }
        
        Tws[] tools = createTools();
        Properties env = new Properties();
        for (int i = 0; i < tools.length; i++) {
            String vmOpts = jvmopt_d64 + " -Dmain=" + tools[i].getClass().getName();
            Generator g = new Generator(dir, PREFIX + tools[i].name, props, jar, env, vmOpts, nativeLib);
            g.run();
        }
    }
    
    public static Tws[] createTools() {
        return new Tws[] {
            new Server(), new Stores(), new Config(), new Repair()
        };
    }
}


