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

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import com.sun.interview.FinalQuestion;
import com.sun.interview.Interview;
import com.sun.interview.Question;
import com.sun.interview.StringQuestion;
import com.sun.interview.YesNoQuestion;
import com.sun.javatest.InterviewParameters;
import com.sun.javatest.Parameters;
import com.sun.javatest.Keywords;
import com.sun.javatest.KeywordsFilter;
import com.sun.javatest.TestFilter;
import com.sun.javatest.TestSuite;
import com.sun.javatest.util.StringArray;

/**
 * This interview collects the keyword parameters. It is normally used as
 * one of a series of sub-interviews that collect the parameter information for
 * a test run.
 */
public class KeywordsInterview 
    extends Interview
    implements Parameters.MutableKeywordsParameters
{
    /**
     * Create an interview.
     * @param parent The parent interview of which this is a child.
     * @throws Interview.Fault if there is a problem while creating the interview.
     */
    public KeywordsInterview(InterviewParameters parent) 
	throws Interview.Fault
    {
	super(parent, "keywords");
	this.parent = parent;
	setResourceBundle("i18n");
	setFirstQuestion(qNeedKeywords);
    }


    /**
     * Get a Keywords object based on the information in the interview.
     * @return A Keywords object based on the information in the interview.
     * @see #setKeywords
     */
    public Keywords getKeywords() {
	if (qNeedKeywords.getValue() == YesNoQuestion.YES) {
	    updateCachedKeywordsData();
	    return cachedKeywords;
	}
	else
	    return null;
    }

    public void setKeywords(int mode, String value) {
	if (value == null)
	    setKeywordsMode(NO_KEYWORDS);
	else {
	    setKeywordsMode(MATCH_KEYWORDS);
	    setMatchKeywords(mode, value);
	}
    }

    public int getKeywordsMode() {
	return (qNeedKeywords.getValue() == YesNoQuestion.YES 
		? MATCH_KEYWORDS : NO_KEYWORDS);
    }

    public void setKeywordsMode(int mode) {
	qNeedKeywords.setValue(mode == MATCH_KEYWORDS ? YesNoQuestion.YES : YesNoQuestion.NO);
    }
	    
    public int getMatchKeywordsMode() {
	return qKeywords.getMode();
    }

    public String getMatchKeywordsValue() {
	return qKeywords.getModeValue();
    }

    public void setMatchKeywords(int mode, String value) {
	qKeywords.setValue(mode, value);
    }


    /**
     * Get a test filter based on the keyword expression in the interview.
     * @return a test filter based on the keyword expression in the interview.
     */
    public TestFilter getKeywordFilter() {
	if (qNeedKeywords.getValue() == YesNoQuestion.YES) {
	    updateCachedKeywordsData();
	    return cachedKeywordsFilter;
	}
	else
	    return null;
    }

    //----------------------------------------------------------------------------
    //
    // Need keywords

    private YesNoQuestion qNeedKeywords = new YesNoQuestion(this, "needKeywords", YesNoQuestion.NO) {
	protected Question getNext() {
	    if (value == null)
		return null;
	    else if (value == YES)
		return qKeywords;
	    else
		return qEnd;
	}
    };

    //----------------------------------------------------------------------------
    //
    // Keywords

    private static abstract class KeywordsQuestion extends StringQuestion
    {
	KeywordsQuestion(Interview i, String t) {
	    super(i, t);
	}

	public int getMode() {
	    return mode;
	}

	public String getModeValue() {
	    return modeValue;
	}

	public void setValue(int m, String v) {
	    //System.err.println("KI: m=" + m + " v=" + v);
	    switch (m) {
	    case ANY_OF:
		mode = ANY_OF;
		modeValue = v;
		super.setValue(termsToExpr(v, " | "));
		break;

	    case ALL_OF:
		mode = ALL_OF;
		modeValue = v;
		super.setValue(termsToExpr(v, " & "));
		break;

	    default:
		mode = EXPR;
		modeValue = v;
		super.setValue(v == null || v.length() == 0 ? null : v);
	    }
	}

	public void setValue(String v) {
	    //System.err.println("KI: v=" + v);
	    if (v != null && v.equals(getValue()))
		return;

	    mode = EXPR;
	    modeValue = v;
	    super.setValue(modeValue);
	}

	public void load(Map data) {
	    Object m = data.get(tag + ".mode");
	    Object v = data.get(tag + ".value");
	    //System.err.println("KI.load: m=" + m + " v=" + v);
	    if (m == null) 
		super.load(data); // support existing interview files
	    else if (m instanceof String && (v == null || v instanceof String)) {
		String ms = (String) m;
		String vs = (String) v;
		if (ms.equals("allOf")) 
		    setValue(ALL_OF, vs);
		else if (ms.equals("anyOf"))
		    setValue(ANY_OF, vs);
		else
		    setValue(EXPR, vs);
	    }
	}

	public void save(Map data) {
	    super.save(data);  // for backwards compatibility
	    String modeText = (mode == ANY_OF ? "anyOf" : mode == ALL_OF ? "allOf" : "expr");
	    data.put(tag + ".mode", modeText);
	    if (modeValue != null)
		data.put(tag + ".value", modeValue);
	    //System.err.println("KI.save: m=" + mode + " v=" + modeValue);
	}

	private String termsToExpr(String list, String op) {
	    if (list == null || list.length() == 0)
		return null;

	    String[] keys = StringArray.split(list);
	    StringBuffer sb = new StringBuffer(list.length());
	    for (int i = 0; i < keys.length; i++) {
		if (i > 0)
		    sb.append(op);
		sb.append(keys[i]);
	    }
	    //System.err.println("KI.t2E: list=" + list + " result=" + sb);
	    return sb.toString();
	}

	private int mode;
	private String modeValue;	
    }

    private KeywordsQuestion qKeywords = new KeywordsQuestion(this, "keywords") {
	protected Question getNext() {
	    if (value == null || value.length() == 0)
		return null;
	    else {
		return qEnd;
	    }
	}
        
        public boolean isValueValid() {
            updateCachedKeywordsData();
            return cachedKeywordsError == null;
        }
    };

    private void updateCachedKeywordsData() {
	String expr = qKeywords.getValue();
	if (!equal(cachedKeywords_expr, expr)) {
	    try {
		TestSuite ts = parent.getTestSuite();

		if (ts == null) {
		    throw new IllegalStateException("Null TestSuite, cannot get keyword info");
		}

		String[] validKeywords = ts.getKeywords();
		HashSet validKeywordsSet;
		if (validKeywords == null)
		    validKeywordsSet = null;
		else
		    validKeywordsSet = new HashSet(Arrays.asList(validKeywords));

		int mode = qKeywords.getMode();
		String modeName = (mode == ANY_OF ? Keywords.ANY_OF 
				   : mode == ALL_OF ? Keywords.ALL_OF 
				   : Keywords.EXPR);
		Keywords k = Keywords.create(modeName, qKeywords.getModeValue(), validKeywordsSet);
		cachedKeywords = k;
		cachedKeywordsFilter = new KeywordsFilter(k);
		cachedKeywordsError = null;
	    }
	    catch (Keywords.Fault e) {
		cachedKeywords = null;
		cachedKeywordsFilter = null;
		cachedKeywordsError = e.getMessage();
	    }
	    cachedKeywords_expr = expr;
	}
    }

    private Keywords cachedKeywords;
    private String cachedKeywords_expr;
    private String cachedKeywordsError;
    private TestFilter cachedKeywordsFilter;

    private Question qEnd = new FinalQuestion(this);

    //----------------------------------------------------------------------------

    private static boolean equal(String s1, String s2) {
	return (s1 == null ? s2 == null : s1.equals(s2));
    }

    //--------------------------------------------------------

    private InterviewParameters parent;
}
