package org.commcare.suite.model;

import org.commcare.util.DatumUtil;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.util.externalizable.DeserializationException;
import org.javarosa.core.util.externalizable.ExtUtil;
import org.javarosa.core.util.externalizable.ExtWrapTagged;
import org.javarosa.core.util.externalizable.PrototypeFactory;
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathPathExpr;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * Data class for list query data elements
 *
 * <pre>{@code
 * <data
 *   key="case_id_list">
 *   nodeset="instance('selected-cases')/session-data/value"
 *   exclude="count(instance('casedb')/casedb/case[@case_id = current()/.]) = 1"
 *   ref="."
 * />
 * }</pre>
 *
 * The {@code exclude} attribute is optional.
 */
public class ListQueryData implements QueryData {
    private String key;
    private TreeReference nodeset;
    private XPathExpression excludeExpr;
    private XPathPathExpr ref;

    @SuppressWarnings("unused")
    public ListQueryData() {}

    public ListQueryData(String key, TreeReference nodeset, XPathExpression excludeExpr, XPathPathExpr ref) {
        this.key = key;
        this.nodeset = nodeset;
        this.excludeExpr = excludeExpr;
        this.ref = ref;
    }

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public Iterable<String> getValues(EvaluationContext ec) {
        List<String> values = new ArrayList<String>();

        Vector<TreeReference> result = ec.expandReference(nodeset);

        for (TreeReference node : result) {
            EvaluationContext temp = new EvaluationContext(ec, node);
            if (excludeExpr == null || !(boolean) excludeExpr.eval(temp)) {
                values.add(DatumUtil.getReturnValueFromSelection(node, ref, ec));
            }
        }
        return values;
    }

    @Override
    public void readExternal(DataInputStream in, PrototypeFactory pf)
            throws IOException, DeserializationException {
        key = ExtUtil.readString(in);
        nodeset = (TreeReference)ExtUtil.read(in, TreeReference.class, pf);
        excludeExpr = (XPathExpression) ExtUtil.read(in, new ExtWrapTagged(), pf);
        ref = (XPathPathExpr) ExtUtil.read(in, new ExtWrapTagged(), pf);
    }

    @Override
    public void writeExternal(DataOutputStream out) throws IOException {
        ExtUtil.writeString(out, key);
        ExtUtil.write(out, nodeset);
        ExtUtil.write(out, new ExtWrapTagged(excludeExpr));
        ExtUtil.write(out, new ExtWrapTagged(ref));
    }
}

@@ -2364,38 +2364,28 @@
     
-excludeExpr = (XPathExpression
+ref = (XPathPathExpr
 ) Ex
@@ -2433,36 +2433,176 @@
         
-ref = (XPath
+boolean excludeIsNull = ExtUtil.readBool(in);%0A        if (excludeIsNull) %7B%0A            excludeExpr = null;%0A        %7D else %7B%0A            excludeExpr = (X
 PathExpr
 ) ExtUti
@@ -2585,32 +2585,38 @@
 xpr = (XPathExpr
+ession
 ) ExtUtil.read(i
@@ -2640,24 +2640,34 @@
 ged(), pf);%0A
+        %7D%0A
     %7D%0A%0A    @
@@ -2869,16 +2869,55 @@
 pTagged(
+ref));%0A        boolean excludeIsNull = 
 excludeE
@@ -2919,18 +2919,24 @@
 ludeExpr
-))
+ == null
 ;%0A      
@@ -2956,38 +2956,29 @@
 te(out, 
-new ExtWrapTagged(ref)
+excludeIsNull
 );%0A    %7D

check for null