/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * 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.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
 */
package io.jenetics.util;

import static java.lang.Math.min;
import static java.lang.System.arraycopy;

import java.util.function.IntFunction;

import io.jenetics.internal.collection.Array;
import io.jenetics.internal.collection.ArrayMSeq;
import io.jenetics.internal.collection.ObjectStore;

/**
 * Ring buffer implementation.
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
 * @version !__version__!
 * @since 5.0
 */
final class Buffer<T> {
	private final Object[] _buffer;

	private int _index;
	private int _size;

	/**
	 * Create a new ring buffer with the given {@code capacity}.
	 *
	 * @param capacity the buffer capacity
	 */
	private Buffer(final int capacity) {
		_buffer = new Object[capacity];
	}

	/**
	 * Add a new element to te ring buffer.
	 *
	 * @param value the value to add
	 */
	void add(final T value) {
		_buffer[_index] = value;

		if (++_index == _buffer.length) {
			_index = 0;
		}
		if (_size < _buffer.length) {
			++_size;
		}
	}

	/**
	 * Add the given values to the ring buffer.
	 *
	 * @param values the values being added to the ring buffer
	 */
	@SafeVarargs
	final void addAll(final T... values) {
		if (values.length >= _buffer.length) {
			arraycopy(
				values, values.length - _buffer.length,
				_buffer, 0, _buffer.length
			);
			_size = _buffer.length;
			_index = 0;
		} else {
			final int remaining = _buffer.length - _index;
			_size = min(_size + values.length, _buffer.length);

			if (values.length <= remaining) {
				arraycopy(values, 0, _buffer, _index, values.length);
				_index += values.length;
			} else {
				arraycopy(values, 0, _buffer, _index, remaining);
				arraycopy(values, remaining, _buffer, 0, values.length - remaining);
				_index = values.length - remaining;
			}
		}
	}

	/**
	 * Add the given values to the ring buffer.
	 *
	 * @param values the values being added to the ring buffer
	 */
	void addAll(final Iterable<? extends T> values) {
		for (T value : values) {
			add(value);
		}
	}

	/**
	 * Return a snapshot of the current buffer content.
	 *
	 * @return the buffer snapshot
	 */
	Object[] toArray() {
		return toArray(Object[]::new);
	}

	/**
	 * Return a snapshot of the current buffer content.
	 *
	 * @param generator a function which produces a new array of the desired
	 *        type and the provided length
	 * @param <A> the element type of the resulting array
	 * @return the buffer snapshot
	 */
	<A> A[] toArray(final IntFunction<A[]> generator) {
		final A[] result = generator.apply(_size);
		if (_size < _buffer.length || _index == 0) {
			arraycopy(_buffer, 0, result, 0, _size);
		} else {
			arraycopy(_buffer, _index, result, 0, _buffer.length - _index);
			arraycopy(_buffer, 0, result, _buffer.length - _index, _index);
		}

		return result;
	}

	/**
	 * Return a snapshot of the current buffer content.
	 *
	 * @return the buffer snapshot
	 */
	ISeq<T> toSeq() {
		return new ArrayMSeq<T>(Array.of(ObjectStore.of(toArray()))).toISeq();
	}

	/**
	 * Create a new ring buffer with the given {@code capacity}.
	 *
	 * @param capacity the buffer capacity
	 * @return a new ring buffer with the given capacity
	 */
	static <T> Buffer<T> ofCapacity(final int capacity) {
		return new Buffer<T>(capacity);
	}

}

@@ -1915,27 +1915,213 @@
 es) %7B%0A%09%09
-if (values.
+addAll(values, 0, values.length);%0A%09%7D%0A%0A%09void addArray(final Object%5B%5D values) %7B%0A%09%09addAll(values, 0, values.length);%0A%09%7D%0A%0A%09void addAll(final Object%5B%5D values, final int start, final int length) %7B%0A%09%09if (
 length %3E
@@ -2196,16 +2196,24 @@
 r.length
+ + start
 ,%0A%09%09%09%09_b
@@ -2369,23 +2369,16 @@
 _size + 
-values.
 length, 
@@ -2402,23 +2402,16 @@
 %0A%09%09%09if (
-values.
 length %3C
@@ -2439,33 +2439,37 @@
 raycopy(values, 
-0
+start
 , _buffer, _inde
@@ -2471,23 +2471,16 @@
 _index, 
-values.
 length);
@@ -2494,23 +2494,16 @@
 ndex += 
-values.
 length;%0A
@@ -2536,17 +2536,21 @@
 values, 
-0
+start
 , _buffe
@@ -2603,16 +2603,24 @@
 emaining
+ + start
 , _buffe
@@ -2621,31 +2621,24 @@
 _buffer, 0, 
-values.
 length - rem
@@ -2659,23 +2659,16 @@
 index = 
-values.
 length -

Generalize 'addAll' method.