package aalg.algebra;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * Iterator that iterates over multimonoid elements.
 * @author Ing. Petr Ležák
 *
 * @param <F> type of underlying field elements
 */
public class MultiMonoidIterator<F extends IElement> implements Iterator<MultiMonoidElement<F>> {
   
	private MultiMonoidElement<F> nextElement;   // Next element, null if there are no more elements
	
	private final IField<F> field;   // Underlying field
	private final int size;   // Number of parts
	private final List<Iterator<F>> iteratorList;   // List of part iterators
	private final VectorImpl<F> parts;   // Vector of parts of 'nextElement'
	
	/**
	 * Creates iterator over given multimonoid.
	 * @param monoid iterated monoid
	 */
	public MultiMonoidIterator(final MultiMonoid<F> monoid) {
		this.field = monoid.getBaseField();
		this.size = monoid.getSize();
		
		this.iteratorList = new ArrayList<Iterator<F>>(size);
		
		for (int i = 0; i < size; i++)
			this.iteratorList.add (null);
		
		this.parts = new DenseVector<F>(field, size);
		
		findFirstElement();
	}
	
	/**
	 * Moves iterator to next element.
	 * Method updates vector 'parts' and list of iterators 'iteratorList'
	 * and sets 'nextElement' from them.
	 * If there is no more elements then nextElement is set to null.
	 */
	private boolean findNextElement() {
		for (int i = 0; i < size; i++) {
			final Iterator<F> iterator = iteratorList.get(i);
			
			if (iterator.hasNext()) {
				parts.set (i, iterator.next());
				nextElement = createElementFromIteratorList();
				
				return true;
			}
			else {
				final Iterator<F> newIterator = field.getElements().iterator();
				
				iteratorList.set(i, newIterator);
				parts.set(i, newIterator.next());
			}
		}
		
		nextElement = null;
		return false;
	}
	
	/**
	 * Creates element from vector parts.
	 * @return new element with parts from vector 'parts'
	 */
	private MultiMonoidElement<F> createElementFromIteratorList() {
		return new MultiMonoidElement<F> (parts);
	}

	/**
	 * Finds first element.
	 * First element contains zero parts.
	 */
	private void findFirstElement() {
		for (int i = 0; i < size; i++) {
			final Iterator<F> newIterator = field.getElements().iterator();
			
			iteratorList.set(i, newIterator);
			parts.set(i, newIterator.next());
		}
		
		nextElement = createElementFromIteratorList();
	}
	
	@Override
	public boolean hasNext() {
		return nextElement != null;
	}

	@Override
	public MultiMonoidElement<F> next() {
		if (!hasNext())
			throw new NoSuchElementException();
		
		final MultiMonoidElement<F> result = nextElement;
		findNextElement();
		
		return result;
	}

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

}
