/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.drafts
 */
package net.ranides.assira.index;

import java.util.Comparator;
import java.util.function.Function;

import net.ranides.assira.collection.query.CQuery;
import net.ranides.assira.collection.query.CQueryAbstract;

/**
 * Base class for all IQuery implementations.
 * Implements searching for named indexes using correct type of index (transforming or sorting).
 * Implements filters non-accepting null (by delegating to filters which support both options).
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @param <V> result type
 */
public abstract class IQueryAbstract<V> extends CQueryAbstract<V> implements IQuery<V> {

    /**
     * Must be implemented by subclasses. Allows to match index name to transforming index.
     *
     * @param name name
     * @param <T> inferred index type
     * @return null if there is no index with specified name
     */
    protected abstract <T> Function<V,T> mapIndexer(String name);

    /**
     *  Must be implemented by subclasses. Allows to match index name to sorting index.
     *
     * @param name name
     * @return null if there is no index with specified name
     */
    protected abstract Comparator<V> sortIndexer(String name);

    @Override
    public CQuery<V> parallel() {
        return this;
    }

    @Override
    public CQuery<V> sequential() {
        return this;
    }

    @Override
    public boolean isParallel() {
        return false;
    }

    @Override
    public IQuery<V> eq(Comparator<V> index, V value) {
        return eq(index, false, value);
    }

    @Override
    public <T> IQuery<V> eq(Function<V, T> index, T value) {
        return eq(index, false, value);
    }

    @Override
    public IQuery<V> eq(String index, Object value) {
        return eq(index, false, value);
    }

    @Override
    public IQuery<V> gt(Comparator<V> index, V value) {
        return gt(index, false, value);
    }

    @Override
    public <T> IQuery<V> gt(Function<V, T> index, T value) {
        return gt(index, false, value);
    }

    @Override
    public IQuery<V> gt(String index, Object value) {
        return gt(index, false, value);
    }

    @Override
    public IQuery<V> lt(Comparator<V> index, V value) {
        return lt(index, false, value);
    }

    @Override
    public <T> IQuery<V> lt(Function<V, T> index, T value) {
        return lt(index, false, value);
    }

    @Override
    public IQuery<V> lt(String index, Object value) {
        return lt(index, false, value);
    }

    @Override
    public IQuery<V> in(Comparator<V> index, V begin, V end) {
        return in(index, false, begin, end);
    }

    @Override
    public <T> IQuery<V> in(Function<V, T> index, T begin, T end) {
        return in(index, false, begin, end);
    }

    @Override
    public IQuery<V> in(String index, Object begin, Object end) {
        return in(index, false, begin, end);
    }

    @SuppressWarnings("unchecked")
    @Override
    public final IQuery<V> eq(String index, boolean nullable, Object value) {
        return addFilter(index, idx -> eq(idx, nullable, value), idx -> eq(idx, nullable, (V)value));
    }

    @SuppressWarnings("unchecked")
    @Override
    public final IQuery<V> lt(String index, boolean nullable, Object value) {
        return addFilter(index, idx -> lt(idx, nullable, value), idx -> lt(idx, nullable, (V) value));
    }

    @SuppressWarnings("unchecked")
    @Override
    public final IQuery<V> gt(String index, boolean nullable, Object value) {
        return addFilter(index, idx -> gt(idx, nullable, value), idx -> gt(idx, nullable, (V) value));
    }

    @SuppressWarnings("unchecked")
    @Override
    public final IQuery<V> in(String index, boolean nullable, Object begin, Object end) {
        return addFilter(index, idx -> in(idx, nullable, begin, end), idx -> in(idx, nullable, (V) begin, (V) end));
    }

    private IQuery<V> addFilter(
            String indexName,
            Function<Function<V, Object>, IQuery<V>> indexedFilter,
            Function<Comparator<V>, IQuery<V>> sortedFilter
    ) {
        Function<V, Object> mapIndexer = mapIndexer(indexName);
        if(mapIndexer != null) {
            return indexedFilter.apply(mapIndexer);
        }
        Comparator<V> sortIndexer = sortIndexer(indexName);
        if(sortIndexer != null) {
            return sortedFilter.apply(sortIndexer);
        }
        throw new IllegalArgumentException("Unknown index: " + indexName);
    }

}
