/*******************************************************************************
 * Copyright (c) 2016 École Polytechnique de Montréal
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/

package org.eclipse.tracecompass.internal.tmf.chart.core.consumer;

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

import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.provisional.tmf.chart.core.resolver.INumericalResolver;

import com.google.common.collect.ImmutableList;

/**
 * This class processes numerical values in order to create valid data for a XY
 * chart. It takes a {@link INumericalResolver} for mapping values and a
 * {@link Predicate} for testing them.
 * <p>
 * It also computes the minimum and maximum values of all the numbers it has
 * consumed.
 *
 * @author Gabriel-Andrew Pollo-Guilbert
 */
public class NumericalConsumer implements IDataConsumer {

    // ------------------------------------------------------------------------
    // Members
    // ------------------------------------------------------------------------

    private final INumericalResolver<Object, Number> fResolver;
    private final Predicate<@Nullable Number> fPredicate;
    private final List<Number> fData = new ArrayList<>();
    private Number fMin;
    private Number fMax;

    // ------------------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------------------

    /**
     * Constructor with a default non null predicate.
     *
     * @param resolver
     *            The resolver that maps values
     */
    public NumericalConsumer(INumericalResolver<Object, Number> resolver) {
        fResolver = resolver;
        fPredicate = Objects::nonNull;
        fMin = fResolver.getMaxValue();
        fMax = fResolver.getMinValue();
    }

    /**
     * Overloaded constructor with a predicate.
     *
     * @param resolver
     *            The resolver that maps values
     * @param predicate
     *            The predicate for testing values
     */
    public NumericalConsumer(INumericalResolver<Object, Number> resolver, Predicate<@Nullable Number> predicate) {
        fResolver = resolver;
        fPredicate = predicate;
        fMin = fResolver.getMaxValue();
        fMax = fResolver.getMinValue();
    }

    // ------------------------------------------------------------------------
    // Overriden methods
    // ------------------------------------------------------------------------

    @Override
    public boolean test(Object obj) {
        Number number = fResolver.getMapper().apply(obj);
        return fPredicate.test(number);
    }

    @Override
    public void accept(Object obj) {
        Number number = fResolver.getMapper().apply(obj);

        if (number == null) {
            number = fResolver.getZeroValue();
        }

        /* Update the minimum value */
        if (fResolver.getComparator().compare(number, fMin) < 0) {
            fMin = number;
        }

        /* Update the maximum value */
        if (fResolver.getComparator().compare(number, fMax) > 0) {
            fMax = number;
        }

        fData.add(number);
    }

    // ------------------------------------------------------------------------
    // Accessors
    // ------------------------------------------------------------------------

    /**
     * Accessor that returns the generated list of number.
     *
     * @return The list of number
     */
    public List<Number> getData() {
        return ImmutableList.copyOf(fData);
    }

    /**
     * Accessor that returns the minimum numerical value that has been consumed.
     *
     * @return The minimum value
     */
    public Number getMin() {
        return fMin;
    }

    /**
     * Accessor that returns the maximum numerical value that has been consumed.
     *
     * @return The maximum value
     */
    public Number getMax() {
        return fMax;
    }

}
