package com.wizzardo.tools.collections.lazy;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created by wizzardo on 08.11.15.
 */
public class Lazy<A, B> extends AbstractLazy<A, B> {

    public static <T> Lazy<T, T> of(final Iterable<T> iterable) {
        return new StartLazy<T>() {
            @Override
            protected void process() {
                for (T t : iterable) {
                    if (stop)
                        break;
                    processToChild(t);
                }
            }
        };
    }

    public static <T> Lazy<T, T> of(final Iterator<T> iterator) {
        return new StartLazy<T>() {
            @Override
            protected void process() {
                while (!stop && iterator.hasNext()) {
                    processToChild(iterator.next());
                }
            }
        };
    }

    public static <K, V> Lazy<Map.Entry<K, V>, Map.Entry<K, V>> of(Map<K, V> map) {
        return of(map.entrySet());
    }

    public static <T> Lazy<T, T> of(final T... array) {
        return new StartLazy<T>() {
            @Override
            protected void process() {
                for (T t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Integer, Integer> of(final int[] array) {
        return new StartLazy<Integer>() {
            @Override
            protected void process() {
                for (int t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Long, Long> of(final long[] array) {
        return new StartLazy<Long>() {
            @Override
            protected void process() {
                for (long t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Double, Double> of(final double[] array) {
        return new StartLazy<Double>() {
            @Override
            protected void process() {
                for (double t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Float, Float> of(final float[] array) {
        return new StartLazy<Float>() {
            @Override
            protected void process() {
                for (float t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Byte, Byte> of(final byte[] array) {
        return new StartLazy<Byte>() {
            @Override
            protected void process() {
                for (byte t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Boolean, Boolean> of(final boolean[] array) {
        return new StartLazy<Boolean>() {
            @Override
            protected void process() {
                for (boolean t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Short, Short> of(final short[] array) {
        return new StartLazy<Short>() {
            @Override
            protected void process() {
                for (short t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    public static Lazy<Character, Character> of(final char[] array) {
        return new StartLazy<Character>() {
            @Override
            protected void process() {
                for (char t : array) {
                    processToChild(t);
                    if (stop)
                        break;
                }
            }
        };
    }

    private static abstract class StartLazy<T> extends Lazy<T, T> {
        boolean stop = false;

        @Override
        protected void start() {
            if (child == null)
                return;

            process();

            child.onEnd();
        }

        protected abstract void process();

        @Override
        protected void stop() {
            stop = true;
        }
    }

    public B reduce(Reducer<B> reducer) {
        return reduce(null, reducer);
    }

    public B reduce(B def, Reducer<B> reducer) {
        LazyReduce<B> reduce = then(new LazyReduce<B>(def, reducer));
        reduce.start();
        return reduce.get();
    }

    public <T> Lazy<B, T> merge(Mapper<? super B, ? extends Lazy<T, T>> mapper) {
        return then(new LazyMapMerge<B, T>(mapper));
    }

    public Lazy<B, B> filter(Filter<? super B> filter) {
        return then(new LazyFilter<B>(filter));
    }

    public Lazy<B, B> each(Consumer<? super B> consumer) {
        return then(new LazyEach<B>(consumer));
    }

    public <T> Lazy<B, T> map(Mapper<? super B, T> mapper) {
        return then(new LazyMap<B, T>(mapper));
    }

    public <K> Map<K, List<B>> toMap(Mapper<B, K> toKey) {
        return toMap(Lazy.<K, LazyGroup<K, B>>hashMapSupplier(), toKey, new LazyGroupToListMapper<K, B>());
    }
}

@@ -1221,47 +1221,8 @@
     
-processToChild(t);%0A                    
 if (
@@ -1248,32 +1248,71 @@
            break
+;%0A                    processToChild(t)
 ;%0A              
@@ -1557,47 +1557,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -1606,32 +1606,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -1906,47 +1906,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -1955,32 +1955,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -2265,47 +2265,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -2314,32 +2314,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -2619,47 +2619,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -2668,32 +2668,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -2968,47 +2968,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -3017,32 +3017,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -3332,47 +3332,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -3381,32 +3381,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -3686,47 +3686,8 @@
 ) %7B%0A
-                    processToChild(t);%0A
     
@@ -3735,32 +3735,71 @@
          break;%0A
+                    processToChild(t);%0A
                 
@@ -4070,47 +4070,8 @@
     
-processToChild(t);%0A                    
 if (
@@ -4097,32 +4097,71 @@
            break
+;%0A                    processToChild(t)
 ;%0A              

fix tests