package org.vertexium.query;

import org.vertexium.Element;
import org.vertexium.Property;

import java.util.Iterator;

import static org.vertexium.util.IterableUtils.count;

public class DefaultGraphQueryIterable<T extends Element> implements
        Iterable<T>,
        IterableWithTotalHits<T> {
    private final QueryParameters parameters;
    private final Iterable<T> iterable;
    private final boolean evaluateQueryString;
    private final boolean evaluateHasContainers;

    public DefaultGraphQueryIterable(QueryParameters parameters, Iterable<T> iterable, boolean evaluateQueryString, boolean evaluateHasContainers) {
        this.parameters = parameters;
        this.iterable = iterable;
        this.evaluateQueryString = evaluateQueryString;
        this.evaluateHasContainers = evaluateHasContainers;
    }

    @Override
    public Iterator<T> iterator() {
        return iterator(false);
    }

    private Iterator<T> iterator(final boolean iterateAll) {
        final Iterator<T> it = iterable.iterator();

        return new Iterator<T>() {
            public T next;
            public T current;
            public long count;

            @Override
            public boolean hasNext() {
                loadNext();
                return next != null;
            }

            @Override
            public T next() {
                loadNext();
                this.current = this.next;
                this.next = null;
                return this.current;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

            private void loadNext() {
                if (this.next != null) {
                    return;
                }

                if (!iterateAll && (this.count >= parameters.getSkip() + parameters.getLimit())) {
                    return;
                }

                while (it.hasNext()) {
                    T elem = it.next();

                    boolean match = true;
                    if (evaluateHasContainers) {
                        for (QueryBase.HasContainer has : parameters.getHasContainers()) {
                            if (!has.isMatch(elem)) {
                                match = false;
                                break;
                            }
                        }
                    }
                    if (!match) {
                        continue;
                    }
                    if (evaluateQueryString
                            && parameters instanceof QueryStringQueryParameters
                            && ((QueryStringQueryParameters) parameters).getQueryString() != null
                            && !evaluateQueryString(elem, ((QueryStringQueryParameters) parameters).getQueryString())
                            ) {
                        continue;
                    }

                    this.count++;
                    if (!iterateAll && (this.count <= parameters.getSkip())) {
                        continue;
                    }

                    this.next = elem;
                    break;
                }
            }
        };
    }

    protected boolean evaluateQueryString(Element elem, String queryString) {
        for (Property property : elem.getProperties()) {
            if (evaluateQueryStringOnValue(property.getValue(), queryString)) {
                return true;
            }
        }
        return false;
    }

    private boolean evaluateQueryStringOnValue(Object value, String queryString) {
        if (value == null) {
            return false;
        }
        if (queryString.equals("*")) {
            return true;
        }
        String valueString = value.toString().toLowerCase();
        return valueString.contains(queryString.toLowerCase());
    }

    @Override
    public long getTotalHits() {
        return count(this.iterator(true));
    }
}

@@ -84,16 +84,89 @@
 operty;%0A
+import org.vertexium.util.CloseableIterable;%0A%0Aimport java.io.IOException;
 %0Aimport 
@@ -363,16 +363,46 @@
 lHits%3CT%3E
+,%0A        CloseableIterable%3CT%3E
  %7B%0A    p
@@ -4032,11 +4032,202 @@
 );%0A    %7D
+%0A%0A    @Override%0A    public void close() throws IOException %7B%0A        if (this.iterable instanceof CloseableIterable) %7B%0A            ((CloseableIterable) this.iterable).close();%0A        %7D%0A    %7D
 %0A%7D%0A

allow DefaultGraphQueryIterable to be closable