/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc., Oracle 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.
 *   Oracle Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.extender.internal.dependencies.startup;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.eclipse.gemini.blueprint.service.importer.DefaultOsgiServiceDependency;
import org.eclipse.gemini.blueprint.service.importer.OsgiServiceDependency;
import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils;
import org.springframework.util.ObjectUtils;

/**
 * Holder/helper class representing an OSGi service dependency
 * 
 * @author Costin Leau
 * @author Hal Hildebrand
 * @author Andy Piper
 */
class MandatoryServiceDependency implements OsgiServiceDependency {
	// match the class inside object class (and use a non backing reference group)
	private static final Pattern PATTERN = Pattern.compile("objectClass=(?:[^\\)]+)");

	protected final BundleContext bundleContext;

	private OsgiServiceDependency serviceDependency;
	private final AtomicInteger matchingServices = new AtomicInteger(0);
	protected final String filterAsString;
	private final String[] classes;

	MandatoryServiceDependency(BundleContext bc, Filter serviceFilter, boolean isMandatory, String beanName) {
		this(bc, new DefaultOsgiServiceDependency(beanName, serviceFilter, isMandatory));
	}

	MandatoryServiceDependency(BundleContext bc, OsgiServiceDependency dependency) {
		bundleContext = bc;
		serviceDependency = dependency;
		this.filterAsString = dependency.getServiceFilter().toString();
		this.classes = extractObjectClassFromFilter(filterAsString);
	}

	boolean matches(ServiceEvent event) {
		return serviceDependency.getServiceFilter().match(event.getServiceReference());
	}

	boolean isServicePresent() {
		return (!serviceDependency.isMandatory() || OsgiServiceReferenceUtils.isServicePresent(bundleContext,
				filterAsString));
	}

	public String toString() {
		return "Dependency on [" + filterAsString + "] (from bean [" + serviceDependency.getBeanName() + "])";
	}

	public Filter getServiceFilter() {
		return serviceDependency.getServiceFilter();
	}

	public String getBeanName() {
		return serviceDependency.getBeanName();
	}

	public boolean isMandatory() {
		return serviceDependency.isMandatory();
	}

	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		final MandatoryServiceDependency that = (MandatoryServiceDependency) o;

		return (serviceDependency.equals(that.serviceDependency));
	}

	public int hashCode() {
		int result = MandatoryServiceDependency.class.hashCode();
		result = 29 * result + serviceDependency.hashCode();
		return result;
	}

	public OsgiServiceDependency getServiceDependency() {
		return serviceDependency;
	}

	/**
	 * Adds another matching service.
	 * 
	 * @return the counter after adding the service.
	 */
	int increment() {
		return matchingServices.incrementAndGet();
	}

	/**
	 * Removes a matching service.
	 * 
	 * @return the counter after substracting the service.
	 */
	int decrement() {
		return matchingServices.decrementAndGet();
	}

	private static String[] extractObjectClassFromFilter(String filterString) {
		List<String> matches = null;
		Matcher matcher = PATTERN.matcher(filterString);
		while (matcher.find()) {
			if (matches == null) {
				matches = new ArrayList<String>(4);
			}

			matches.add(matcher.group());
		}

		return (matches == null ? new String[0] : matches.toArray(new String[matches.size()]));
	}
}