/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 org.apache.jena.arq.querybuilder.handlers;

import static org.junit.Assert.assertEquals;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.jena.arq.querybuilder.handlers.SolutionModifierHandler;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
import org.apache.jena.query.SortCondition;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.expr.E_Random;
import org.apache.jena.sparql.lang.sparql_11.ParseException;
import org.junit.Before;
import org.junit.Test;

public class SolutionModifierHandlerTest extends AbstractHandlerTest {

	private Query query;
	private SolutionModifierHandler solutionModifier;

	@Before
	public void setup() {
		query = new Query();
		solutionModifier = new SolutionModifierHandler(query);
	}

	@Test
	public void testAddAll() throws ParseException {
		SolutionModifierHandler solutionModifier2 = new SolutionModifierHandler(new Query());
		solutionModifier2.addOrderBy(Var.alloc("orderBy"));
		solutionModifier2.addGroupBy(Var.alloc("groupBy"));
		solutionModifier2.addHaving("?having<10");
		solutionModifier2.setLimit(500);
		solutionModifier2.setOffset(200);

		solutionModifier.addAll(solutionModifier2);

		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("groupBy"), s);
		assertContainsRegex(HAVING + OPEN_PAREN + var("having") + OPT_SPACE + LT + OPT_SPACE + "10" + CLOSE_PAREN, s);
		assertContainsRegex(ORDER_BY + var("orderBy"), s);
		assertContainsRegex(LIMIT + "500", s);
		assertContainsRegex(OFFSET + "200", s);
	}

	@Test
	public void testAll() throws ParseException {
		solutionModifier.addOrderBy(Var.alloc("orderBy"));
		solutionModifier.addGroupBy(Var.alloc("groupBy"));
		solutionModifier.addHaving("SUM(?lprice) > 10");
		solutionModifier.setLimit(500);
		solutionModifier.setOffset(200);

		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("groupBy"), s);
		assertContainsRegex(HAVING + OPEN_PAREN + "SUM" + OPEN_PAREN + var("lprice") + CLOSE_PAREN + OPT_SPACE + GT
				+ "10" + CLOSE_PAREN, s);
		assertContainsRegex(ORDER_BY + var("orderBy"), s);
		assertContainsRegex(LIMIT + "500", s);
		assertContainsRegex(OFFSET + "200", s);

	}

	@Test
	public void testAddOrderBy() {
		solutionModifier.addOrderBy(Var.alloc("orderBy"));
		List<SortCondition> sc = query.getOrderBy();
		assertEquals("Wrong number of conditions", 1, sc.size());
		assertEquals("Wrong value", sc.get(0).expression.asVar(), Var.alloc("orderBy"));

		solutionModifier.addOrderBy(Var.alloc("orderBy2"));
		sc = query.getOrderBy();
		assertEquals("Wrong number of conditions", 2, sc.size());
		assertEquals("Wrong value", sc.get(0).expression.asVar(), Var.alloc("orderBy"));
		assertEquals("Wrong value", sc.get(1).expression.asVar(), Var.alloc("orderBy2"));
	}

	@Test
	public void testAddGroupByVar() {
		solutionModifier.addGroupBy(Var.alloc("groupBy"));
		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("groupBy"), s);

		solutionModifier.addGroupBy(Var.alloc("groupBy2"));
		s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("groupBy") + SPACE + var("groupBy2"), s);
	}

	@Test
	public void testAddGroupByExpr() {
		solutionModifier.addGroupBy(new E_Random());
		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + "rand" + OPEN_PAREN + CLOSE_PAREN, s);

		solutionModifier.addGroupBy(Var.alloc("groupBy2"));
		s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + "rand" + OPEN_PAREN + CLOSE_PAREN + SPACE + var("groupBy2"), s);
	}

	@Test
	public void testAddGroupByVarAndExpr() {
		solutionModifier.addGroupBy(Var.alloc("groupBy"), new E_Random());
		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + OPEN_PAREN + "rand" + OPEN_PAREN + CLOSE_PAREN + SPACE + "AS" + SPACE
				+ var("groupBy") + CLOSE_PAREN, s);

		solutionModifier.addGroupBy(Var.alloc("groupBy2"));
		s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + OPEN_PAREN + "rand" + OPEN_PAREN + CLOSE_PAREN + SPACE + "AS" + SPACE
				+ var("groupBy") + CLOSE_PAREN + SPACE + var("groupBy2"), s);
	}

	@Test
	public void testAddHavingString() throws ParseException {
		solutionModifier.addHaving("?having<10");
		assertContainsRegex(HAVING + OPEN_PAREN + var("having") + OPT_SPACE + LT + 10 + CLOSE_PAREN, query.toString());

		solutionModifier.addHaving("?having2");
		assertContainsRegex(
				HAVING + OPEN_PAREN + var("having") + OPT_SPACE + LT + 10 + CLOSE_PAREN + OPT_SPACE + var("having2"),
				query.toString());
	}

	@Test
	public void testAddHavingVar() throws ParseException {
		solutionModifier.addHaving(Var.alloc("foo"));
		assertContainsRegex(HAVING + var("foo"), query.toString());

		solutionModifier.addHaving("?having2");
		assertContainsRegex(HAVING + var("foo") + SPACE + var("having2"), query.toString());
	}

	@Test
	public void testAddHavingExpr() throws ParseException {
		solutionModifier.addHaving(new E_Random());
		assertContainsRegex(HAVING + "rand" + OPEN_PAREN + CLOSE_PAREN, query.toString());

		solutionModifier.addHaving("?having2");
		assertContainsRegex(HAVING + "rand" + OPEN_PAREN + CLOSE_PAREN + SPACE + var("having2"), query.toString());
	}

	@Test
	public void testSetLimit() {
		solutionModifier.setLimit(500);
		String[] s = byLine(query.toString());
		assertContainsRegex("LIMIT\\s+500", s);

		solutionModifier.setLimit(200);
		s = byLine(query.toString());
		assertContainsRegex("LIMIT\\s+200", s);

		solutionModifier.setLimit(-1);
		s = byLine(query.toString());
		assertNotContainsRegex("LIMIT.*", s);

	}

	@Test
	public void testSetOffset() {
		solutionModifier.setOffset(500);
		String[] s = byLine(query.toString());
		assertContainsRegex("OFFSET\\s+500", s);

		solutionModifier.setOffset(200);
		s = byLine(query.toString());
		assertContainsRegex("OFFSET\\s+200", s);

		solutionModifier.setOffset(-1);
		s = byLine(query.toString());
		assertNotContainsRegex("OFFSET.*", s);
	}

	@Test
	public void testSetVarsGroupBy() {
		Var v = Var.alloc("v");
		solutionModifier.addGroupBy(v);

		String[] s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("v"), s);

		Map<Var, Node> values = new HashMap<>();
		values.put(v, Var.alloc("v2"));
		solutionModifier.setVars(values);
		s = byLine(query.toString());
		assertContainsRegex(GROUP_BY + var("v2"), s);
	}

	@Test
	public void testSetVarsHaving() {
		Var v = Var.alloc("v");
		solutionModifier.addHaving(v);

		String[] s = byLine(query.toString());
		assertContainsRegex(HAVING + var("v"), s);

		Map<Var, Node> values = new HashMap<>();
		values.put(v, Var.alloc("v2"));
		solutionModifier.setVars(values);
		s = byLine(query.toString());
		assertContainsRegex(HAVING + var("v2"), s);
	}

	@Test
	public void testSetVarsOrderBy() {
		Var v = Var.alloc("v");
		solutionModifier.addOrderBy(v);

		String[] s = byLine(query.toString());
		assertContainsRegex(ORDER_BY + var("v"), s);

		Map<Var, Node> values = new HashMap<>();
		values.put(v, Var.alloc("v2"));
		solutionModifier.setVars(values);
		s = byLine(query.toString());
		assertContainsRegex(ORDER_BY + var("v2"), s);
	}

}
