package io.datakernel.async;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collector;

public interface IndexedCollector<T, A, R> {
	A accumulator(int stages);

	void accumulate(A accumulator, int stageIndex, T stageResult);

	R finish(A accumulator);

	default R resultOf() {
		return resultOf(Collections.emptyList());
	}

	default R resultOf(T value1) {
		//noinspection ArraysAsListWithZeroOrOneArgument - using asList instead of singletonList() to allow mutability
		return resultOf(Arrays.asList(value1));
	}

	default R resultOf(T value1, T value2) {
		return resultOf(Arrays.asList(value1, value2));
	}

	default R resultOf(List<? extends T> values) {
		A accumulator = accumulator(values.size());
		for (int i = 0; i < values.size(); i++) {
			accumulate(accumulator, i, values.get(i));
		}
		return finish(accumulator);
	}

	static <T, A, R> IndexedCollector<T, A, R> ofCollector(Collector<T, A, R> collector) {
		return new IndexedCollector<T, A, R>() {
			final BiConsumer<A, T> accumulator = collector.accumulator();

			@Override
			public A accumulator(int stages) {
				return collector.supplier().get();
			}

			@Override
			public void accumulate(A accumulator, int stageIndex, T stageResult) {
				this.accumulator.accept(accumulator, stageResult);
			}

			@Override
			public R finish(A accumulator) {
				return collector.finisher().apply(accumulator);
			}
		};
	}

	IndexedCollector<Object, Object[], List<Object>> TO_LIST = new IndexedCollector<Object, Object[], List<Object>>() {
		@Override
		public Object[] accumulator(int stages) {
			return new Object[stages];
		}

		@Override
		public void accumulate(Object[] accumulator, int stageIndex, Object stageResult) {
			accumulator[stageIndex] = stageResult;
		}

		@SuppressWarnings("unchecked")
		@Override
		public List<Object> finish(Object[] accumulator) {
			return Arrays.asList(accumulator);
		}

		@Override
		public List<Object> resultOf() {
			return Collections.emptyList();
		}

		@Override
		public List<Object> resultOf(Object value1) {
			return Arrays.asList(value1);
		}

		@Override
		public List<Object> resultOf(Object value1, Object value2) {
			return Arrays.asList(value1, value2);
		}

		@SuppressWarnings("unchecked")
		@Override
		public List<Object> resultOf(List<?> values) {
			return (List<Object>) values;
		}
	};

	@SuppressWarnings("unchecked")
	static <T> IndexedCollector<T, Object[], List<T>> toList() {
		return (IndexedCollector) TO_LIST;
	}

	@SuppressWarnings("unchecked")
	static <T> IndexedCollector<T, T[], T[]> toArray(Class<T> type) {
		return (IndexedCollector) new IndexedCollector<Object, Object[], Object[]>() {

			@Override
			public T[] accumulator(int stages) {
				return (T[]) Array.newInstance(type, stages);
			}

			@Override
			public void accumulate(Object[] accumulator, int stageIndex, Object stageResult) {
				accumulator[stageIndex] = stageResult;
			}

			@SuppressWarnings("unchecked")
			@Override
			public T[] finish(Object[] accumulator) {
				return (T[]) accumulator;
			}

			@Override
			public T[] resultOf() {
				return (T[]) Array.newInstance(type, 0);
			}

			@Override
			public T[] resultOf(Object value1) {
				T[] array = (T[]) Array.newInstance(type, 1);
				array[0] = (T) value1;
				return array;
			}

			@Override
			public T[] resultOf(Object value1, Object value2) {
				T[] array = (T[]) Array.newInstance(type, 2);
				array[0] = (T) value1;
				array[1] = (T) value2;
				return array;
			}

			@Override
			public T[] resultOf(List<?> values) {
				return (T[]) values.toArray();
			}
		};
	}
}

@@ -1,8 +1,612 @@
+/*%0A * Copyright (C) 2015-2018 SoftIndex LLC.%0A *%0A * Licensed under the Apache License, Version 2.0 (the %22License%22);%0A * you may not use this file except in compliance with the License.%0A * You may obtain a copy of the License at%0A *%0A * http://www.apache.org/licenses/LICENSE-2.0%0A *%0A * Unless required by applicable law or agreed to in writing, software%0A * distributed under the License is distributed on an %22AS IS%22 BASIS,%0A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.%0A * See the License for the specific language governing permissions and%0A * limitations under the License.%0A */%0A%0A
 package 
@@ -3291,35 +3291,16 @@
 %09%09return
- (IndexedCollector)
  new Ind
@@ -3317,32 +3317,17 @@
 tor%3C
-Object, Object%5B%5D, Object
+T, T%5B%5D, T
 %5B%5D%3E(
@@ -3471,38 +3471,33 @@
 void accumulate(
-Object
+T
 %5B%5D accumulator, 
@@ -3504,38 +3504,33 @@
 int stageIndex, 
-Object
+T
  stageResult) %7B%0A
@@ -3638,38 +3638,33 @@
 blic T%5B%5D finish(
-Object
+T
 %5B%5D accumulator) 
@@ -3820,38 +3820,33 @@
 ic T%5B%5D resultOf(
-Object
+T
  value1) %7B%0A%09%09%09%09T
@@ -3969,38 +3969,33 @@
 ic T%5B%5D resultOf(
-Object
+T
  value1, Object 
@@ -3979,38 +3979,33 @@
 ultOf(T value1, 
-Object
+T
  value2) %7B%0A%09%09%09%09T
@@ -4161,32 +4161,42 @@
  resultOf(List%3C?
+ extends T
 %3E values) %7B%0A%09%09%09%09
@@ -4201,22 +4201,16 @@
 %09%09return
- (T%5B%5D)
  values.
@@ -4217,16 +4217,30 @@
 toArray(
+accumulator(0)
 );%0A%09%09%09%7D%0A

Fix IndexedCollector.toArray() bug