/*******************************************************************************
 * 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.internal;

import java.util.Iterator;
import java.util.Map;

import org.eclipse.sisu.peaberry.Export;
import org.eclipse.sisu.peaberry.Import;
import org.eclipse.sisu.peaberry.ServiceWatcher;
import org.eclipse.sisu.peaberry.util.SimpleExport;

/**
 * A {@link ServiceWatcher} with similar behaviour to {@link ConcurrentImport}.
 * 
 * @author mcculls@gmail.com (Stuart McCulloch)
 */
final class ConcurrentServiceWatcher<S>
    implements ServiceWatcher<S> {

  final Iterable<Import<S>> services;
  final ServiceWatcher<? super S> watcher;

  // the "best" service
  Import<S> currentImport;
  Export<S> currentExport;

  ConcurrentServiceWatcher(final Iterable<Import<S>> services,
      final ServiceWatcher<? super S> watcher) {
    this.services = services;
    this.watcher = watcher;
  }

  private final class TrackingExport<T>
      extends SimpleExport<T> {

    private final Import<T> thisImport;

    TrackingExport(final Import<T> thisImport) {
      super(thisImport);

      this.thisImport = thisImport;
    }

    // Export aspect is only active when our service is "best"

    @Override
    @SuppressWarnings("unchecked")
    public void put(final T newInstance) {
      super.put(newInstance);

      synchronized (ConcurrentServiceWatcher.this) {
        if (null != currentExport && thisImport.equals(currentImport)) {
          currentExport.put((S) newInstance);

          // is this being removed?
          if (null == newInstance) {
            updateBestService();
          }
        }
      }
    }

    @Override
    public void attributes(final Map<String, ?> newAttributes) {
      super.attributes(newAttributes);

      synchronized (ConcurrentServiceWatcher.this) {
        if (null != currentExport && thisImport.equals(currentImport)) {
          currentExport.attributes(newAttributes);
        }

        // has ranking changed?
        updateBestService();
      }
    }
  }

  public synchronized <T extends S> Export<T> add(final Import<T> service) {
    updateBestService();

    // every new service has a tracker
    return new TrackingExport<T>(service);
  }

  void updateBestService() {

    // check the last-known best service is still the best
    final Iterator<Import<S>> i = services.iterator();
    final Import<S> bestImport = i.hasNext() ? i.next() : null;

    if (null != currentImport && currentImport.equals(bestImport)) {
      return; // still the same...
    }

    // best service has changed
    if (null != currentExport) {
      currentExport.unput();
    }

    // report service (if any)
    currentImport = bestImport;
    currentExport = null == bestImport ? null : watcher.add(bestImport);
  }

  @Override
  public boolean equals(final Object rhs) {
    if (rhs instanceof ConcurrentServiceWatcher<?>) {
      final ConcurrentServiceWatcher<?> concurrentWatcher = (ConcurrentServiceWatcher<?>) rhs;
      return services.equals(concurrentWatcher.services)
          && watcher.equals(concurrentWatcher.watcher);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return services.hashCode() ^ watcher.hashCode();
  }
}
