/*
 * 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.sparql.algebra;

import org.apache.jena.atlas.junit.BaseTest ;
import org.apache.jena.atlas.lib.StrUtils ;
import org.apache.jena.query.Query ;
import org.apache.jena.query.QueryFactory ;
import org.apache.jena.sparql.algebra.Algebra ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.sse.SSE ;
import org.junit.Test ;

//Tests for conversion of algebra forms to quad form. 
public class TestTransformQuads extends BaseTest
{
    // Simple
    @Test public void quads01() { test ("{ GRAPH ?g { ?s ?p ?o } }", 
                                        "(quadpattern (quad ?g ?s ?p ?o))" 
                                        ) ; }
    // Not nested
    @Test public void quads02() { test ("{ GRAPH ?g { ?s ?p ?o } GRAPH ?g1 { ?s1 ?p1 ?o1 }  }", 
                                        "(sequence" +
                                        "    (quadpattern (quad ?g ?s ?p ?o))",
                                        "    (quadpattern (quad ?g1 ?s1 ?p1 ?o1)))"
                                       ) ; }
    
    @Test public void quads03() { test ("{ GRAPH ?g { ?s ?p ?o } GRAPH ?g { ?s1 ?p1 ?o1 }  }",
                                        "(sequence" +
                                        "   (quadpattern (quad ?g ?s ?p ?o))" +
                                        "   (quadpattern (quad ?g ?s1 ?p1 ?o1)))"
                                        ) ; }
    // Nested
    @Test public void quads04() { test ("{ GRAPH ?g { ?s ?p ?o GRAPH ?g1 { ?s1 ?p1 ?o1 }  } }",
                                        "(sequence" +
                                        "   (quadpattern (quad ?g ?s ?p ?o))" +
                                        "   (quadpattern (quad ?g1 ?s1 ?p1 ?o1)))"
                                        ) ; }
    
    @Test public void quads05() { test ("{ GRAPH ?g { ?s ?p ?o GRAPH ?g { ?s1 ?p1 ?o1 }  } }", 
                                        "(assign ((?g ?*g0))" +
                                        "   (sequence" +
                                        "     (quadpattern (quad ?*g0 ?s ?p ?o))" +
                                        "     (quadpattern (quad ?g ?s1 ?p1 ?o1))))" 
                                        ) ; }
    // Filters
    @Test public void quads10() { test ("{ GRAPH ?g { ?s ?p ?o FILTER (str(?g) = 'graphURI') } }", 
                                        "(assign ((?g ?*g0))" +
                                        "   (filter (= (str ?g) 'graphURI')" +
                                        "     (quadpattern (quad ?*g0 ?s ?p ?o))))" 
                                        ) ; }
    
    @Test public void quads11() { test ("{ GRAPH ?g { ?s ?p ?o } FILTER (str(?g) = 'graphURI') }",
                                        "(filter (= (str ?g) 'graphURI')" +
                                        "   (quadpattern (quad ?g ?s ?p ?o)))"
                                        ) ; }
    
    // Nested and filter
    // ?g is unbound in the filter. 
    @Test public void quads20() { test ("{ GRAPH ?g { ?s ?p ?o GRAPH ?g1 { ?s1 ?p1 ?o1 FILTER (str(?g) = 'graphURI') } } }",
                                        "(assign ((?g ?*g0))" +
                                        "   (join" +
                                        "     (quadpattern (quad ?*g0 ?s ?p ?o))" +
                                        "     (filter (= (str ?g) 'graphURI')" +
                                        "       (quadpattern (quad ?g1 ?s1 ?p1 ?o1)))))"
                                        ) ; }
    
    @Test public void quads21() { test ("{ GRAPH ?g { ?s ?p ?o GRAPH ?g1 { ?s1 ?p1 ?o1 FILTER (str(?g1) = 'graphURI') } } }",
                                        "(sequence" +
                                        "   (quadpattern (quad ?g ?s ?p ?o))" +
                                        "   (assign ((?g1 ?*g0))" +
                                        "     (filter (= (str ?g1) 'graphURI')" +
                                        "       (quadpattern (quad ?*g0 ?s1 ?p1 ?o1)))))"
                                       ) ; }
    
    // Tricky pattern ... twice.
    @Test public void quads30() { test ( "{ GRAPH ?g { ?s ?p ?o FILTER (str(?g) = 'graphURI') } " +
                                         "  GRAPH ?g { ?s ?p ?o FILTER (str(?g) = 'graphURI') } }",
                                         "(sequence" +
                                         "   (assign ((?g ?*g0))" +
                                         "     (filter (= (str ?g) 'graphURI')" +
                                         "       (quadpattern (quad ?*g0 ?s ?p ?o))))" +
                                         "   (assign ((?g ?*g1))" +
                                         "     (filter (= (str ?g) 'graphURI')" +
                                         "       (quadpattern (quad ?*g1 ?s ?p ?o)))))"
                                         ) ; }

    // NOT EXISTS
    @Test public void quads31() { test ( "{ GRAPH ?g { ?s ?p ?o FILTER NOT EXISTS { GRAPH ?g1 { ?s1 ?p ?o1 } } } }",
                                         "(filter (notexists",
                                         "   (quadpattern (quad ?g1 ?s1 ?p ?o1)))",
                                         "  (quadpattern (quad ?g ?s ?p ?o)))"
                                         ) ; }

    // NOT EXISTS
    @Test public void quads32() { test ( "{ ?s ?p ?o FILTER NOT EXISTS { GRAPH ?g1 { ?s1 ?p ?o1 } } }",
                                         "(filter (notexists",
                                         "   (quadpattern (quad ?g1 ?s1 ?p ?o1)))",
                                         "  (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o)))"
                                         ) ; }
    
    @Test public void quads33() { test ( "{ GRAPH ?g { { SELECT ?x WHERE { ?x ?p ?g } } } }",
                                          "(project (?x)",
                                          "  (quadpattern (quad ?g ?x ?/p ?/g)))") ; }

    // JENA-535
    @Test public void quads34() { test ( "{ ?s ?p ?o OPTIONAL { FILTER NOT EXISTS { ?x ?y ?z } } }",
                                         "(conditional",
                                         "  (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o))",
                                         "  (filter (notexists",
                                         "             (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?x ?y ?z)))",
                                         "    (table unit)))") ; }
    
    // NOT EXISTS in left join expression. 
    @Test public void quads35() { test ( "{ ?s ?p ?o OPTIONAL { FILTER NOT EXISTS { ?x ?y ?z } } }",
                                         false,
                                         "(leftjoin",
                                         "   (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o))",
                                         "   (table unit)",
                                         "   (notexists",
                                         "     (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?x ?y ?z))))") ; }
        
    // NOT EXISTS in left join expression. 
    @Test public void quads36() { test ( "{ ?s ?p ?o OPTIONAL { FILTER NOT EXISTS { GRAPH ?g { ?x ?y ?z } } } }",
                                         false,
                                         "(leftjoin",
                                         "   (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o))",
                                         "   (table unit)",
                                         "   (notexists",
                                         "     (quadpattern (?g ?x ?y ?z))))") ; }
    
    // NOT EXISTS in BIND 
    @Test public void quads37() { test ( "{ BIND ( true && NOT EXISTS { GRAPH ?g { ?x ?y ?z } } AS ?X ) }",
                                         "(extend ((?X (&& true (notexists",
                                         "                         (quadpattern (quad ?g ?x ?y ?z))))))",
                                         "    (table unit))") ; }

    // Don't touch SERVICE 
    @Test public void quads38() { test ("{ {?s ?p ?o } UNION { SERVICE <http://host/endpoint> { GRAPH ?gr { ?sr ?pr ?or }}} }",
                                        "(union",
                                        "  (quadpattern (quad <urn:x-arq:DefaultGraphNode> ?s ?p ?o))",
                                        "  (service <http://host/endpoint>",
                                        "    (graph ?gr",
                                        "      (bgp (triple ?sr ?pr ?or))))",
                                        ")") ; }
    
    // Don't touch SERVICE 
    @Test public void quads39() { test ("{ { GRAPH ?g { ?s ?p ?o } } UNION { SERVICE <http://host/endpoint> { GRAPH ?gr { ?sr ?pr ?or }}} }",
                                        "(union",
                                        "  (quadpattern (?g ?s ?p ?o))",
                                        "  (service <http://host/endpoint>",
                                        "    (graph ?gr",
                                        "      (bgp (triple ?sr ?pr ?or))))",
                                        ")") ; }
    
    // Don't touch SERVICE 
    @Test public void quads40() { test ("{ GRAPH ?g { SERVICE <http://host/endpoint> { ?s ?p ?o }}}",
                                        "(service <http://host/endpoint> (bgp (triple ?s ?p ?o)))") ;
                                }

    // Don't touch SERVICE 
    @Test public void quads41() { test ("{ GRAPH ?g1 { SERVICE <http://host/endpoint> { ?s ?p ?o } ?s1 ?p1 ?o1 } }",
                                        "(sequence",
                                        "   (service <http://host/endpoint> (bgp (triple ?s ?p ?o)))",
                                        "   (quadpattern (?g1 ?s1 ?p1 ?o1))",
                                        ")") ;
                                }



    private static void test(String patternString, String... strExpected) {
        test(patternString, true, strExpected) ;
    }
    
    
    private static void test(String patternString, boolean optimize, String... strExpected)
    {
        Query q = QueryFactory.create("SELECT * WHERE "+patternString) ;
        Op op = Algebra.compile(q) ;
        if ( optimize )
            op = Algebra.optimize(op) ;
        op = Algebra.toQuadForm(op) ;
        
        Op op2 = SSE.parseOp(StrUtils.strjoinNL(strExpected)) ;
        assertEquals(op2, op) ;
    }
}

