/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.service.exporter.support.internal.support;

import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils;
import org.springframework.beans.factory.BeanFactory;

/**
 * Class encapsulating the lazy dependency lookup of the target bean. Handles caching as well as the lazy listener
 * notifications.
 * 
 * @author Costin Leau
 */
public class LazyTargetResolver implements UnregistrationNotifier {

	private final BeanFactory beanFactory;
	private final String beanName;
	private final boolean cacheService;
	private volatile Object target;
	private final Object lock = new Object();
	private final AtomicBoolean activated;
	private final ListenerNotifier notifier;
	private volatile ServiceRegistrationDecorator decorator;

	public LazyTargetResolver(Object target, BeanFactory beanFactory, String beanName, boolean cacheService,
			ListenerNotifier notifier, boolean lazyListeners) {
		this.target = target;
		this.beanFactory = beanFactory;
		this.beanName = beanName;
		this.cacheService = cacheService;
		this.notifier = notifier;
		this.activated = new AtomicBoolean(!lazyListeners);
	}

	public void activate() {
		if (activated.compareAndSet(false, true)) {
			// no service registered
			if (decorator == null) {
				notifier.callUnregister(null, null);
			} else {
				Object target = getBeanIfPossible();
				Map properties = (Map) OsgiServiceReferenceUtils.getServicePropertiesSnapshot(decorator.getReference());
				notifier.callRegister(target, properties);
			}
		}
	}

	private Object getBeanIfPossible() {
		if (target == null) {
			if (cacheService || beanFactory.isSingleton(beanName)) {
				getBean();
			}
		}

		return target;
	}

	public Object getBean() {
		if (target == null) {
			if (cacheService) {
				synchronized (lock) {
					if (target == null) {
						target = beanFactory.getBean(beanName);
					}
				}
			} else {
				return beanFactory.getBean(beanName);
			}
		}
		return target;
	}

	public Class<?> getType() {
		return (target == null ? (beanFactory.isSingleton(beanName) ? beanFactory.getBean(beanName).getClass()
				: beanFactory.getType(beanName)) : target.getClass());

	}

	public void unregister(Map properties) {
		if (activated.get()) {
			Object target = getBeanIfPossible();
			notifier.callUnregister(target, properties);
		}
	}

	public void setDecorator(ServiceRegistrationDecorator decorator) {
		this.decorator = decorator;
		if (decorator != null) {
			decorator.setNotifier(this);
		}
	}

	public void notifyIfPossible() {
		if (activated.get() && notifier != null) {
			Object target = getBeanIfPossible();
			Map properties = (Map) OsgiServiceReferenceUtils.getServicePropertiesSnapshot(decorator.getReference());
			notifier.callRegister(target, properties);
		}
	}

	// called when the exporter is activated but no service is published
	public void startupUnregisterIfPossible() {
		if (activated.get() && notifier != null) {
			notifier.callUnregister(null, null);
		}
	}
}