package com.axellience.vuegwt.client.jsnative.types;

import com.axellience.vuegwt.client.jsnative.JsTools;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsComparator;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsEach;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsMap;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsPredicate;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsPredicateWithIndex;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsPredicateWithIndexAndArray;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsReduce;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsReduceWithIndex;
import com.axellience.vuegwt.client.jsnative.jsfunctions.JsReduceWithIndexAndArray;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsOverlay;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsProperty;
import jsinterop.annotations.JsType;

import java.util.Iterator;

/**
 * Original Source: https://github.com/ltearno/angular2-gwt/
 */
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array")
public class JsArray<T>
{
    public int length;

    @JsOverlay
    @SafeVarargs
    public static <T> JsArray<T> of(T... array)
    {
        JsArray<T> result = new JsArray<>();
        for (T item : array)
            result.push(item);
        return result;
    }

    @JsOverlay
    public static <T> JsArray<T> empty()
    {
        return new JsArray<>();
    }

    @JsMethod
    public native T pop();

    @JsMethod
    public native void push(T item);

    @JsMethod
    public native JsArray<T> reverse();

    @JsMethod
    public native T shift();

    @JsMethod
    public native JsArray<T> sort(JsComparator<T> comparator);

    @JsMethod
    public native void splice(int start, int length);

    @JsMethod
    public native void unshift(T item);

    @JsMethod
    public native JsArray<T> concat(JsArray<T> array);

    @JsMethod
    public native int indexOf(T item);

    @JsMethod
    public native String join();

    @JsMethod
    public native String join(String joiner);

    @JsMethod
    public native int lastIndexOf(Object item);

    @JsMethod
    public native int lastIndexOf(Object item, int fromIndex);

    @JsMethod
    public native boolean every(JsPredicate<T> every);

    @JsMethod
    public native boolean every(JsPredicateWithIndex<T> every);

    @JsMethod
    public native boolean every(JsPredicateWithIndexAndArray<T> every);

    @JsMethod
    public native JsArray<T> filter(JsPredicate<T> predicate);

    @JsMethod
    public native JsArray<T> filter(JsPredicateWithIndex<T> predicate);

    @JsMethod
    public native JsArray<T> filter(JsPredicateWithIndexAndArray<T> predicate);

    @JsMethod
    public native T find(JsPredicate<T> predicate);

    @JsMethod
    public native T find(JsPredicateWithIndex<T> predicate);

    @JsMethod
    public native T find(JsPredicateWithIndexAndArray<T> predicate);

    @JsMethod
    public native void forEach(JsEach<T> each);

    @JsMethod
    public native <T2> JsArray<T2> map(JsMap<T, T2> map);

    @JsMethod
    public native <T2> T2 reduce(JsReduce<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduce(JsReduce<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduce<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduce<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native <T2> T2 reduce(JsReduceWithIndex<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduce(JsReduceWithIndex<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduceWithIndex<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduceWithIndex<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native <T2> T2 reduce(JsReduceWithIndexAndArray<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduce(JsReduceWithIndexAndArray<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduceWithIndexAndArray<T, T2> reduce);

    @JsMethod
    public native <T2> T2 reduceRight(JsReduceWithIndexAndArray<T, T2> reduce, T2 initialValue);

    @JsMethod
    public native boolean some(JsPredicate<T> every);

    @JsMethod
    public native boolean some(JsPredicateWithIndex<T> every);

    @JsMethod
    public native boolean some(JsPredicateWithIndexAndArray<T> every);

    @JsMethod
    public native JsArray<T> slice(int start, int end);

    @JsMethod
    public native JsArray<T> slice(int start);

    @JsProperty
    public native int getLength();

    @JsProperty
    public native void setLength(int length);

    @JsOverlay
    public final T get(int index)
    {
        return JsTools.get(this, index);
    }

    @JsOverlay
    public final void set(int index, T value)
    {
        JsTools.set(this, index, value);
    }

    @JsOverlay
    public final Iterable<T> iterate()
    {
        return () -> new Iterator<T>()
        {
            int index = 0;

            @Override
            public boolean hasNext()
            {
                return index < getLength();
            }

            @Override
            public T next()
            {
                T result = get(index);
                index++;
                return result;
            }

            @Override
            public void remove()
            {
                throw new RuntimeException();
            }
        };
    }
}

@@ -1153,16 +1153,39 @@
 Array%3CT%3E
+ implements Iterable%3CT%3E
 %0A%7B%0A    p
@@ -4965,33 +4965,32 @@
     %7D%0A%0A    @
-Js
 Over
-lay
+ride
 %0A    public 
@@ -4993,22 +4993,16 @@
 lic 
-final 
 Itera
-ble
+tor
 %3CT%3E 
@@ -5007,17 +5007,18 @@
 %3E iterat
-e
+or
 ()%0A    %7B
@@ -5036,14 +5036,8 @@
 turn
- () -%3E
  new
@@ -5087,16 +5087,51 @@
 dex = 0;
+%0A            boolean hasCalledNext;
 %0A%0A      
@@ -5384,16 +5384,54 @@
 ndex++;%0A
+                hasCalledNext = true;%0A
         
@@ -5557,35 +5557,158 @@
     
-throw new RuntimeException(
+if (!hasCalledNext)%0A                    throw new IllegalStateException();%0A%0A                hasCalledNext = false;%0A                splice(index - 1, 1
 );%0A 

Make JsArray iterable