/*   Copyright 2006 The Apache Software Foundation
 *
 *   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 xmlcursor.xquery.detailed;

import common.Common;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.junit.Test;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.*;

/** This class tests the use of XmlOptions in binding XQuery variables */
public class XQueryVariableBindingTest extends Common
{
    public static final String XQUERY_CASE_DIR =
        XBEAN_CASE_ROOT + P + "xmlcursor" + P + "xquery";
    public static File dir = new File(XQUERY_CASE_DIR);

    private XmlCursor _testDocCursor1() throws Exception {
        String xml =
            "<elem1>" +
            "<elem11 id=\"123\">text11</elem11>" +
            "<elem21 id=\"456\">text11</elem21>" +
            "<elem12 idRef=\"123\"/>" +
            "<elem13 idRef=\"456\"/>" +
            "<elem14 idRef=\"123\"/>" +
            "<elem15 idRef=\"456\"/>" +
            "<elem16 idRef=\"123\"/>" +
            "<elem17 idRef=\"789\"/>" +
            "</elem1>";
        XmlObject doc = XmlObject.Factory.parse(xml);
        XmlCursor xc = doc.newCursor();
        return xc;
    }

    private void _verifySelection(XmlCursor xc) {
        assertEquals(3, xc.getSelectionCount());
        assertTrue(xc.toNextSelection());
        assertEquals("<elem12 idRef=\"123\"/>", xc.xmlText());
        assertTrue(xc.toNextSelection());
        assertEquals("<elem14 idRef=\"123\"/>", xc.xmlText());
        assertTrue(xc.toNextSelection());
        assertEquals("<elem16 idRef=\"123\"/>", xc.xmlText());
    }

    /** test the automatic binding of $this to the current node: selectPath() */
    @Test
    public void testThisVariable1() throws Exception {
        XmlCursor xc = _testDocCursor1();
        xc.toFirstChild(); //<elem1>
        xc.toFirstChild(); //<elem11>
        xc.selectPath("//*[@idRef=$this/@id]");
        _verifySelection(xc);
        xc.clearSelections();
        xc.dispose();
    }

    // this fails: see JIRA issue XMLBEANS-276
    /** test the binding of a variable to the current node: selectPath() */
    @Test
    public void testCurrentNodeVariable1() throws Exception {
        XmlCursor xc = _testDocCursor1();
        xc.toFirstChild();
        xc.toFirstChild();
        XmlOptions opts = new XmlOptions();
        opts.setXqueryCurrentNodeVar("cur");
        //String varDecl = "declare variable $cur external; ";
        //xc.selectPath(varDecl + "//*[@idRef=$cur/@id]", opts);
        xc.selectPath("//*[@idRef=$cur/@id]", opts);
        _verifySelection(xc);
        xc.clearSelections();
        xc.dispose();
    }

    private XmlCursor _testDocCursor2() throws Exception {
        File f = new File(dir, "employees.xml");
        XmlObject doc = XmlObject.Factory.parse(f);
        return doc.newCursor();
    }

    public void _verifyQueryResult(XmlCursor qc) {
        System.out.println(qc.xmlText());
        assertTrue(qc.toFirstChild());
        assertEquals("<phone location=\"work\">(425)555-5665</phone>", qc.xmlText());
        assertTrue(qc.toNextSibling());
        assertEquals("<phone location=\"work\">(425)555-6897</phone>", qc.xmlText());
        assertFalse(qc.toNextSibling());
    }

    /** test the automatic binding of $this to the current node: execQuery() */
    @Test
    public void testThisVariable2() throws Exception
    {
        XmlCursor xc = _testDocCursor2();
        // xc.toNextToken();
        String q =
            "for $e in $this/employees/employee " +
            "let $s := $e/address/state " +
            "where $s = 'WA' " +
            "return $e//phone[@location='work']";
        XmlCursor qc = xc.execQuery(q);
        _verifyQueryResult(qc);
        xc.dispose();
        qc.dispose();
    }

    /** test the binding of a variable to the current node: execQuery() */
    @Test
    public void testCurrentNodeVariable2() throws Exception {
        XmlCursor xc = _testDocCursor2();
        // xc.toNextToken();
        String q =
            "for $e in $cur/employees/employee " +
            "let $s := $e/address/state " +
            "where $s = 'WA' " +
            "return $e//phone[@location='work']";
        XmlOptions opts = new XmlOptions();
        opts.setXqueryCurrentNodeVar("cur");
        //String varDecl = "declare variable $cur external; ";
        //XmlCursor qc = xc.execQuery(varDecl + q, opts);
        XmlCursor qc = xc.execQuery(q, opts);
        _verifyQueryResult(qc);
        xc.dispose();
        qc.dispose();
    }

    private XmlObject[] _execute(XmlObject xo, Map m, String q) {
        XmlOptions opts = new XmlOptions();
        opts.setXqueryVariables(m);
        return xo.execQuery(q, opts);
    }

    /** test the binding of a variable to an XmlTokenSource using a map */
    @Test
    public void testOneVariable() throws Exception {
        File f = new File(dir, "bookstore.xml");
        XmlObject doc = XmlObject.Factory.parse(f);
        String q =
            "declare variable $rt external; " +
            "for $x in $rt/book " +
            "where $x/price > 30 " +
            "return $x/title";
        Map<String,Object> m = new HashMap<>();
        m.put("rt", doc.selectChildren("", "bookstore")[0]);
        XmlObject[] results = _execute(doc, m, q);
        assertNotNull(results);
        assertEquals(2, results.length);
        assertEquals("<title lang=\"en\">XQuery Kick Start</title>", results[0].xmlText());
        assertEquals("<title lang=\"en\">Learning XML</title>", results[1].xmlText());
    }

    /** test the binding of multiple variables using a map;
        at the same time, test the binding of a variable to a String
     */
    @Test
    public void testMultipleVariables() throws Exception {
        File f = new File(dir, "bookstore.xml");
        XmlObject doc = XmlObject.Factory.parse(f);
        String q =
            "declare variable $rt external; " +
            "declare variable $c external; " +
            "for $x in $rt/book " +
            "where $x[@category=$c] " +
            "return $x/title";
        Map<String,Object> m = new HashMap<>();
        m.put("rt", doc.selectChildren("", "bookstore")[0]);
        m.put("c", "CHILDREN");
        XmlObject[] results = _execute(doc, m, q);
        assertNotNull(results);
        assertEquals(1, results.length);
        assertEquals("<title lang=\"en\">Harry Potter</title>", results[0].xmlText());
    }

}
