/*
 * $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 com.sun.interview.ChoiceArrayQuestion;
import com.sun.interview.FinalQuestion;
import com.sun.interview.Interview;
import com.sun.interview.Question;
import com.sun.interview.YesNoQuestion;
import com.sun.javatest.InterviewParameters;
import com.sun.javatest.Parameters;
import com.sun.javatest.Status;
import com.sun.javatest.StatusFilter;
import com.sun.javatest.TestFilter;
import com.sun.javatest.TestResultTable;
import com.sun.javatest.WorkDirectory;

/**
 * This interview collects the "prior status" test filter parameters.
 * It is normally used as one of a series of sub-interviews that collect
 * the parameter information for a test run.
 */
public class PriorStatusInterview
    extends Interview
    implements Parameters.MutablePriorStatusParameters
{
    /**
     * 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 PriorStatusInterview(InterviewParameters parent)
        throws Interview.Fault
    {
        super(parent, "priorStatus");
        this.parent = parent;
        setResourceBundle("i18n");
        setFirstQuestion(qNeedStatus);
    }

    /**
     * Get an array of boolean values from the interview, which indicate
     * which "prior status" values will cause a test to be selected for execution.
     * The array of values can be indexed with {@link Status#PASSED}, {@link Status#FAILED},
     * etc.
     * @return an array of boolean values which indicate which "prior status" values
     * will cause a test to be selected for execution.
     * @see #setPriorStatusValues
     */
    public boolean[] getPriorStatusValues() {
        if (qNeedStatus.getValue() == YesNoQuestion.YES)
            return getMatchPriorStatusValues();
        else
            return null;
    }

    public void setPriorStatusValues(boolean[] b) {
        if (b == null)
            setPriorStatusMode(NO_PRIOR_STATUS);
        else {
            setPriorStatusMode(MATCH_PRIOR_STATUS);
            setMatchPriorStatusValues(b);
        }
    }

    public int getPriorStatusMode() {
        return (qNeedStatus.getValue() == YesNoQuestion.YES ? MATCH_PRIOR_STATUS : NO_PRIOR_STATUS);
    }

    public void setPriorStatusMode(int mode) {
        qNeedStatus.setValue(mode == MATCH_PRIOR_STATUS ? YesNoQuestion.YES : YesNoQuestion.NO);
    }

    public boolean[] getMatchPriorStatusValues() {
        boolean[] choices = qStatus.getValue();
        boolean[] b = new boolean[Status.NUM_STATES];
        b[Status.ERROR] = choices[0];
        b[Status.FAILED] = choices[1];
        b[Status.NOT_RUN] = choices[2];
        b[Status.PASSED] = choices[3];
        return b;
    }

    /**
     * Set an array of boolean values in the interview, which indicate
     * which "prior status" values will cause a test to be selected for execution.
     * The array of values can be indexed with {@link Status#PASSED}, {@link Status#FAILED},
     * etc.
     * @param b an array of {@link Status#NUM_STATES} boolean values which indicate
     * which "prior status" values will cause a test to be selected for execution.
     * @see #getMatchPriorStatusValues
     */
    public void setMatchPriorStatusValues(boolean[] b) {
        if (b.length != Status.NUM_STATES)
            throw new IllegalArgumentException();

        boolean[] choices = new boolean[Status.NUM_STATES];
        choices[0] = b[Status.ERROR];
        choices[1] = b[Status.FAILED];
        choices[2] = b[Status.NOT_RUN];
        choices[3] = b[Status.PASSED];
        qStatus.setValue(choices);
    }


    /**
     * Get a test filter generated from the status test values in the interview.
     * @return a test filter generated from the status test values in the interview
     * @see #getPriorStatusValues
     */
    public TestFilter getStatusFilter() {
        updateCachedStatusFilter();
        return cachedStatusFilter;
    }

    //----------------------------------------------------------------------------
    //
    // Need status

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


    //----------------------------------------------------------------------------
    //
    // Status

    // I18N...
    private static final String PASSED = "passed";
    private static final String FAILED = "failed";
    private static final String ERROR = "error";
    private static final String NOT_RUN = "not_run";
    private static int[] choiceToStatus =  {Status.ERROR, Status.FAILED,
                                            Status.NOT_RUN, Status.PASSED};

    private ChoiceArrayQuestion qStatus = new ChoiceArrayQuestion(this, "status") {
        {
            setChoices(new String[] {ERROR, FAILED, NOT_RUN, PASSED}, true);
        }

        public boolean isValueValid() {
            // one of the choices must be set
            for (int i = 0; i < value.length; i++) {
                if (value[i])
                    return true;
            }
            return false;
        }


        protected Question getNext() {
            return qEnd;
        }
    };

    private void updateCachedStatusFilter() {
        WorkDirectory wd = parent.getWorkDirectory();
        TestResultTable r = (wd == null ? null : wd.getTestResultTable());
        boolean[] s = getPriorStatusValues();
        if (r == null || s == null)
            cachedStatusFilter = null;
        else if (cachedStatusFilter == null
                 || cachedStatusFilter.getTestResultTable() != r
                 || !equal(cachedStatusFilter.getStatusValues(), s))
            cachedStatusFilter = new StatusFilter(s, r);
        // else
        //   cachedStatusFilter is OK
    }

    private static boolean equal(boolean[] b1, boolean[] b2) {
        if (b1 == null || b2 == null)
            return (b1 == b2);
        if (b1.length != b2.length)
            return false;
        for (int i = 0; i < b1.length; i++) {
            if (b1[i] != b2[i])
                return false;
        }
        return true;
    }

    private StatusFilter cachedStatusFilter;

    //----------------------------------------------------------------------------
    //
    // End

    private Question qEnd = new FinalQuestion(this);

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

    private InterviewParameters parent;
}
