/*
 * $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;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
//import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import com.sun.javatest.util.I18NResourceBundle;
import com.sun.javatest.util.Properties;
import com.sun.javatest.util.DynamicArray;
//import com.sun.interview.Interview;

/**
 * A table representing the collection of environments found in a set of environment files.
 */
public class TestEnvContext
{
    /**
     * This exception is to report problems using {@link TestEnvContext} objects.
     */
    public static class Fault extends Exception
    {
	Fault(I18NResourceBundle i18n, String s) {
	    super(i18n.getString(s));
	}

	Fault(I18NResourceBundle i18n, String s, Object o) {
	    super(i18n.getString(s, o));
	}

	Fault(I18NResourceBundle i18n, String s, Object[] o) {
	    super(i18n.getString(s, o));
	}
    }

    /**
     * Create a context from a set of environment (.jte) files.
     * @param files The environment files to read
     * @throws TestEnvContext.Fault if an error is found while reading the files
     */
    public TestEnvContext(File[] files) throws Fault {
	Vector n = new Vector();
	Vector p = new Vector();
	try {
	    if (files != null) {
		for (int i = 0; i < files.length; i++) {
		    File f = files[i];
		    add(p, n, load(f), f.getPath());
		}
	    }
	}
	finally {
	    propTables = new Map[p.size()];
	    p.copyInto(propTables);
	    propTableNames = new String[n.size()];
	    n.copyInto(propTableNames);
	    updateEnvTable(); 
	}
    }

    /**
     * Create a context from a specified set of named tables.
     * @param tables An array of tables giving the properties to be read
     * @param tableNames An array of names, one for each table in the tables array,
     *  that will be used to identify the spource of the properties in any
     *  environments that are created
     */
    public TestEnvContext(Map[] tables, String[] tableNames) {
	Vector n = new Vector();
	Vector p = new Vector();
	for (int i = 0; i < tables.length; i++) {
	    add(p, n, tables[i], tableNames[i]);
	}
	propTables = new Map[p.size()];
	p.copyInto(propTables);
	propTableNames = new String[n.size()];
	n.copyInto(propTableNames);
	updateEnvTable(); 
    }

    /**
     * Create a context from a named table.
     * @param table A table giving the properties to be read
     * @param tableName The name that will be used to identify the source
     * 	 of the properties in any environments that are created.
     */
    public TestEnvContext(Map table, String tableName) {
	Vector n = new Vector();
	Vector p = new Vector();
	add(p, n, table, tableName);
	propTables = new Map[p.size()];
	p.copyInto(propTables);
	propTableNames = new String[n.size()];
	n.copyInto(propTableNames);
	updateEnvTable(); 
    }

    /**
     * Get a environment from this set of environments.
     * @param name the name of the desired environment
     * @return the specified environment, or null if not found
     * @throws TestEnvironment.Fault if there is a problem creating
     *		the specified environment
     */
    public TestEnvironment getEnv(String name) throws TestEnvironment.Fault {
	if (isValidEnv(name))
	    return new TestEnvironment(name, propTables, propTableNames);
	else 
	    return null;
    }

    /**
     * Check if a name matches the name of one of the environments in this
     * set of environments.
     * @param name the name to check
     * @return true if and only if the name matches the name of one of the
     * environments in trhis set of environments
     */
    public boolean isValidEnv(String name) {
	// empty name is always valid (null is not)
	if (name.length() == 0)
	    return true;

	for (int i = 0; i < envNames.length; i++)
	    if (envNames[i].equals(name))
		return true;

	return false;
    }

    /**
     * Get an array containing all the names of environments in this set of 
     * environments.
     * @return an array containing the names of all the environments in this set
     */
    public String[] getEnvNames() {
	// does not include null string (for now)
	return envNames;
    }

    /**
     * Get an array containing all the names of environments that should 
     * appear in a menu of valid environment names. This is all environment
     * names, excluding those environments that define an entry "menu=false".
     * @return an array containing the names of all the environments in this set
     * that should appear in a menu of valid environment names
     */
    public String[] getEnvMenuNames() {
	return envMenuNames;
    }

    private Map load(File f) throws Fault {
	if (f == null) 
	    return null;

	try {
	    Properties p = new Properties();
	    Reader in = new BufferedReader(new FileReader(f));
	    p.load(in);
	    in.close();
	    
	    /*
	    if (f.getName().endsWith(".jti" )) {  //BETTER TO GET THIS FROM SOMEWHERE
		String interviewClassName = (String)p.get("INTERVIEW");
		if (interviewClassName == null)
		    throw new Fault(i18n, "tec.noInterview", f);
		Class c = Class.forName(interviewClassName);
		Interview i = (Interview)(c.newInstance());
		Map d = new Hashtable();
		i.export(d);
		return d;
	    }
	    else*/
		return p;
	}
	catch (FileNotFoundException e) {
	    throw new Fault(i18n, "tec.cantFindFile", f);
	}
	catch (IOException e) {
	    throw new Fault(i18n, "tec.ioError", new Object[] {f, e});
	}
	/*
	catch (ClassNotFoundException e) {
	    throw new Fault(i18n, "tec.interviewClassNotFound", 
			    new Object[] {f, e.getMessage()});
	}
	*/
	/*
	catch (IllegalAccessException e) {
	    throw new Fault(i18n, "tec.interviewClassAccess", 
			    new Object[] {f, e.getMessage()});
	}
	*/
	/*
	catch (InstantiationException e) {
	    throw new Fault(i18n, "tec.interviewClassInstantiation", 
			    new Object[] {f, e.getMessage()});
	}
	*/
    }

    private void add(Vector pv, Vector nv, Map p, String n) {
	if (p != null) {
	    pv.addElement(p);
	    nv.addElement(n);
	}
    }

    private void updateEnvTable() {
	// the tables given to the constructor ...
	Map[] tables = propTables;
	String[] tableNames = propTableNames;

	// defaults given to TestEnvironment
	Map[] defaultTables = TestEnvironment.defaultPropTables;
	String[] defaultTableNames = TestEnvironment.defaultPropTableNames;

	// if there are defaults, merge them with the TestEnvContext tables
	// for the purposes of determining the EnvTable
	if (defaultTables != null && defaultTables.length > 0) {
	    tables = (Map[]) (DynamicArray.join(defaultTables, tables));
	    tableNames = (String[]) (DynamicArray.join(defaultTableNames, tableNames));
	}

	Vector allVec = new Vector();
	Vector menuExcludeVec = new Vector();
	
	// scan all the property tables, looking for entries
	final String ENV_DOT = "env.";
	final String DOT_DESCRIPTION = ".description";
	final String DOT_FINDER = ".finder";
	final String DOT_SCRIPT = ".script";
	final String DOT_SCRIPT_DOT = ".script.";
	final String DOT_INHERITS = ".inherits";
	final String DOT_MENU = ".menu";
	final String DOT_TESTSUITE = ".testsuite";

	if (debug) 
	    System.err.println(getClass().getName() + ": trace");

	for (int i = 0; i < tables.length; i++) {
	    if (debug) 
		System.err.println("Checking " + tableNames[i] + " for environments...");

	    Map table = tables[i];
	    for (Iterator ii = table.keySet().iterator(); ii.hasNext(); ) {
		String prop = (String) (ii.next());
		String name = null;

		if (debug) 
		    System.err.println("Checking property " + prop);

		if (!prop.startsWith(ENV_DOT))
		    continue;
		
		if (prop.endsWith(DOT_INHERITS)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_INHERITS.length());
		}
		else if (prop.endsWith(DOT_MENU)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_MENU.length());
		    String value = (String) (table.get(prop));
		    if ("false".equals(value))
			sortedInsert(menuExcludeVec, name);
		} 
		else if (prop.endsWith(DOT_DESCRIPTION)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_DESCRIPTION.length());
		} 
		else if (prop.endsWith(DOT_FINDER)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_FINDER.length());
		} 
		else if (prop.endsWith(DOT_SCRIPT)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_SCRIPT.length());
		} 
		else if (prop.endsWith(DOT_TESTSUITE)) {
		    name = prop.substring(ENV_DOT.length(), prop.length() - DOT_TESTSUITE.length());
		} 
		else {
		    int lastDot = prop.lastIndexOf('.');
		    int scriptStartIndex = lastDot - DOT_SCRIPT_DOT.length() + 1;
		    if (scriptStartIndex > 0 &&
			prop.regionMatches(scriptStartIndex, DOT_SCRIPT_DOT, 0, DOT_SCRIPT_DOT.length())) {
			name = prop.substring(ENV_DOT.length(), scriptStartIndex);
		    }
		    else
			continue;
		}

		if (debug) 
		    System.err.println("found environment name: " + name);

		sortedInsert(allVec, name);
	    }
	}

	envNames = new String[allVec.size()];
	allVec.copyInto(envNames);
	Vector menuVec = (Vector)allVec.clone();
	for (int i = 0; i < menuExcludeVec.size(); i++)
	    menuVec.removeElement(menuExcludeVec.elementAt(i));
	envMenuNames = new String[menuVec.size()];
	menuVec.copyInto(envMenuNames);
    }

    private void sortedInsert(Vector v, String s) {
	for (int i = 0; i < v.size(); i++) {
	    int c = s.compareTo((String)(v.elementAt(i)));
	    if (c > 0) {
		v.insertElementAt(s, i);
		return;
	    }
	    else if (c == 0)
		return;
	}
	v.addElement(s);
    }

    private Map[] propTables;
    private String[] propTableNames;
    private String[] envNames;
    private String[] envMenuNames;

    private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(TestEnvContext.class);
    private static boolean debug = Boolean.getBoolean("debug." + TestEnvContext.class.getName());
}
