package com.mmnaseri.utils.spring.data.query.impl;

import com.mmnaseri.utils.spring.data.domain.*;
import com.mmnaseri.utils.spring.data.proxy.RepositoryFactoryConfiguration;
import com.mmnaseri.utils.spring.data.query.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.util.List;

/**
 * This is a mutable query descriptor that you can use to describe what a query does.
 *
 * @author Milad Naseri (mmnaseri@programmer.net)
 * @since 1.0 (9/20/15)
 */
public class DefaultQueryDescriptor implements QueryDescriptor {

    private static final Log log = LogFactory.getLog(InvocationMatcher.class);

    private final boolean distinct;
    private final String function;
    private final int limit;
    private final PageParameterExtractor pageExtractor;
    private final SortParameterExtractor sortExtractor;
    private final List<List<Parameter>> branches;
    private final RepositoryFactoryConfiguration configuration;
    private final RepositoryMetadata repositoryMetadata;

    public DefaultQueryDescriptor(boolean distinct, String function, int limit, PageParameterExtractor pageExtractor, SortParameterExtractor sortExtractor, List<List<Parameter>> branches, RepositoryFactoryConfiguration configuration, RepositoryMetadata repositoryMetadata) {
        this.distinct = distinct;
        this.function = function;
        this.limit = limit;
        this.pageExtractor = pageExtractor;
        this.sortExtractor = sortExtractor;
        this.branches = branches;
        this.configuration = configuration;
        this.repositoryMetadata = repositoryMetadata;
    }

    @Override
    public RepositoryFactoryConfiguration getConfiguration() {
        return configuration;
    }

    @Override
    public RepositoryMetadata getRepositoryMetadata() {
        return repositoryMetadata;
    }

    @Override
    public boolean isDistinct() {
        return distinct;
    }

    @Override
    public String getFunction() {
        return function;
    }

    @Override
    public int getLimit() {
        return limit;
    }

    @Override
    public Page getPage(Invocation invocation) {
        return pageExtractor == null ? null : pageExtractor.extract(invocation);
    }

    @Override
    public Sort getSort(Invocation invocation) {
        return sortExtractor == null ? null : sortExtractor.extract(invocation);
    }

    @Override
    public List<List<Parameter>> getBranches() {
        return branches;
    }

    @Override
    public boolean matches(Object entity, Invocation invocation) {
        log.info("Matching " + entity + " against " + invocation);
        final List<List<Parameter>> branches = getBranches();
        if (branches.isEmpty()) {
            return entity != null;
        }
        final BeanWrapper wrapper = new BeanWrapperImpl(entity);
        for (List<Parameter> branch : branches) {
            boolean matches = true;
            for (Parameter parameter : branch) {
                final Object value = wrapper.getPropertyValue(parameter.getPath());
                final Operator operator = parameter.getOperator();
                final Object[] properties = new Object[operator.getOperands()];
                for (int i = 0; i < operator.getOperands(); i++) {
                    properties[i] = invocation.getArguments()[parameter.getIndices()[i]];
                }
                if (!operator.getMatcher().matches(parameter, value, properties)) {
                    matches = false;
                    break;
                }
            }
            if (matches) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {
        return (getFunction() != null ? getFunction() + " " : "") + (isDistinct() ? "distinct " : "") + getBranches();
    }

}

@@ -212,24 +212,81 @@
 ta.query.*;%0A
+import com.mmnaseri.utils.spring.data.tools.StringUtils;%0A
 import org.a
@@ -3147,69 +3147,347 @@
 nal 
-Object value = wrapper.getPropertyValue(parameter.getPath());
+boolean ignoreCase;%0A                ignoreCase = parameter.getModifiers().contains(Modifier.IGNORE_CASE);%0A                Object value = wrapper.getPropertyValue(parameter.getPath());%0A                if (ignoreCase && value != null && value instanceof String) %7B%0A                    value = ((String) value).toLowerCase();%0A                %7D
 %0A   
@@ -3787,16 +3787,216 @@
 ()%5Bi%5D%5D;%0A
+                    if (ignoreCase && properties%5Bi%5D != null && properties%5Bi%5D instanceof String) %7B%0A                        properties%5Bi%5D = ((String) properties%5Bi%5D).toLowerCase();%0A                    %7D%0A
         

fix error where ignore case wasn't honored everywhere