/*
 * Copyright 2013 Charles University in Prague
 * Copyright 2013 Vojtech Horky
 * 
 * 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 cz.cuni.mff.d3s.spl.core.impl.formula;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import cz.cuni.mff.d3s.spl.core.Data;
import cz.cuni.mff.d3s.spl.core.Formula;
import cz.cuni.mff.d3s.spl.core.MathematicalInterpretation;
import cz.cuni.mff.d3s.spl.core.Result;
import cz.cuni.mff.d3s.spl.core.impl.DataForTest;
import cz.cuni.mff.d3s.spl.core.impl.InterpretationForTests;
import cz.cuni.mff.d3s.spl.core.impl.formula.SplFormula.SplParseException;

@RunWith(Parameterized.class)
public class FormulaParserTest {
	protected static final int SAMPLE_COUNT_GOOD = InterpretationForTests.MINIMUM_SAMPLES_REQUIRED * 2;
	protected static final int SAMPLE_COUNT_BAD = InterpretationForTests.MINIMUM_SAMPLES_REQUIRED / 2;
	
	/* We use these constants only for nicer formatting. */
	protected static final Result TRU = Result.TRUE;
	protected static final Result FAL = Result.FALSE;
	protected static final Result UNK = Result.CANNOT_COMPUTE;

	@Parameters(name = "{index}: {0} {1}")
	public static Collection<Object[]> createParameters() {
		return Arrays.asList(new Object[][] {
			{ TRU, "low < high" },
			{ FAL, "high < low" },
			{ UNK, "empty < high" },
			
			{ TRU, "high > low" },
			{ FAL, "low > high" },
			{ UNK, "high > empty" },
			
			{ TRU, "low < medium && medium < high" },
			{ TRU, "low < medium && medium < high" },
			
			/*
			 * Test Kleene logic.
			 * (The combinations are tried in FALSE, UKNOWN, TRUE.)
			 */
			/* AND */
			{ FAL, "  low > medium && medium > high" },
			{ FAL, "  low > medium &&  empty > high" },
			{ FAL, "  low > medium && medium < high" },
			{ FAL, "empty > medium && medium > high" },
			{ UNK, "empty > medium &&  empty > high" },
			{ UNK, "empty > medium && medium < high" },
			{ FAL, "  low < medium && medium > high" },
			{ UNK, "  low < medium &&  empty > high" },
			{ TRU, "  low < medium && medium < high" },
			/* OR */
			{ FAL, "  low > medium || medium > high" },
			{ UNK, "  low > medium ||  empty > high" },
			{ TRU, "  low > medium || medium < high" },
			{ UNK, "empty > medium || medium > high" },
			{ UNK, "empty > medium ||  empty > high" },
			{ TRU, "empty > medium || medium < high" },
			{ TRU, "  low < medium || medium > high" },
			{ TRU, "  low < medium ||  empty > high" },
			{ TRU, "  low < medium || medium < high" },
		});
	}
	
	private final String formulaAsString;
	private final Result expectedResult;
	
	protected MathematicalInterpretation interpretation;
	protected Data low;
	protected Data medium;
	protected Data high;
	protected Data empty;
	
	public FormulaParserTest(final Result result, final String formula) {
		formulaAsString = formula;
		expectedResult = result;
	}
	
	@Before
	public void setupInterpretation() {
		interpretation = new InterpretationForTests();
	}
	
	@Before
	public void setupData() {
		low = new DataForTest(10, SAMPLE_COUNT_GOOD);
		medium = new DataForTest(20, SAMPLE_COUNT_GOOD);
		high = new DataForTest(30, SAMPLE_COUNT_GOOD);
		empty = new DataForTest(0, SAMPLE_COUNT_BAD);
	}
	
	@Test
	public void parse() throws SplParseException {
		Formula formula = SplFormula.create(formulaAsString);
		
		assertNotNull(formula);
		
		/* First, set the proper interpretation. */
		formula.setInterpreation(new InterpretationForTests());

		/* Bind the provided data sources. */
		bindIfPresent(formula, formulaAsString);
		
		/* And evaluate. */
		assertEquals(expectedResult, formula.evaluate());
	}
	
	private void bindIfPresent(Formula parsed, String formula) {
		bindIfPresent(parsed, formula, low, "low");
		bindIfPresent(parsed, formula, medium, "medium");
		bindIfPresent(parsed, formula, high, "high");
		bindIfPresent(parsed, formula, empty, "empty");
	}
	
	private void bindIfPresent(Formula parsed, String formula, Data source, String variable) {
		if (formula.contains(variable)) {
			parsed.bind(variable, source);
		}
	}
}

@@ -2284,16 +2284,295 @@
  %7D,%0A%09%09%09%0A
+%09%09%09%7B TRU, %22(low %3C medium)%22 %7D,%0A%09%09%09%7B TRU, %22(high %3C medium) %7C%7C (medium %3C high)%22 %7D,%0A%09%09%09%7B TRU, %22low %3C high && (low %3E medium %7C%7C high %3E medium)%22 %7D,%0A%09%09%09%0A%09%09%09%7B TRU, %22low %3C medium =%3E low %3C medium%22 %7D,%0A%09%09%09%7B FAL, %22low %3C medium =%3E medium %3C low%22 %7D,%0A%09%09%09%7B TRU, %22medium %3C low =%3E low %3E high%22 %7D,%0A%09%09%09%0A
 %09%09%09/*%0A%09%09

Add parser tests for implication and parenthesis