/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.jet2;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.jet2.impl.AbstractProcessor;
import com.hazelcast.jet2.impl.ListConsumer;
import com.hazelcast.jet2.impl.ListProducer;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.TestHazelcastInstanceFactory;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.junit.Assert.assertEquals;

@Category(QuickTest.class)
@RunWith(HazelcastParallelClassRunner.class)
public class JetEngineTest extends HazelcastTestSupport {

    private static TestHazelcastInstanceFactory factory;
    private JetEngine jetEngine;

    @BeforeClass
    public static void setupFactory() {
        factory = new TestHazelcastInstanceFactory();
    }

    @AfterClass
    public static void shutdownFactory() {
        factory.shutdownAll();
    }

    @Before
    public void setupEngine() {
        HazelcastInstance instance = factory.newHazelcastInstance();
        jetEngine = JetEngine.get(instance, "jetEngine");
    }

    @Test
    public void when_broadcast() {
        List<Integer> numbers = IntStream.range(0, 10).
                boxed().collect(Collectors.toList());
        DAG dag = new DAG();
        Vertex producer = new Vertex("producer", () -> new ListProducer(numbers, 4)).parallelism(1);

        ListConsumer listConsumer = new ListConsumer();
        Vertex consumer = new Vertex("consumer", () -> listConsumer)
                .parallelism(1);

        int parallelism = 4;
        Vertex processor = new Vertex("processor", Identity::new)
                .parallelism(parallelism);

        dag.addVertex(producer)
                .addVertex(consumer)
                .addVertex(processor)
                .addEdge(new Edge(producer, processor).broadcast())
                .addEdge(new Edge(processor, consumer));

        execute(dag);

        List<Object> output = listConsumer.getList();
        assertEquals(numbers.size()*parallelism, output.size());
    }

    private void execute(DAG dag) {
        Job job = jetEngine.newJob(dag);
        job.execute();
    }

    @Test
    public void test() {

        List<Integer> evens = IntStream.range(0, 10).filter(f -> f % 2 == 0).
                boxed().collect(Collectors.toList());
        List<Integer> odds = IntStream.range(0, 10).filter(f -> f % 2 != 0)
                .boxed().collect(Collectors.toList());

        DAG dag = new DAG();
        Vertex evensVertex = new Vertex("evens", () -> new ListProducer(evens, 4))
                .parallelism(1);

        Vertex oddsVertex = new Vertex("odds", () -> new ListProducer(odds, 4))
                .parallelism(1);


        Vertex processor = new Vertex("processor",
                () -> new SplittingMapper(o -> (int) o * (int) o, o -> (int) o * (int) o * (int) o))
                .parallelism(4);

        ListConsumer lhsConsumer = new ListConsumer();
        ListConsumer rhsConsumer = new ListConsumer();

        Vertex lhs = new Vertex("lhs", () -> lhsConsumer)
                .parallelism(1);

        Vertex rhs = new Vertex("rhs", () -> rhsConsumer)
                .parallelism(1);

        dag
                .addVertex(evensVertex)
                .addVertex(oddsVertex)
                .addVertex(processor)
                .addVertex(lhs)
                .addVertex(rhs)
                .addEdge(new Edge(evensVertex, 0, processor, 0))
                .addEdge(new Edge(oddsVertex, 0, processor, 1))
                .addEdge(new Edge(processor, 0, lhs, 0))
                .addEdge(new Edge(processor, 1, rhs, 0));

        execute(dag);

        System.out.println(lhsConsumer.getList());
        System.out.println(rhsConsumer.getList());
    }

    private static class Identity extends AbstractProcessor {

        @Override
        public boolean process(int ordinal, Object item) {
            emit(item);
            return true;
        }
    }
    private static class SplittingMapper extends AbstractProcessor {

        private final Function<Object, Object> left;
        private final Function<Object, Object> right;

        public SplittingMapper(Function<Object, Object> left,
                               Function<Object, Object> right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public boolean process(int ordinal, Object item) {
            if (ordinal == 0) {
                emit(0, left.apply(item));
            } else if (ordinal == 1) {
                emit(1, right.apply(item));
            }
            return true;
        }
    }
}

@@ -2030,1081 +2030,8 @@
  %7D%0A%0A
-    @Test%0A    public void when_broadcast() %7B%0A        List%3CInteger%3E numbers = IntStream.range(0, 10).%0A                boxed().collect(Collectors.toList());%0A        DAG dag = new DAG();%0A        Vertex producer = new Vertex(%22producer%22, () -%3E new ListProducer(numbers, 4)).parallelism(1);%0A%0A        ListConsumer listConsumer = new ListConsumer();%0A        Vertex consumer = new Vertex(%22consumer%22, () -%3E listConsumer)%0A                .parallelism(1);%0A%0A        int parallelism = 4;%0A        Vertex processor = new Vertex(%22processor%22, Identity::new)%0A                .parallelism(parallelism);%0A%0A        dag.addVertex(producer)%0A                .addVertex(consumer)%0A                .addVertex(processor)%0A                .addEdge(new Edge(producer, processor).broadcast())%0A                .addEdge(new Edge(processor, consumer));%0A%0A        execute(dag);%0A%0A        List%3CObject%3E output = listConsumer.getList();%0A        assertEquals(numbers.size()*parallelism, output.size());%0A    %7D%0A%0A    private void execute(DAG dag) %7B%0A        Job job = jetEngine.newJob(dag);%0A        job.execute();%0A    %7D%0A%0A
     
@@ -3518,16 +3518,38 @@
     
+jetEngine.newJob(dag).
 execute(
 dag)
@@ -3544,19 +3544,16 @@
 execute(
-dag
 );%0A%0A    

fix broken test on JetEngineTest