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

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

/**
 * A {@link ServiceWatcher} decorated by the given {@link ImportDecorator}.
 * 
 * @author mcculls@gmail.com (Stuart McCulloch)
 */
final class DecoratedServiceWatcher<S>
    implements ServiceWatcher<S> {

  private final ImportDecorator<? super S> decorator;
  private final ServiceWatcher<? super S> watcher;

  DecoratedServiceWatcher(final ImportDecorator<? super S> decorator,
      final ServiceWatcher<? super S> watcher) {
    this.decorator = decorator;
    this.watcher = watcher;
  }

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

    // wrap service to allow updates, decorate the wrapper, then publish
    final Export<T> original = new SimpleExport<T>(service);
    final Import<T> decorated = decorator.decorate(original);
    final Export<T> published = watcher.add(decorated);

    // watcher is not interested!
    if (null == published) {
      return null;
    }

    return new DecoratedExport<T>(original, decorated, published);
  }

  private static final class DecoratedExport<T>
      extends DelegatingImport<T>
      implements Export<T> {

    private final Export<T> original;
    private final Import<T> decorated;
    private final Export<T> published;

    DecoratedExport(final Export<T> original, final Import<T> decorated, final Export<T> published) {
      super(original);

      this.original = original;
      this.decorated = decorated;
      this.published = published;
    }

    public synchronized void put(final T newInstance) {
      // force decoration of new instance
      original.put(newInstance);
      published.put(null == newInstance ? null : decorated.get());
    }

    public synchronized void attributes(final Map<String, ?> attributes) {
      // force decoration of new attributes
      original.attributes(attributes);
      published.attributes(decorated.attributes());
    }

    public void unput() {
      put(null); // simple alias
    }
  }

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

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