package ua.nure.jfn.task2;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class ArrayImpl<T> implements Array<T> {

	private Object[] elements;
	private int size = 0;

	public ArrayImpl() {
	}

	public ArrayImpl(Container<T> container) {
		elements = new Object[container.size() + 1];

		for (T element : container)
			add(element);
	}

	@Override
	public void add(T element) {
		if (elements == null) {
			elements = new Object[255];
		} else if (size == elements.length) {
			Object[] newElements = new Object[elements.length * 2];

			for (int i = 0; i < size; i++)
				newElements[i] = elements[i];

			elements = newElements;
		}

		elements[size++] = element;
	}

	@Override
	public void clear() {
		for (int i = 0; i < size; i++)
			elements[i] = null;

		size = 0;
	}

	@Override
	public int size() {
		return size;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T get(int index) {
		if (index >= size || index < 0)
			throw new ArrayIndexOutOfBoundsException();

		return (T) elements[index];
	}

	@Override
	public int indexOf(T element) {
		for (int i = 0; i < size; i++) {
			if (element == null) {
				if (elements[i] == null)
					return i;
			} else if (elements[i] != null && elements[i].equals(element))
				return i;
		}

		return -1;
	}

	@Override
	public void remove(int index) {
		if (index >= size || index < 0)
			throw new ArrayIndexOutOfBoundsException();

		for (index += 1; index < size; index++)
			elements[index - 1] = elements[index];

		size--;

	}

	@Override
	public void set(int index, T element) {
		if (index >= size || index < 0)
			throw new ArrayIndexOutOfBoundsException();

		elements[index] = element;
	}

	@Override
	public String toString() {
		if (elements == null)
			return "[]";

		String repr = "[";
		for (int i = 0; i < size; i++) {
			if (elements[i] == null)
				repr += "null";
			else
				repr += elements[i].toString();

			if (i != size - 1)
				repr += ", ";
		}

		repr += "]";

		return repr;
	}

	@Override
	public Iterator<T> iterator() {
		return new IteratorImpl();
	}

	private class IteratorImpl implements Iterator<T> {
		int currentIndex = 0;
		int lastReturnedIndex = -1;

		@Override
		public boolean hasNext() {
			return currentIndex < size;
		}

		@SuppressWarnings("unchecked")
		@Override
		public T next() {
			if (!hasNext())
				throw new NoSuchElementException();

			lastReturnedIndex = currentIndex;
			return (T) elements[currentIndex++];
		}

		@Override
		public void remove() {
			if (lastReturnedIndex == -1)
				throw new IllegalStateException(
						"next() has not been called, or remove() has already been called after the last next()");

			ArrayImpl.this.remove(lastReturnedIndex);
			currentIndex = lastReturnedIndex;
			lastReturnedIndex = -1;
		}
	}

	@Override
	public Stream<T> stream() {
		return new StreamImpl<>(this);
	}

	public static void main(String[] args) {
		Array<Integer> intArray = new ArrayImpl<>();

		intArray.add(10);
		intArray.add(20);
		intArray.add(30);
		intArray.add(40);
		intArray.add(20);

		System.out.println("Testing get method:");
		System.out.println("Element at index 0: " + intArray.get(0));
		System.out.println("Element at index 2: " + intArray.get(2));

		System.out.println("\nTesting indexOf method:");
		System.out.println("Index of element 20: " + intArray.indexOf(20));
		System.out.println("Index of element 50: " + intArray.indexOf(50));

		System.out.println("\nTesting set method:");
		System.out.println("Before setting: " + intArray);
		intArray.set(1, 25);
		System.out.println("After setting index 1 to 25: " + intArray);

		System.out.println("\nTesting remove method:");
		System.out.println("Before removing: " + intArray);
		intArray.remove(2);
		System.out.println("After removing element at index 2: " + intArray);

		System.out.println("\nTesting Container interface methods:");
		System.out.println("Size of array: " + intArray.size());
	}
}
