/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * 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 vogar;

import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;

/**
 * Contains an outcome for a test, along with some metadata pertaining to the history of this test,
 * including a list of previous outcomes, an outcome corresponding to the tag Vogar is being run
 * with, if applicable, and the expectation for this test, so that result value information is
 * available.
 */
public final class AnnotatedOutcome {
    public static Ordering<AnnotatedOutcome> ORDER_BY_NAME = new Ordering<AnnotatedOutcome>() {
        @Override public int compare(AnnotatedOutcome a, AnnotatedOutcome b) {
            return a.getName().compareTo(b.getName());
       }
    };

    private final Expectation expectation;
    private final Outcome outcome;
    /** a list of previous outcomes for the same action, sorted in chronological order */
    private final SortedMap<Long, Outcome> previousOutcomes;
    /** will be null if not comparing to a tag */
    private final String tagName;
    private final Outcome tagOutcome;
    private final boolean hasMetadata;

    AnnotatedOutcome(Outcome outcome, Expectation expectation,
            SortedMap<Long, Outcome> previousOutcomes, String tagName, Outcome tagOutcome,
            boolean hasMetadata) {
        if (previousOutcomes == null) {
            throw new NullPointerException();
        }
        this.expectation = expectation;
        this.outcome = outcome;
        this.previousOutcomes = previousOutcomes;
        this.tagName = tagName;
        this.tagOutcome = tagOutcome;
        this.hasMetadata = hasMetadata;
    }

    public Outcome getOutcome() {
        return outcome;
    }

    public String getName() {
        return outcome.getName();
    }

    public ResultValue getResultValue() {
        return outcome.getResultValue(expectation);
    }

    public List<ResultValue> getPreviousResultValues() {
        List<ResultValue> previousResultValues = new ArrayList<ResultValue>();
        for (Outcome previousOutcome : previousOutcomes.values()) {
            previousResultValues.add(previousOutcome.getResultValue(expectation));
        }
        return previousResultValues;
    }

    /**
     * Returns the most recent result value of a run of this test (before the current run).
     */
    public ResultValue getMostRecentResultValue(ResultValue defaultValue) {
        List<ResultValue> previousResultValues = getPreviousResultValues();
        return previousResultValues.isEmpty() ?
                defaultValue :
                previousResultValues.get(previousResultValues.size() - 1);
    }

    public boolean hasTag() {
        return tagOutcome != null;
    }

    public String getTagName() {
        return tagName;
    }

    public ResultValue getTagResultValue() {
        return tagOutcome == null ? null : tagOutcome.getResultValue(expectation);
    }

    /**
     * Returns true if the outcome is noteworthy given the result value and previous history.
     */
    public boolean isNoteworthy() {
        return getResultValue() != ResultValue.OK || recentlyChanged() || changedSinceTag();
    }

    public boolean outcomeChanged() {
        List<Outcome> previousOutcomesList = getOutcomeList();
        return previousOutcomesList.isEmpty()
                || !outcome.equals(previousOutcomesList.get(previousOutcomesList.size() - 1));
    }

    private ArrayList<Outcome> getOutcomeList() {
        return new ArrayList<Outcome>(previousOutcomes.values());
    }

    /**
     * Returns true if the outcome recently changed in result value.
     */
    private boolean recentlyChanged() {
        List<ResultValue> previousResultValues = getPreviousResultValues();
        if (previousResultValues.isEmpty()) {
            return false;
        }
        return previousResultValues.get(previousResultValues.size() - 1) != getResultValue();
    }

    private boolean changedSinceTag() {
        ResultValue tagResultValue = getTagResultValue();
        return tagResultValue != null && tagResultValue != getResultValue();
    }

    /**
     * Returns a Long representing the time the outcome was last run. Returns {@code defaultValue}
     * if the outcome is not known to have run before.
     */
    public Long lastRun(Long defaultValue) {
        if (!hasMetadata) {
            return defaultValue;
        }
        List<Long> runTimes = Lists.newArrayList(previousOutcomes.keySet());
        return runTimes.isEmpty() ? defaultValue : runTimes.get(runTimes.size() - 1);
    }
}