/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.lib;

import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.io.IOException;
import java.io.PrintWriter;

import com.sun.javatest.Script;
import com.sun.javatest.Status;
import com.sun.javatest.TestResult;
import com.sun.javatest.TestDescription;
import com.sun.javatest.TestEnvironment;
import com.sun.javatest.util.StringArray;

/**
 * Default script, which delegates to one of a number of scripts defined in
 * environment entries, according to the keywords on the test description.
 */
public class KeywordScript extends Script
{
    /**
     * Run the script, using the parameters set up by the standard initialization
     * methods.
     */
    public void run() {

        PrintWriter trOut = getTestResult().getTestCommentWriter();
        TestDescription td = getTestDescription();

        for (int i = 0; i < scriptArgs.length; i++) {
            if (scriptArgs[i].equals("-debug"))
                debug = true;
            else {
                setStatus(Status.error("bad args for script: " + scriptArgs[i]));
                return;
            }   // else
        }   // for

        String prefix = "script.";
        Set testKeys = td.getKeywordTable();
        Vector choices = new Vector(); // the set of choices
        Vector matches = new Vector(); // the set of matches
        int wordsMatchingInMatches = 0;// the number of words matching

    findMatch:
        for (Iterator iter = env.keys().iterator(); iter.hasNext(); ) {
            String key = (String) (iter.next());

            // if the key does not begin with the `script.' prefix, ignore key
            if (!key.startsWith(prefix))
                continue;

            if (debug)
                trOut.println("CHECKING " + key);

            String keyList = key.substring(prefix.length()).replace('_', ' ').toLowerCase();
            String[] keys = StringArray.split(keyList);

            choices.addElement(keyList);

            if (debug)
                trOut.println("keys: " + StringArray.join(keys));

            // if there are no words after the `script.' prefix,
            // or if it has fewer words than the best match so far, ignore key
            if (keys == null || keys.length < wordsMatchingInMatches)
                continue;

            for (int i = 0; i < keys.length; i++) {
                // if key has a word that is not for the test, ignore key
                if (!testKeys.contains(keys[i])) {

                    if (debug)
                        trOut.println("discarding, because of " + keys[i]);

                    continue findMatch;
                }
            }

            // see if key is better than best so far
            if (keys.length > wordsMatchingInMatches) {
                // update best so far

                if (debug)
                    trOut.println("new best match, " + keys.length + " keys");

                matches = new Vector();
                wordsMatchingInMatches = keys.length;
            }

            // this key deserves note
            matches.addElement(key);
        }   // for

        // check we have a unique script selected
        String name = env.getName();
        String envName = (name.length() == 0 ?
                          "The anonymous environment" :
                          "Environment `" + env.getName() + "'");
        if (matches.size() == 0) {
            if (choices.size() == 0) {
                String s = envName + " has no `script' entries";
                trOut.println(s);
                setStatus(Status.error(s));
                return;
            }
            else {
                String s = envName + " has no suitable `script' entry";
                trOut.println(s);
                trOut.println("The keyword combinations for scripts in this environment are: ");
                for (int i = 0; i < choices.size(); i++) {
                    trOut.println((String)choices.elementAt(i));
                }   // for

                setStatus(Status.error(s));
                return;
            }   // inner else
        } else if (matches.size() > 1) {
            String s = envName + " has ambiguous `script' entries";
            trOut.println(s);
            for (int i = 0; i < matches.size(); i++) {
                trOut.println(i + ": " + matches.elementAt(i));
            }   // for

            setStatus(Status.error(s));
            return;
        }   // else if

        String bestScript = (String)matches.elementAt(0);
        //trOut.report.println("BEST " + bestScript);

        try {
            String[] command = env.lookup(bestScript);
            if (command.length == 0) {
                String s = "INTERNAL ERROR: failed to lookup key: " + bestScript;
                trOut.println(s);
                setStatus(Status.error(s));
                return;
            }

            trOut.println("test: " + td.getRootRelativeURL());
            trOut.println("script: " + this.getClass().getName() + " " +
                          StringArray.join(scriptArgs));

            String[] msgs = {
                "Based on these keywords:    " +
                bestScript.substring(prefix.length()).replace('_', ' ').toLowerCase(),
                "this script has now been selected: " + "   " +
                StringArray.join(command) };
            printStrArr(trOut, msgs);

            try {
                Class c = Class.forName(command[0]);

                Script script = (Script)c.newInstance();
                String[] scriptArgs = new String[command.length - 1];
                System.arraycopy(command, 1, scriptArgs, 0, scriptArgs.length);
                initDelegate(script, scriptArgs);

                script.run();
            }
            catch (ClassNotFoundException ex) {
                setStatus(Status.error("Can't find class `" +
                                     command[0] + "' for `" + env.getName() + "'"));
            }
            catch (IllegalAccessException ex) {
                setStatus(Status.error("Illegal access to class `" +
                                     command[0] + "' for `" + env.getName() + "'"));
            }
            catch (InstantiationException ex) {
                setStatus(Status.error("Can't instantiate class`" +
                                     command[0] + "' for `" + env.getName() + "'"));
            }
        }
        catch (TestEnvironment.Fault ex) {
            setStatus(Status.error("environment `" +
                                      env.getName() +
                                      "' has bad `script' entry for `" +
                                      bestScript +"'"));
        }
    }

    public Status run(String[] args, TestDescription td, TestEnvironment env) {
        throw new Error("Method not applicable.");
    }

    private static void printStrArr(PrintWriter pw, String[] data) {
        if(data == null) return;

        for(int i = 0; i < data.length; i++) {
            pw.println(data[i]);
        }
    }

    private void setStatus(Status s) {
        TestResult tr = getTestResult();
        tr.setEnvironment(env);
        tr.setStatus(s);
        try {
            tr.writeResults(workDir, backupPolicy);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static boolean debug = false;
}
