package migl.util;

import java.util.Iterator;
import java.util.function.Function;

public class ConsListImpl<E> extends Cons<E, ConsList<E>> implements ConsList<E>{
	
	private final int size;

	public ConsListImpl(E left, ConsList<E> right) {
		super(left, right);
		this.size = 1 + right.size();
	}

	@Override
	public Iterator<E> iterator() {
		return new Iterator<E>() {
			private ConsList<E> current = ConsListImpl.this;
			@Override
			public boolean hasNext() {
				return !current.isEmpty();
			}
			
			@Override
			public E next() {
				E e = current.car();
				current = current.cdr();
				return e;
			}
		};
	}

	@Override
	public ConsList<E> prepend(E e) {
		return new ConsListImpl<>(e, this);
	}

	@Override
	public ConsList<E> append(E e) {
		return appendRec(e, this);
	}
	
	private ConsList<E> appendRec(E e, ConsList<E> list) {
		if(list.isEmpty()) {
			return new ConsListImpl<>(e, list);
		}
		return new ConsListImpl<>(list.car(), this.appendRec(e, list.cdr()));
	}

	@Override
	public boolean isEmpty() {
		return this.size == 0;
	}

	@Override
	public E car() {
		return this.left();
	}

	@Override
	public ConsList<E> cdr() {
		return this.right();
	}

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

	@Override
	public <T> ConsList<T> map(Function<E, T> f) {
		ConsList<T> result = new ConsEmptyList<>();
		for(E e : this) {
			result = result.append(f.apply(e));
		}
		return result;

		/* 
		//Version avec prepend
		ConsList<E> list[] = new ConsList[this.size];
		//Recuperation des elements de la liste
		ConsList<E> actual = this;
		for(int i = 0; i < this.size; i++) {
			list[i] = actual;
			actual = actual.cdr();
		}
		//Creation nouvelle liste
		ConsList<T> result = new ConsEmptyList<>();
		for(int i = this.size - 1; i > -1; i--) {
			result = result.prepend(f.apply(list[i].car()));
		}
		return result;
		//*/
	}
	
	@Override
	public String toString() {
		String str = "(";
		for(E e : this) {
			if(e == null) {
				str += "null ";
			} else {
				str += e.toString() + " ";
			}
		}
		return str.trim() + ")";
	}

}
