package cc.redberry.pipe;

import cc.redberry.pipe.blocks.Merger;
import cc.redberry.pipe.blocks.ParallelProcessor;
import cc.redberry.pipe.util.Chunk;
import org.apache.commons.math.random.RandomGenerator;
import org.apache.commons.math.random.Well19937c;
import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static cc.redberry.pipe.CUtils.*;

public class CUtilsTest {
    @Test
    public void testAsOutputPort1() throws Exception {
        OutputPort<Integer> op = asOutputPort(Arrays.asList(1));
        Assert.assertEquals((Integer) 1, op.take());
        Assert.assertNull(op.take());
    }

    @Test
    public void testAsOutputPort2() throws Exception {
        OutputPort<Integer> op = asOutputPort(Arrays.asList(1, 3));
        Assert.assertEquals((Integer) 1, op.take());
        Assert.assertEquals((Integer) 3, op.take());
        Assert.assertNull(op.take());
    }

    @Test
    public void testChunks1() throws Exception {
        int count = 100000;
        RandomGenerator rd = new Well19937c();
        Set<Integer> values = new HashSet<>();
        for (int i = 0; i < count; ++i)
            values.add(rd.nextInt());

        Set<Integer> valuesCopy = new HashSet<>(values);

        Merger<Chunk<Integer>> buffered = buffered(chunked(asUnsafeOutputPort(values), 128), 16);
        Processor<Integer, Integer> proc = new Processor<Integer, Integer>() {
            @Override
            public Integer process(Integer input) {
                return input + 1;
            }
        };
        ParallelProcessor<Chunk<Integer>, Chunk<Integer>> cParallelProcessor =
                new ParallelProcessor<>(buffered, chunked(proc), 16, 20);

        for (Integer integer : it(unchunked(cParallelProcessor)))
            Assert.assertTrue(valuesCopy.remove(integer - 1));

        Assert.assertTrue(valuesCopy.isEmpty());
    }

    @Test
    public void testChunks2() throws Exception {
        Assert.assertNull(unchunked(EMPTY_OUTPUT_PORT).take());
        Assert.assertNull(unchunked(EMPTY_OUTPUT_PORT_CLOSEABLE).take());
    }

    @Test
    public void testChunks3() throws Exception {
        int count = 100000;
        RandomGenerator rd = new Well19937c();
        Set<Integer> values = new HashSet<>();
        for (int i = 0; i < count; ++i)
            values.add(rd.nextInt());

        final ConcurrentHashMap<Integer, Integer> valuesCopy = new ConcurrentHashMap<>(values.size());
        for (Integer value : values)
            valuesCopy.put(value, value);

        Merger<Chunk<Integer>> buffered = buffered(chunked(asUnsafeOutputPort(values), 128), 16);
        Processor<Integer, Integer> proc = new Processor<Integer, Integer>() {
            @Override
            public Integer process(Integer input) {
                return input + 1;
            }
        };
        ParallelProcessor<Chunk<Integer>, Chunk<Integer>> cParallelProcessor =
                new ParallelProcessor<>(buffered, chunked(proc), 16, 20);

        processAllInParallel(unchunked(cParallelProcessor), new VoidProcessor<Integer>() {
            @Override
            public void process(Integer input) {
                Assert.assertTrue(valuesCopy.remove(input - 1) != null);
            }
        }, 20);

        Assert.assertTrue(valuesCopy.isEmpty());
    }

    @Test
    public void testChunks4() throws Exception {
        int count = 100000;
        RandomGenerator rd = new Well19937c();
        Set<Integer> values = new HashSet<>();
        for (int i = 0; i < count; ++i)
            values.add(rd.nextInt());

        final ConcurrentHashMap<Integer, Integer> valuesCopy = new ConcurrentHashMap<>(values.size());
        for (Integer value : values)
            valuesCopy.put(value, value);

        Merger<Chunk<Integer>> buffered = buffered(chunked(asUnsafeOutputPort(values), 128), 16);
        processAllInParallel(unchunked(buffered), new VoidProcessor<Integer>() {
            @Override
            public void process(Integer input) {
                Assert.assertTrue(valuesCopy.remove(input) != null);
            }
        }, 20);

        Assert.assertTrue(valuesCopy.isEmpty());
    }

    @Test
    public void testChunks5() throws Exception {
        int count = 100000;
        RandomGenerator rd = new Well19937c();
        Set<Integer> values = new HashSet<>();
        for (int i = 0; i < count; ++i)
            values.add(rd.nextInt());

        final ConcurrentHashMap<Integer, Integer> valuesCopy = new ConcurrentHashMap<>(values.size());
        for (Integer value : values)
            valuesCopy.put(value, value);

        OutputPort<Chunk<Integer>> chunked = chunked(asUnsafeOutputPort(values), 128);
        processAllInParallel(unchunked(chunked), new VoidProcessor<Integer>() {
            @Override
            public void process(Integer input) {
                Assert.assertTrue(valuesCopy.remove(input) != null);
            }
        }, 20);

        Assert.assertTrue(valuesCopy.isEmpty());
    }
}