/*
 * cc.redberry.pipe: java library for implementation of concurrent pipelines
 * Copyright (c) 2010-2012
 * Bolotin Dmitriy <bolotin.dmitriy@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package cc.redberry.pipe.blocks;

import cc.redberry.pipe.InputPort;
import cc.redberry.pipe.OutputPort;
import cc.redberry.pipe.OutputPortCloseable;
import cc.redberry.primitives.Filter;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Filtering OutputPort wrapper. Propagates only elements accepted by filter.
 *
 * @param <T> type of filtered objects
 * @author Bolotin Dmitriy (bolotin.dmitriy@gmail.com)
 */
public final class FilteringPort<T> implements OutputPortCloseable<T> {
    private final OutputPort<T> port;
    private final Filter<? super T> filter;
    private final AtomicInteger accepted = new AtomicInteger(0);
    private final AtomicInteger rejected = new AtomicInteger(0);
    private InputPort<T> discardedPort;

    /**
     * Creates filtering port.
     *
     * @param port   input object stream
     * @param filter filter
     */
    public FilteringPort(OutputPort<T> port, Filter<? super T> filter) {
        this(port, filter, null);
    }

    /**
     * Creates filtering port.
     *
     * @param port          input object stream
     * @param filter        filter
     * @param discardedPort input port to put rejected objects of {@code null}
     */
    public FilteringPort(OutputPort<T> port, Filter<? super T> filter, InputPort<T> discardedPort) {
        if (port == null || filter == null)
            throw new NullPointerException();
        this.port = port;
        this.filter = filter;
        this.discardedPort = discardedPort;
    }

    @Override
    public T take() {
        T o;
        while ((o = port.take()) != null && !filter.accept(o)) {
            if (o != null && discardedPort != null)
                discardedPort.put(o);
            rejected.incrementAndGet();
        }
        if (o != null)
            accepted.incrementAndGet();
        return o;
    }

    public void attachDiscardPort(InputPort<T> discardedPort) {
        this.discardedPort = discardedPort;
    }

    public int getAcceptedCount() {
        return accepted.get();
    }

    public int getRejectedCount() {
        return rejected.get();
    }

    public int getTotalCount() {
        return accepted.get() + rejected.get();
    }

    @Override
    public void close() {
        if (port instanceof OutputPortCloseable)
            ((OutputPortCloseable) port).close();
    }
}
