/*******************************************************************************
 * Copyright (c) 2013, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.cm;

import java.util.*;
import org.osgi.framework.*;

public class TargetMap {

	private final Map<ServiceReference<?>, List<List<String>>> targetToQualifiedPids = new HashMap<>();
	private final Map<String, Collection<ServiceReference<?>>> qualifiedPidToTargets = new HashMap<>();

	public List<List<String>> add(ServiceReference<?> ref) {
		List<String> specifiedPids = getPids(ref.getProperty(Constants.SERVICE_PID));
		if (specifiedPids.isEmpty())
			return Collections.EMPTY_LIST;

		Bundle b = ref.getBundle();
		String bsn = b.getSymbolicName();
		if (bsn == null) {
			bsn = ""; //$NON-NLS-1$
		}
		Version v = b.getVersion();
		String version = v == null ? "" : v.toString(); //$NON-NLS-1$
		String location = ConfigurationAdminImpl.getLocation(b);

		List<List<String>> result = new ArrayList<>(specifiedPids.size());
		for (String specifiedPid : specifiedPids) {
			getTargetsInternal(specifiedPid).add(ref);

			StringBuilder sb = new StringBuilder(specifiedPid);
			sb.append('|').append(bsn);
			String pidBsn = sb.toString();
			getTargetsInternal(pidBsn).add(ref);

			sb.append('|').append(version);
			String pidBsnVersion = sb.toString();
			getTargetsInternal(pidBsnVersion).add(ref);

			sb.append('|').append(location);
			String pidBsnVersionLocation = sb.toString();
			getTargetsInternal(pidBsnVersionLocation).add(ref);

			List<String> qualifiedPIDs = new ArrayList<>(4);
			qualifiedPIDs.add(pidBsnVersionLocation);
			qualifiedPIDs.add(pidBsnVersion);
			qualifiedPIDs.add(pidBsn);
			qualifiedPIDs.add(specifiedPid);
			result.add(qualifiedPIDs);
		}

		List<List<String>> unmodifiable = Collections.unmodifiableList(result);
		targetToQualifiedPids.put(ref, unmodifiable);
		return unmodifiable;
	}

	public static List<String> getPids(Object pid) {
		List<String> specifiedPids = Collections.EMPTY_LIST;
		if (pid == null)
			return specifiedPids;

		if (pid instanceof String) {
			specifiedPids = Collections.singletonList((String) pid);
		} else if (pid instanceof Collection) {
			@SuppressWarnings("unchecked")
			Collection<String> pidList = (Collection<String>) pid;
			specifiedPids = new ArrayList<>(pidList);
		} else if (pid.getClass().isArray()) {
			specifiedPids = new ArrayList<>(Arrays.asList((String[]) pid));
		}
		return specifiedPids;
	}

	public void remove(ServiceReference<?> ref) {
		List<List<String>> qualifiedPidLists = targetToQualifiedPids.remove(ref);
		if (qualifiedPidLists != null) {
			for (List<String> qualifiedPids : qualifiedPidLists) {
				for (String qualifiedPid : qualifiedPids) {
					Collection<ServiceReference<?>> targets = qualifiedPidToTargets.get(qualifiedPid);
					if (targets != null) {
						targets.remove(ref);
					}
				}
			}
		}
	}

	private Collection<ServiceReference<?>> getTargetsInternal(String pid) {
		Collection<ServiceReference<?>> targets = qualifiedPidToTargets.get(pid);
		if (targets == null) {
			targets = new ArrayList<>(1);
			qualifiedPidToTargets.put(pid, targets);
		}
		return targets;
	}

	List<ServiceReference<?>> getTargets(String qualifiedPid) {
		Collection<ServiceReference<?>> targets = qualifiedPidToTargets.get(qualifiedPid);
		return targets == null ? Collections.EMPTY_LIST : new ArrayList<>(targets);
	}

	List<List<String>> getQualifiedPids(ServiceReference<?> ref) {
		List<List<String>> pids = targetToQualifiedPids.get(ref);
		return pids == null ? Collections.EMPTY_LIST : pids;
	}
}
