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

import static org.osgi.framework.Constants.OBJECTCLASS;

import java.util.Dictionary;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.eclipse.sisu.peaberry.Export;
import org.eclipse.sisu.peaberry.Import;
import org.eclipse.sisu.peaberry.util.SimpleExport;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

/**
 * {@link Export} implementation backed by an OSGi {@link ServiceRegistration}.
 * 
 * @author mcculls@gmail.com (Stuart McCulloch)
 */
final class OSGiServiceExport<T>
    extends SimpleExport<T> {

  private final BundleContext bundleContext;
  private ServiceRegistration reg;

  OSGiServiceExport(final BundleContext bundleContext, final Import<T> service) {
    super(service);
    this.bundleContext = bundleContext;
    exportOSGiService();
  }

  @Override
  public synchronized void put(final T newInstance) {
    removeOSGiService();
    super.put(newInstance);
    exportOSGiService();
  }

  @Override
  public synchronized void attributes(final Map<String, ?> newAttributes) {
    super.attributes(newAttributes);
    if (null != reg) {
      reg.setProperties(getProperties(newAttributes));
    }
  }

  private void exportOSGiService() {
    final T instance = get();
    if (null != instance) {
      final Dictionary<String, ?> properties = getProperties(attributes());
      final String[] interfaceNames = getInterfaceNames(instance, properties);
      reg = bundleContext.registerService(interfaceNames, instance, properties);
    }
  }

  private void removeOSGiService() {
    if (null != reg) {
      try {
        reg.unregister();
      } catch (final RuntimeException re) {/* already gone */} // NOPMD
      reg = null;
    }
  }

  private static Dictionary<String, ?> getProperties(final Map<String, ?> attributes) {
    return null == attributes || attributes.isEmpty() ? null : new AttributeDictionary(attributes);
  }

  private static String[] getInterfaceNames(final Object instance, final Dictionary<?, ?> properties) {
    final Object objectClass = null == properties ? null : properties.get(OBJECTCLASS);

    // check service attributes setting
    if (objectClass instanceof String[]) {
      return (String[]) objectClass;
    }

    final Set<String> names = new HashSet<String>();

    for (Class<?> clazz = instance.getClass(); null != clazz; clazz = clazz.getSuperclass()) {
      for (final Class<?> i : clazz.getInterfaces()) {
        names.add(i.getName());
      }
    }

    return names.toArray(new String[names.size()]);
  }
}
