package com.insightfullogic.honest_profiler.core.aggregation.filter;

import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.CONTAINS;
import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.NOT_CONTAINS;
import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.NOT_STARTS_WITH;
import static com.insightfullogic.honest_profiler.core.aggregation.filter.Target.KEY;
import static java.util.Collections.emptyList;

import java.util.List;
import java.util.function.Predicate;

import com.insightfullogic.honest_profiler.core.aggregation.result.ItemType;

/**
 * This class describes a filter composed of several {@link FilterItem}s. The resulting filter will only accept inputs
 * which satisfy the conditions of all the {@link FilterItem}s.
 * <p>
 * Additionally it contains a boolean which generates an extra {@link Predicate} for filtering out "error frames"
 * (frames in the profile which do not correspond to proper Java methods), and a quick-filter String generates an extra
 * {@link Predicate} for filtering the key.
 * <p>
 * @param <T> the type of the input items which can be filtered
 */
public class FilterSpecification<T>
{
    // Instance Properties

    private ItemType type;
    private boolean hideErrors;
    private String quickFilter;

    private List<FilterItem<T, ?>> filters;

    // Instance Constructors

    /**
     * Constructor for an empty {@link FilterSpecification} which specifies a filter for filtering items of the
     * specified {@link ItemType}.
     * <p>
     * @param type the type of items the filter can filter
     */
    public FilterSpecification(ItemType type)
    {
        super();

        this.type = type;
        filters = emptyList();
    }

    /**
     * Constructor for an empty {@link FilterSpecification} which specifies a filter for filtering items of the
     * specified {@link ItemType}.
     * <p>
     * @param type the type of items the filter can filter
     * @param hideErrors a boolean specifying if error frames should be filtered out
     * @param filters a {@link List} of the contained {@link FilterItem}s
     */
    public FilterSpecification(ItemType type, boolean hideErrors, List<FilterItem<T, ?>> filters)
    {
        super();

        this.type = type;
        this.hideErrors = hideErrors;
        this.filters = filters;
    }

    // Instance Accessors

    /**
     * Returns a boolean indicating whether the resulting filter will filter out error frames.
     * <p>
     * @return a boolean indicating whether the resulting filter will filter out error frames.
     */
    public boolean isHideErrors()
    {
        return hideErrors;
    }

    /**
     * Returns a boolean indicating whether the FilterSpecification is non-trivial, i.e. whether it actually has any
     * filters defined. The quickfilter is not taken into account.
     * <p>
     * The reason is that this is used by the front-end to indicate to the user whether currently a filter has been
     * defined by the user. The filter is specified separately from the quickfilter condition.
     * <p>
     * @return a boolean indicating whether the FilterSpecification is non-trivial
     */
    public boolean isFiltering()
    {
        return hideErrors || filters.size() > 0;
    }

    /**
     * Sets the quickfilter String, which when not empty will generate an extra {@link Predicate} for filtering the key.
     * <p>
     * @param value the value used for filtering
     */
    public void setQuickFilter(String value)
    {
        this.quickFilter = value;
    }

    // Filter Construction Methods

    /**
     * Generates a {@link Predicate} which accepts items of type T if they are accepted by all of the filters from all
     * contained {@link FilterItem}s, and optionally if they do not contain errors and/or if the key contains the String
     * specified by the quickfilter String.
     * <p>
     * @return a {@link Predicate} implementing the {@link FilterSpecification}
     */
    public Predicate<T> getFilter()
    {
        Predicate<T> result = hideErrors ? errorFilter() : null;
        if (quickFilter != null && !quickFilter.isEmpty())
        {
            result = result == null ? quickFilter() : result.and(quickFilter());
        }
        if (filters.size() > 0)
        {
            result = result == null ? filter() : result.and(filter());
        }
        return result == null ? str -> true : result;
    }

    // Internal Filter Factory Methods

    /**
     * Create a {@link Predicate} which filters as specified by the {@link FilterItem}s in this FilterSpecification.
     * <p>
     * @return a {@link Predicate} corresponding to the contained {@link FilterItem}s
     */
    private Predicate<T> filter()
    {
        return filters.stream().map(item -> item.toFilter(type)).reduce(Predicate::and).get();
    }

    /**
     * Create a {@link Predicate} for filtering the key using the quickfilter.
     * <p>
     * @return a {@link Predicate} for filtering the key
     */
    private Predicate<T> quickFilter()
    {
        return new FilterPredicate<T, String>(type, KEY, CONTAINS, quickFilter);
    }

    /**
     * Create a {@link Predicate} for filtering out error frames.
     * <p>
     * @return a {@link Predicate} for filtering out error frames
     */
    private final Predicate<T> errorFilter()
    {
        return new FilterPredicate<T, String>(type, KEY, NOT_CONTAINS, "[ERR=").and(
            new FilterPredicate<T, String>(type, KEY, NOT_STARTS_WITH, "Unknown <"));
    }
}

@@ -162,107 +162,8 @@
 NS;%0A
-import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.NOT_CONTAINS;%0A
 impo
@@ -5376,24 +5376,27 @@
 NOT_
-CONTAINS, %22%5BERR=
+STARTS_WITH, %22AGCT.
 %22).a

FIX : #195 Error Filtering Regression