/*--------------------------------------------------------------*
  Copyright (C) 2006-2015 OpenSim Ltd.

  This file is distributed WITHOUT ANY WARRANTY. See the file
  'License' for details on this and other legal matters.
*--------------------------------------------------------------*/

package org.omnetpp.common.collections;

import org.apache.commons.collections.ResettableIterator;

/**
 * Iterator for iterating the cartesian product of n iterators.
 *
 * @author tomi
 */
public class ProductIterator implements ResettableIterator {

    ResettableIterator[] iterators;
    Object[] current;
    Object[] next;
    boolean isEmpty;
    boolean hasNext;

    public ProductIterator(ResettableIterator... iterators) {
        this.iterators = iterators;
        this.current = new Object[iterators.length];
        this.next = new Object[iterators.length];
        this.hasNext = init();
        this.isEmpty = !hasNext;
    }

    public boolean init() {
        for(int i = 0; i < iterators.length; ++i) {
            ResettableIterator iterator = iterators[i];
            iterator.reset();
            if (iterator.hasNext())
                next[i] = iterator.next();
            else
                return false;
        }
        return next.length > 0;
    }

    public void reset() {
        if (isEmpty)
            hasNext = false;
        else {
            for(int i = 0; i < iterators.length; ++i) {
                ResettableIterator iterator = iterators[i];
                iterator.reset();
                next[i] = iterator.next();
            }
            hasNext = true;
        }
    }

    public boolean hasNext() {
        return hasNext;
    }

    public Object next() {
        if (!hasNext)
            return null;

        System.arraycopy(next, 0, current, 0, next.length);
        for (int i = 0; i < iterators.length; ++i) {
            ResettableIterator iterator = iterators[i];
            if (iterator.hasNext()) {
                next[i] = iterator.next();
                break;
            }
            else if (i < iterators.length - 1) {
                iterator.reset();
                next[i] = iterator.next();
            }
            else {
                hasNext = false;
            }
        }
        return current;
    }

    public void remove() {
        throw new UnsupportedOperationException("Cannot remove from a product iteration");
    }
}
