/*******************************************************************************
 * Copyright (c) 2004, 2005 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.osgi.tests.services.resolver;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.eclipse.osgi.service.resolver.HostSpecification;
import org.eclipse.osgi.service.resolver.ImportPackageSpecification;
import org.eclipse.osgi.service.resolver.PlatformAdmin;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.service.resolver.StateObjectFactory;
import org.eclipse.osgi.service.resolver.VersionConstraint;
import org.eclipse.osgi.tests.OSGiTest;
import org.eclipse.osgi.tests.OSGiTestsActivator;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;

public abstract class AbstractStateTest extends OSGiTest {
	private ServiceReference platformAdminRef;
	protected PlatformAdmin platformAdminService;

	protected void setUp() throws Exception {
		platformAdminRef = OSGiTestsActivator.getContext().getServiceReference(PlatformAdmin.class);
		platformAdminService = (PlatformAdmin) OSGiTestsActivator.getContext().getService(platformAdminRef);
	}

	protected void tearDown() throws Exception {
		OSGiTestsActivator.getContext().ungetService(platformAdminRef);
	}

	public AbstractStateTest(String testName) {
		super(testName);
	}

	public void assertContains(String tag, Object[] array, Object element) {
		for (Object o : array) {
			if (o == element) {
				return;
			}
		}
		fail(tag);
	}

	public void assertEquals(State original, State copy) {
		assertEquals("", original, copy);
	}

	public void assertEquals(String tag, BundleDescription original, BundleDescription copy) {
		if (original == copy)
			return;
		assertEquals(tag + ".0", original.getBundleId(), copy.getBundleId());
		assertEquals(tag + ".1", original.getSymbolicName(), copy.getSymbolicName());
		assertEquals(tag + ".2", original.getVersion(), copy.getVersion());
		assertEquals(tag + ".3", original.getLocation(), copy.getLocation());
		assertEquals(tag + ".4", original.isResolved(), copy.isResolved());
		assertEquals(tag + ".5", original.getHost(), copy.getHost());
		ExportPackageDescription[] originalExportPackages = original.getExportPackages();
		ExportPackageDescription[] copyExportPackages = copy.getExportPackages();
		assertEquals(tag + ".6", originalExportPackages.length, copyExportPackages.length);
		for (int i = 0; i < originalExportPackages.length; i++)
			assertEquals(tag + ".7." + i, originalExportPackages[i], copyExportPackages[i]);
		ImportPackageSpecification[] originalImportPackages = original.getImportPackages();
		ImportPackageSpecification[] copyImportPackages = copy.getImportPackages();
		assertEquals(tag + ".8", originalImportPackages.length, copyImportPackages.length);
		for (int i = 0; i < originalImportPackages.length; i++)
			assertEquals(tag + ".9." + i, originalImportPackages[i], copyImportPackages[i]);
		BundleSpecification[] originalRequiredBundles = original.getRequiredBundles();
		BundleSpecification[] copyRequiredBundles = copy.getRequiredBundles();
		assertEquals(tag + ".10", originalRequiredBundles.length, copyRequiredBundles.length);
		for (int i = 0; i < originalRequiredBundles.length; i++)
			assertEquals(tag + ".11." + i, originalRequiredBundles[i], copyRequiredBundles[i]);
		ExportPackageDescription[] originalResolvedImports = original.getResolvedImports();
		ExportPackageDescription[] copyResolvedImports = copy.getResolvedImports();
		assertEquals(tag + ".12", originalResolvedImports.length, copyResolvedImports.length);
		for (int i = 0; i < originalResolvedImports.length; i++)
			assertEquals(tag + ".13." + i, originalResolvedImports[i], copyResolvedImports[i]);
		BundleDescription[] originalResolvedRequires = original.getResolvedRequires();
		BundleDescription[] copyResolvedRequires = copy.getResolvedRequires();
		assertEquals(tag + ".14", originalResolvedRequires.length, copyResolvedRequires.length);
		for (int i = 0; i < originalResolvedRequires.length; i++)
			assertEquals(tag + ".15." + i, originalResolvedRequires[i], copyResolvedRequires[i]);
	}

	public void assertEquals(String tag, ExportPackageDescription original, ExportPackageDescription copy) {
		assertEquals(tag + ".0", original.getName(), copy.getName());
		assertEquals(tag + ".1", original.getVersion(), copy.getVersion());
		assertEquals(tag + ".2", original.getAttributes(), copy.getAttributes());
		assertEquals(tag + ".3", original.getDirectives(), original.getDirectives());
	}

	public void assertEquals(String tag, State original, State copy) {
		BundleDescription[] originalBundles = original.getBundles();
		BundleDescription[] copyBundles = copy.getBundles();
		assertEquals(tag + ".1", originalBundles.length, copyBundles.length);
		for (int i = 0; i < originalBundles.length; i++)
			assertEquals(tag + ".2." + i, originalBundles[i], copyBundles[i]);
		assertEquals(tag + ".3", original.isResolved(), copy.isResolved());
		BundleDescription[] originalResolvedBundles = original.getResolvedBundles();
		BundleDescription[] copyResolvedBundles = copy.getResolvedBundles();
		assertEquals(tag + ".4", originalResolvedBundles.length, copyResolvedBundles.length);
		for (int i = 0; i < originalResolvedBundles.length; i++)
			assertEquals(tag + ".5." + i, originalResolvedBundles[i], copyResolvedBundles[i]);
	}

	private void assertVersionConstraintEquals(String tag, VersionConstraint original, VersionConstraint copy) {
		assertEquals(tag + ".0", original == null, copy == null);
		if (original == null)
			return;
		assertEquals(tag + ".1", original.getName(), copy.getName());
		assertEquals(tag + ".2", original.getVersionRange(), copy.getVersionRange());
		assertEquals(tag + ".4", original.getSupplier() == null, copy.getSupplier() == null);
		if (original.getSupplier() != null) {
			Object o = original.getSupplier();
			if (o instanceof BundleDescription)
				assertEquals(tag + ".5", (BundleDescription) original.getSupplier(), (BundleDescription) copy.getSupplier());
			else
				assertEquals(tag + ".5", (ExportPackageDescription) original.getSupplier(), (ExportPackageDescription) copy.getSupplier());
		}
	}

	public void assertEquals(String tag, BundleSpecification original, BundleSpecification copy) {
		assertVersionConstraintEquals(tag + ".0", original, copy);
		if (original == null)
			return;
		assertEquals(tag + ".1", original.isExported(), copy.isExported());
		assertEquals(tag + ".2", original.isOptional(), copy.isOptional());
	}

	public void assertEquals(String tag, ImportPackageSpecification original, ImportPackageSpecification copy) {
		assertVersionConstraintEquals(tag + ".0", original, copy);
		if (original == null)
			return;
		assertEquals(tag + ".1", original.getAttributes(), copy.getAttributes());
		assertEquals(tag + ".2", original.getBundleSymbolicName(), copy.getBundleSymbolicName());
		assertEquals(tag + ".3", original.getBundleVersionRange(), copy.getBundleVersionRange());
		assertEquals(tag + ".5", original.getDirectives(), original.getDirectives());
	}

	public void assertEquals(String tag, HostSpecification original, HostSpecification copy) {
		assertVersionConstraintEquals(tag + ".0", original, copy);
		if (original == null)
			return;
		BundleDescription[] originalHosts = original.getHosts();
		BundleDescription[] copyHosts = copy.getHosts();
		assertEquals(tag + ".1", originalHosts == null, copyHosts == null);
		if (originalHosts == null)
			return;
		assertEquals(tag + ".2", originalHosts.length, copyHosts.length);
		for (int i = 0; i < originalHosts.length; i++)
			assertEquals(tag + ".3." + i, originalHosts[i], copyHosts[i]);
	}

	public void assertFullyResolved(String tag, BundleDescription bundle) {
		assertTrue(tag + "a", bundle.isResolved());
		ImportPackageSpecification[] packages = bundle.getImportPackages();
		for (int i = 0; i < packages.length; i++)
			assertNotNull(tag + "b_" + i, packages[i].getSupplier());
		HostSpecification host = bundle.getHost();
		if (host != null)
			assertNotNull(tag + "c", host.getHosts());
		BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
		for (int i = 0; i < requiredBundles.length; i++)
			assertNotNull(tag + "d_" + i, requiredBundles[i].getSupplier());
	}

	public void assertFullyUnresolved(String tag, BundleDescription bundle) {
		assertFalse(tag + "a", bundle.isResolved());
		ImportPackageSpecification[] packages = bundle.getImportPackages();
		for (int i = 0; i < packages.length; i++)
			assertNull(tag + "b_" + i, packages[i].getSupplier());
		HostSpecification host = bundle.getHost();
		if (host != null)
			assertNull(tag + "c", host.getHosts());
		BundleSpecification[] requiredBundles = bundle.getRequiredBundles();
		for (int i = 0; i < requiredBundles.length; i++)
			assertNull(tag + "d_" + i, requiredBundles[i].getSupplier());
	}

	public void assertIdentical(String tag, State original, State copy) {
		assertEquals(tag + ".0a", original.isResolved(), copy.isResolved());
		assertEquals(tag + ".0b", original.getTimeStamp(), copy.getTimeStamp());
		assertEquals(tag, original, copy);
	}

	public State buildComplexState() throws BundleException {
		State state = buildEmptyState();
		/*
		 * org.eclipse.b1_1.0 exports org.eclipse.p1_1.0 imports org.eclipse.p2
		 */
		final String B1_LOCATION = "org.eclipse.b1";
		final String B1_MANIFEST = "Bundle-SymbolicName: org.eclipse.b1\n" + "Bundle-Version: 1.0\n" + "Export-Package: org.eclipse.p1;specification-version=1.0\n" + "Import-Package: org.eclipse.p2";
		BundleDescription b1 = state.getFactory().createBundleDescription(parseManifest(B1_MANIFEST), B1_LOCATION, 1);
		state.addBundle(b1);
		/*
		 * org.eclipse.b2_2.0 exports org.eclipse.p2 imports org.eclipse.p1
		 */
		final String B2_LOCATION = "org.eclipse.b2";
		final String B2_MANIFEST = "Bundle-SymbolicName: org.eclipse.b2\n" + "Bundle-Version: 2.0\n" + "Export-Package: org.eclipse.p2\n" + "Import-Package: org.eclipse.p1";
		BundleDescription b2 = state.getFactory().createBundleDescription(parseManifest(B2_MANIFEST), B2_LOCATION, 2);
		state.addBundle(b2);
		/*
		 * org.eclipse.b3_2.0 exports org.eclipse.p2_2.0
		 */
		final String B3_LOCATION = "org.eclipse.b3";
		final String B3_MANIFEST = "Bundle-SymbolicName: org.eclipse.b3\n" + "Bundle-Version: 2.0\n" + "Export-Package: org.eclipse.p2; specification-version=2.0";
		BundleDescription b3 = state.getFactory().createBundleDescription(parseManifest(B3_MANIFEST), B3_LOCATION, 3);
		state.addBundle(b3);
		/*
		 * org.eclipse.b4_1.0 requires org.eclipse.b1_*
		 */
		final String B4_LOCATION = "org.eclipse.b4";
		final String B4_MANIFEST = "Bundle-SymbolicName: org.eclipse.b4\n" + "Bundle-Version: 2.0\n" + "Require-Bundle: org.eclipse.b1";
		BundleDescription b4 = state.getFactory().createBundleDescription(parseManifest(B4_MANIFEST), B4_LOCATION, 4);
		state.addBundle(b4);
		/*
		 * org.eclipse.b5_1.0 fragment for org.eclipse.b3_*
		 */
		final String B5_LOCATION = "org.eclipse.b5";
		final String B5_MANIFEST = "Bundle-SymbolicName: org.eclipse.b5\n" + "Bundle-Version: 1.0\n" + "Fragment-Host: org.eclipse.b3";
		BundleDescription b5 = state.getFactory().createBundleDescription(parseManifest(B5_MANIFEST), B5_LOCATION, 5);
		state.addBundle(b5);
		/*
		 * org.eclipse.b6_1.0 requires org.eclipse.b4
		 */
		final String B6_LOCATION = "org.eclipse.b6";
		final String B6_MANIFEST = "Bundle-SymbolicName: org.eclipse.b6\n" + "Bundle-Version: 1.0\n" + "Require-Bundle: org.eclipse.b4";
		BundleDescription b6 = state.getFactory().createBundleDescription(parseManifest(B6_MANIFEST), B6_LOCATION, 6);
		state.addBundle(b6);
		return state;
	}

	public State buildEmptyState() {
		return StateObjectFactory.defaultFactory.createState(true);
	}

	public State buildInitialState() throws BundleException {
		State state = buildEmptyState();
		/*
		 * org.eclipse.b1_1.0 exports org.eclipse.p1_1.0
		 */
		final String SYSTEM_BUNDLE_LOCATION = "org.eclipse.b1";
		final String SYSTEM_BUNDLE_MANIFEST = "Bundle-SymbolicName: org.osgi.framework\n" + "Bundle-Version: 3.0\n" + "Export-Package: org.osgi.framework; specification-version=3.0";
		BundleDescription b0 = state.getFactory().createBundleDescription(parseManifest(SYSTEM_BUNDLE_MANIFEST), SYSTEM_BUNDLE_LOCATION, 0);
		state.addBundle(b0);
		return state;
	}

	public State buildSimpleState() throws BundleException {
		State state = buildEmptyState();
		/*
		 * org.eclipse.b1_1.0 exports org.eclipse.p1_1.0 imports org.eclipse.p2
		 */
		final String B1_LOCATION = "org.eclipse.b1";
		final String B1_MANIFEST = "Bundle-SymbolicName: org.eclipse.b1\n" + "Bundle-Version: 1.0\n" + "Export-Package: org.eclipse.p1;specification-version=1.0\n" + "Import-Package: org.eclipse.p2";
		BundleDescription b1 = state.getFactory().createBundleDescription(parseManifest(B1_MANIFEST), B1_LOCATION, 1);
		state.addBundle(b1);
		/*
		 * org.eclipse.b2_2.0 exports org.eclipse.p2 imports org.eclipse.p1
		 */
		final String B2_LOCATION = "org.eclipse.b2";
		final String B2_MANIFEST = "Bundle-SymbolicName: org.eclipse.b2\n" + "Bundle-Version: 2.0\n" + "Export-Package: org.eclipse.p2\n" + "Import-Package: org.eclipse.p1";
		BundleDescription b2 = state.getFactory().createBundleDescription(parseManifest(B2_MANIFEST), B2_LOCATION, 2);
		state.addBundle(b2);
		/*
		 * org.eclipse.b3_2.0 imports org.eclipse.p1_2.0
		 */
		final String B3_LOCATION = "org.eclipse.b3";
		final String B3_MANIFEST = "Bundle-SymbolicName: org.eclipse.b3\n" + "Bundle-Version: 2.0\n" + "Import-Package: org.eclipse.p1; specification-version=2.0";
		BundleDescription b3 = state.getFactory().createBundleDescription(parseManifest(B3_MANIFEST), B3_LOCATION, 3);
		state.addBundle(b3);
		return state;
	}

	public static Dictionary parseManifest(String manifest) {
		Dictionary entries = new Hashtable();
		StringTokenizer tokenizer = new StringTokenizer(manifest, ":\n");
		while (tokenizer.hasMoreTokens()) {
			String key = tokenizer.nextToken();
			String value = tokenizer.hasMoreTokens() ? tokenizer.nextToken().trim() : "";
			entries.put(key, value);
		}
		return entries;
	}
}
