/*******************************************************************************
 * Copyright (c) 2006, 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.core.tests.internal.registry.simple;

import org.eclipse.core.runtime.*;
import org.eclipse.core.tests.internal.registry.simple.utils.SimpleRegistryListener;

/**
 * Tests removal APIs using a simple registry.
 * @since 3.2
 */
public class DirectExtensionRemove extends BaseExtensionRegistryRun {

	private final static String pointA = "PointA"; //$NON-NLS-1$
	private final static String pointB = "PointB"; //$NON-NLS-1$

	private final static String extensionA1 = "TestExtensionA1"; //$NON-NLS-1$
	private final static String extensionA2 = "TestExtensionA2"; //$NON-NLS-1$

	public DirectExtensionRemove() {
		super();
	}

	public DirectExtensionRemove(String name) {
		super(name);
	}

	// Fill the registry; remove half; check listener; check what's left
	public void testExtensionPointAddition() {
		IContributor nonBundleContributor = ContributorFactorySimple.createContributor("DirectRemoveProvider"); //$NON-NLS-1$
		String namespace = nonBundleContributor.getName();
		fillRegistry(nonBundleContributor);
		checkRegistryFull(namespace);

		SimpleRegistryListener listener = new SimpleRegistryListener();
		listener.register(simpleRegistry);
		remove(namespace);

		checkListener(listener);
		checkRegistryRemoved(namespace);
		listener.unregister(simpleRegistry);
	}

	/**
	 * Tests that configuration elements associated with the removed extension
	 * are removed.
	 */
	public void testAssociatedConfigElements() {
		IContributor nonBundleContributor = ContributorFactorySimple.createContributor("CETest"); //$NON-NLS-1$
		String namespace = nonBundleContributor.getName();
		processXMLContribution(nonBundleContributor, getXML("CERemovalTest.xml")); //$NON-NLS-1$

		IExtensionPoint extensionPointA = simpleRegistry.getExtensionPoint(qualifiedName(namespace, "PointA")); //$NON-NLS-1$
		assertNotNull(extensionPointA);
		IExtension[] extensionsA = extensionPointA.getExtensions();
		assertTrue(extensionsA.length == 2);

		// check first extension
		IExtension ext1 = extensionPointA.getExtension(qualifiedName(namespace, "TestExtensionA1")); //$NON-NLS-1$
		assertNotNull(ext1);
		IConfigurationElement[] ces11 = ext1.getConfigurationElements(); // this will be used later
		assertNotNull(ces11);
		assertEquals(1, ces11.length);
		String[] attrs1 = ces11[0].getAttributeNames();
		assertNotNull(attrs1);
		assertEquals(1, attrs1.length);
		assertEquals("class", attrs1[0]); //$NON-NLS-1$
		IConfigurationElement[] ces12 = ces11[0].getChildren(); // this will be used later
		assertNotNull(ces12);
		assertEquals(1, ces12.length);
		String[] attrs2 = ces12[0].getAttributeNames();
		assertNotNull(attrs2);
		assertEquals(1, attrs2.length);
		assertEquals("value", attrs2[0]); //$NON-NLS-1$

		// check second extension
		IExtension ext2 = extensionPointA.getExtension(qualifiedName(namespace, "TestExtensionA2")); //$NON-NLS-1$
		assertNotNull(ext2);
		IConfigurationElement[] ces21 = ext2.getConfigurationElements(); // this will be used later
		IConfigurationElement[] ces22 = ces21[0].getChildren(); // this will be used later
		String[] attrs22 = ces22[0].getAttributeNames();
		assertNotNull(attrs22);
		assertEquals(1, attrs22.length);
		assertEquals("value", attrs22[0]); //$NON-NLS-1$

		// remove extension1
		// listener to verify that valid CEs are included in the notification
		IRegistryChangeListener listener = event -> {
			IExtensionDelta[] deltas = event.getExtensionDeltas();
			assertTrue(deltas.length == 1);
			for (IExtensionDelta delta : deltas) {
				assertTrue(delta.getKind() == IExtensionDelta.REMOVED);
				IExtension extension = delta.getExtension();
				assertNotNull(extension);

				IConfigurationElement[] l_ces11 = extension.getConfigurationElements();
				assertNotNull(l_ces11);
				assertEquals(1, l_ces11.length);
				String[] l_attrs1 = l_ces11[0].getAttributeNames();
				assertNotNull(l_attrs1);
				assertEquals(1, l_attrs1.length);
				assertEquals("class", l_attrs1[0]); //$NON-NLS-1$
				IConfigurationElement[] l_ces12 = l_ces11[0].getChildren();
				assertNotNull(l_ces12);
				assertEquals(1, l_ces12.length);
				String[] l_attrs2 = l_ces12[0].getAttributeNames();
				assertNotNull(l_attrs2);
				assertEquals(1, l_attrs2.length);
				assertEquals("value", l_attrs2[0]); //$NON-NLS-1$
			}
		};

		//SimpleRegistryListener listener = new SimpleRegistryListener() {};
		simpleRegistry.addRegistryChangeListener(listener);
		try {
			simpleRegistry.removeExtension(ext1, userToken);
		} finally {
			simpleRegistry.removeRegistryChangeListener(listener);
		}

		// basic checks
		IExtension[] extensionsRemoved = extensionPointA.getExtensions();
		assertTrue(extensionsRemoved.length == 1);

		// re-check configuration elements
		boolean exceptionFound = false;
		try {
			ces11[0].getAttributeNames(); // should produce an exception
		} catch (InvalidRegistryObjectException e) {
			exceptionFound = true;
		}
		assertTrue(exceptionFound);

		exceptionFound = false;
		try {
			ces12[0].getAttributeNames(); // should produce an exception
		} catch (InvalidRegistryObjectException e) {
			exceptionFound = true;
		}

		assertTrue(exceptionFound);
		// the non-removed extension CEs should still be valid
		String[] attrs22removed = ces22[0].getAttributeNames();
		assertNotNull(attrs22removed);
		assertEquals(1, attrs22removed.length);
		assertEquals("value", attrs22removed[0]); //$NON-NLS-1$
	}

	private void fillRegistry(IContributor contributor) {
		processXMLContribution(contributor, getXML("RemovalTest.xml")); //$NON-NLS-1$
	}

	private void checkRegistryFull(String namespace) {
		IExtensionPoint extensionPointA = simpleRegistry.getExtensionPoint(qualifiedName(namespace, pointA));
		assertNotNull(extensionPointA);
		IExtensionPoint extensionPointB = simpleRegistry.getExtensionPoint(qualifiedName(namespace, pointB));
		assertNotNull(extensionPointB);
		IExtension[] extensionsA = extensionPointA.getExtensions();
		assertTrue(extensionsA.length == 2);
		IExtension[] extensionsB = extensionPointB.getExtensions();
		assertTrue(extensionsB.length == 2);
	}

	private void remove(String namespace) {
		IExtensionPoint extensionPointB = simpleRegistry.getExtensionPoint(qualifiedName(namespace, pointB));
		assertTrue(simpleRegistry.removeExtensionPoint(extensionPointB, userToken));

		IExtension extension = simpleRegistry.getExtension(qualifiedName(namespace, extensionA1));
		assertTrue(simpleRegistry.removeExtension(extension, userToken));
	}

	private void checkRegistryRemoved(String namespace) {
		IExtensionPoint extensionPointA = simpleRegistry.getExtensionPoint(qualifiedName(namespace, pointA));
		assertNotNull(extensionPointA);
		IExtensionPoint extensionPointB = simpleRegistry.getExtensionPoint(qualifiedName(namespace, pointB));
		assertNull(extensionPointB);
		IExtension[] extensionsA = extensionPointA.getExtensions();
		assertTrue(extensionsA.length == 1);
		String Id = extensionsA[0].getUniqueIdentifier();
		assertTrue(qualifiedName(namespace, extensionA2).equals(Id));
	}

	private void checkListener(SimpleRegistryListener listener) {
		IRegistryChangeEvent event = listener.getEvent(5000);
		IExtensionDelta[] deltas = event.getExtensionDeltas();
		assertTrue(deltas.length == 2);
		for (IExtensionDelta delta : deltas) {
			assertTrue(delta.getKind() == IExtensionDelta.REMOVED);
			assertNotNull(delta.getExtension());
			assertNotNull(delta.getExtensionPoint());
		}
	}
}
