package it.unibz.inf.ontop.owlapi;

/*
 * #%L
 * ontop-quest-owlapi
 * %%
 * Copyright (C) 2009 - 2014 Free University of Bozen-Bolzano
 * %%
 * 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.
 * #L%
 */

import com.google.common.collect.ImmutableList;
import it.unibz.inf.ontop.exception.OntopReformulationException;
import it.unibz.inf.ontop.owlapi.exception.OntopOWLException;
import org.junit.*;

/***
 * A simple test that check if the system is able to handle Mappings for
 * classes/roles and attributes even if there are no URI templates. i.e., the
 * database stores URI's directly.
 * 
 * We are going to create an H2 DB, the .sql file is fixed. We will map directly
 * there and then query on top.
 *
 * Class to test if bind in SPARQL is working properly.
 *
 * It uses the test from http://www.w3.org/TR/sparql11-query/#bind
 */

public class BindTest extends AbstractOWLAPITest {

    @BeforeClass
    public static void setUp() throws Exception {
        initOBDA("/test/bind/sparqlBind-create-h2.sql",
                "/test/bind/sparqlBind.obda",
                "/test/bind/sparqlBind.owl");
	}

    @AfterClass
    public static void tearDown() throws Exception {
        release();
    }

    @Test
    public void testSelectSimple() throws Exception {
        String querySelect = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title (17.25 AS ?price)\n" +
                "{ ?x ns:price ?p .\n" +
                "  ?x dc:title ?title . \n" +
                "  ?x ns:discount ?discount . \n" +
                "}";

        checkReturnedValues(querySelect, "price", ImmutableList.of(
                "\"17.25\"^^xsd:decimal",
                "\"17.25\"^^xsd:decimal"));
    }

    @Test
    public void testSelectComplex() throws Exception {
        String querySelect = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title (?p*(1-?discount) AS ?price)\n" +
                "{ ?x ns:price ?p .\n" +
                "  ?x dc:title ?title . \n" +
                "  ?x ns:discount ?discount . \n" +
                "}";

        checkReturnedValues(querySelect, "price", ImmutableList.of(
                "\"33.600000000000\"^^xsd:decimal",
                "\"17.250000000000\"^^xsd:decimal"));
    }

    @Test
    public void testBind() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount\n"
                + "   BIND (?p*(1-?discount) AS ?w)\n"
                + "   FILTER(?w < 20)\n"
                + "   ?x dc:title ?title .\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"17.250000000000\"^^xsd:decimal"));
    }

    @Test
    public void testBindWithStrlen() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT ?x ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount\n"
                + "   BIND (?p*(1-?discount) AS ?w)\n"
                + "   FILTER(?w < 20 && strlen(?title) > 0) \n"
                + "   ?x dc:title ?title .\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"17.250000000000\"^^xsd:decimal"));
    }

    @Test
    public void testDoubleBind() throws Exception{
        
		// double bind
        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title  ?w WHERE \n" +
                "{  ?x ns:discount ?discount .\n" +
                "   ?x dc:title ?title .\n" +
                "   ?x ns:price ?p .\n" +
                "   BIND (?p AS ?fullPrice) \n" +
                "   BIND (?fullPrice  - ?discount AS ?w) \n" +
                "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"41.800000\"^^xsd:decimal",
                "\"22.750000\"^^xsd:decimal"));
    }


    @Test
    public void testBindWithSelect() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title  (?fullPrice * ?discount AS ?w) WHERE \n" +
                "{  ?x ns:discount ?discount .\n" +
                "   ?x dc:title ?title .\n" +
                "  ?x ns:price ?p .\n" +
                "   BIND (?p AS ?fullPrice) \n" +
                "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"8.40000000\"^^xsd:decimal",
                "\"5.75000000\"^^xsd:decimal"));
    }

    @Test
    public void testSelect2()  throws Exception {
        String querySelect = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title (?p * (1-?discount) AS ?customerPrice)\n" +
                "{ ?x ns:price ?p .\n" +
                "   ?x dc:title ?title . \n" +
                "   ?x ns:discount ?discount \n" +
                "}";

        checkReturnedValues(querySelect, "customerPrice", ImmutableList.of(
                "\"33.600000000000\"^^xsd:decimal",
                "\"17.250000000000\"^^xsd:decimal"));
    }

    @Test//(expected = OntopOWLException.class)
    public void testFailingSelect1()  throws Exception {

        String querySelect = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title (?p -?discount AS ?price)\n" +
                "{ ?x ns:price ?p .\n" +
                "  ?x dc:title ?title . \n" +
                "  ?x ns:discount ?discount . \n" +
                "  FILTER(?price < 20) \n" +
                "}";
        checkReturnedValues(querySelect, "price", ImmutableList.of());
    }

    @Test
    public void testFailingBind() throws Exception {

        String queryBind1 = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n" +
                "SELECT  ?title  (?fullPrice * (1- ?discount) AS ?customerPrice) WHERE \n" +
                "{  ?x ns:discount ?discount .\n" +
                "   ?x ns:price ?p .\n" +
                "   ?x dc:title ?title .\n" +
                "   BIND (?p AS ?fullPrice) \n" +
                "}";

        checkReturnedValues(queryBind1, "customerPrice", ImmutableList.of(
                "\"33.600000000000\"^^xsd:decimal",
                "\"17.250000000000\"^^xsd:decimal"));
    }

    @Test
    public void testBindWithConcat() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   BIND (CONCAT(?title, \" title\") AS ?w)\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"SPARQL Tutorial title\"^^xsd:string",
                "\"The Semantic Web title\"^^xsd:string"));
    }

    @Test
    public void testBindWithUCase() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   BIND (UCASE(?title) AS ?w)\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"SPARQL TUTORIAL\"@en",
                "\"THE SEMANTIC WEB\"@en"));
    }

    @Test
    public void testBindWithConcatStrLen() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   BIND (STRLEN(CONCAT(?title, \" \")) AS ?v)\n"
                + "   BIND (CONCAT(?title, \" \", str(?v)) AS ?w)\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"SPARQL Tutorial 16\"^^xsd:string",
                "\"The Semantic Web 17\"^^xsd:string"));
    }

    @Test
    public void testBindWithConcatLanguage() throws Exception {

		String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   BIND (CONCAT(?title, ?title) AS ?w)\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"SPARQL TutorialSPARQL Tutorial\"@en",
                "\"The Semantic WebThe Semantic Web\"@en"));
    }

    @Test
    public void testBindConcatUnderUnion() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?w WHERE \n"
                + "{"
                + "   { BIND (\"plop\" AS ?w) }\n"
                + "   UNION\n"
                + "   { \n"
                + "     ?x ns:price ?p .\n" +
                "       ?x ns:discount ?discount .\n"
                + "    ?x dc:title ?title .\n"
                + "    BIND (CONCAT(?title, ?title) AS ?w) }\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"plop\"^^xsd:string",
                "\"SPARQL TutorialSPARQL Tutorial\"@en",
                "\"The Semantic WebThe Semantic Web\"@en"));
    }

    @Test
    public void testBindConcatAboveUnion() throws Exception {

        String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?w WHERE \n"
                + "{"
                + "   ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   { BIND (\"plop\" AS ?s) }\n"
                + "   UNION\n"
                + "   { \n"
                + "    ?y dc:title ?s .\n"
                + "   }\n"
                + "   BIND (CONCAT(?title, ?s) AS ?w)"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"SPARQL Tutorialplop\"^^xsd:string",
                "\"SPARQL TutorialSPARQL Tutorial\"@en",
                "\"SPARQL TutorialThe Semantic Web\"@en",
                "\"The Semantic Webplop\"^^xsd:string",
                "\"The Semantic WebSPARQL Tutorial\"@en",
                "\"The Semantic WebThe Semantic Web\"@en"));
    }

    @Test
    public void testBindWithConcatLanguageInDatabase() throws Exception {

		String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "SELECT  ?title ?w WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount .\n"
                + "   ?x dc:title ?title .\n"
                + "   ?x dc:description ?desc .\n"
                + "   BIND (CONCAT(?desc, ?title) AS ?w)\n"
                + "}";

        checkReturnedValues(queryBind, "w", ImmutableList.of(
                "\"goodSPARQL Tutorial\"@en",
                "\"badThe Semantic Web\"@en"));
    }


    //CAST function not supported
    //The builtin function http://www.w3.org/2001/XMLSchema#string is not supported yet!
    @Test(expected = OntopReformulationException.class)
    public void testBindWithCast() throws Throwable {

		String queryBind = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n"
                + "PREFIX  ns:  <http://example.org/ns#>\n"
                + "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> \n"
                + "SELECT  ?title ?price WHERE \n"
                + "{  ?x ns:price ?p .\n"
                + "   ?x ns:discount ?discount\n"
                + "   BIND (CONCAT(xsd:string(?p*(1-?discount)), xsd:string(?p)) AS ?price)\n"
                + "   ?x dc:title ?title .\n"
                + "}";

        try {
            checkReturnedValues(queryBind, "w", ImmutableList.of());
        }
        catch (OntopOWLException e) {
            throw e.getCause();
        }
    }

    @Test
    public void testBindLangTag() throws Exception {
        String query = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "SELECT ?w\n" +
                "WHERE {\n" +
                "  ?y dc:title ?title .\n" +
                "  BIND( lang(?title) AS ?w ) .\n" +
                "}";

        checkReturnedValues(query, "w", ImmutableList.of(
                "\"en\"^^xsd:string",
                "\"en\"^^xsd:string"));
    }

    @Test
    public void testBindLangTag2() throws Exception {
        String query = "PREFIX  dc:  <http://purl.org/dc/elements/1.1/>\n" +
                "SELECT ?w\n" +
                "WHERE {\n" +
                "  ?y dc:title ?title .\n" +
                "  BIND (str(?title) AS ?s)\n" +
                "  BIND( lang(?s) AS ?w ) .\n" +
                "}";

        checkReturnedValues(query, "w", ImmutableList.of(
                "\"\"^^xsd:string",
                "\"\"^^xsd:string"));
    }
}
