/* Copyright (C) 2013-2022 TU Dortmund
 * This file is part of LearnLib, http://www.learnlib.de/.
 *
 * 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 de.learnlib.oracle.equivalence;

import java.util.Collection;

import de.learnlib.api.oracle.EquivalenceOracle;
import de.learnlib.api.oracle.MembershipOracle;
import de.learnlib.api.oracle.SingleQueryOracle;
import net.automatalib.automata.concepts.SuffixOutput;
import net.automatalib.words.Alphabet;
import net.automatalib.words.Word;
import org.testng.annotations.Test;

/**
 * Abstract class for testing equivalence oracles.
 *
 * @param <A>
 *         hypothesis type
 * @param <I>
 *         input symbol type
 * @param <D>
 *         output domain type
 *
 * @author frohme
 */
public abstract class AbstractEQOracleTest<A extends SuffixOutput<I, D>, I, D> {

    @Test
    public void testGeneratedEQQueries() {
        final SuffixOutput<I, D> output = getHypothesis();
        final EquivalenceOracle<? super A, I, ?> oracle = getOracle(new DummyMQOracle(output));
        oracle.findCounterExample(getHypothesis(), getAlphabet());
    }

    /**
     * The hypothesis passed to the {@link EquivalenceOracle#findCounterExample(Object, Collection)} method.
     *
     * @return the hypothesis
     */
    protected abstract A getHypothesis();

    /**
     * The alphabet passed to the {@link EquivalenceOracle#findCounterExample(Object, Collection)} method.
     *
     * @return the alphabet
     */
    protected abstract Alphabet<I> getAlphabet();

    /**
     * The check method to be invoked for every query generated by the tested equivalence oracle.
     */
    protected abstract void checkGeneratedQuery(Word<I> query);

    /**
     * Method that retrieves the equivalence oracle instance to test.
     *
     * @param mOracle
     *         the (mocked) membership oracle which does not answer any queries.
     *
     * @return the equivalence oracle to test
     */
    protected abstract EquivalenceOracle<? super A, I, D> getOracle(MembershipOracle<I, D> mOracle);

    private class DummyMQOracle implements SingleQueryOracle<I, D> {

        private final SuffixOutput<I, D> output;

        DummyMQOracle(SuffixOutput<I, D> output) {
            this.output = output;
        }

        @Override
        public D answerQuery(Word<I> prefix, Word<I> suffix) {
            checkGeneratedQuery(prefix.concat(suffix));
            // return hypothesis output, so we don't find an actual counterexample and continue searching
            return output.computeSuffixOutput(prefix, suffix);
        }
    }

}
