/*******************************************************************************
 * Copyright (c) 2008, 2014 Stuart McCulloch
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Stuart McCulloch - initial API and implementation
 *******************************************************************************/

package org.eclipse.sisu.peaberry.cache;

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

import org.eclipse.sisu.peaberry.AttributeFilter;
import org.eclipse.sisu.peaberry.Import;

/**
 * Filtered iterable over dynamic collection of {@link AbstractServiceImport}s.
 * <p>
 * The iterator provided by this view is valid even if the underlying collection
 * of services changes - because it keeps track of where it would be in the list
 * based on the service id and ranking.
 * 
 * @author mcculls@gmail.com (Stuart McCulloch)
 */
final class FilteredIterableService<T>
    implements Iterable<Import<T>> {

  final AbstractServiceListener<T> listener;
  final AttributeFilter filter;

  FilteredIterableService(final AbstractServiceListener<T> listener, final AttributeFilter filter) {
    this.listener = listener;
    this.filter = filter;
  }

  public Iterator<Import<T>> iterator() {
    return new Iterator<Import<T>>() {

      // track where we've been...
      private Import<T> prevImport;
      private Import<T> nextImport;

      public boolean hasNext() {
        return null != findNextImport();
      }

      public Import<T> next() {

        if (null == findNextImport()) {
          throw new NoSuchElementException();
        }

        // used cached result
        prevImport = nextImport;
        nextImport = null;

        return prevImport;
      }

      private Import<T> findNextImport() {
        if (null == nextImport) {

          // based on our last result and the current list, find next result...
          final Import<T> tempImport = listener.findNextImport(prevImport, filter);

          // ...and cache it
          if (null != tempImport) {
            prevImport = null;
            nextImport = tempImport;
          }
        }

        return nextImport;
      }

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

  @Override
  public boolean equals(final Object rhs) {
    if (rhs instanceof FilteredIterableService<?>) {
      final FilteredIterableService<?> iterable = (FilteredIterableService<?>) rhs;
      return listener.equals(iterable.listener) && equals(filter, iterable.filter);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return listener.hashCode() ^ (null == filter ? 0 : filter.hashCode());
  }

  private static boolean equals(final Object lhs, final Object rhs) {
    return null == lhs ? null == rhs : lhs.equals(rhs);
  }
}
