/*******************************************************************************
 * Copyright (c) 2007, 2013 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
 *     Jesper S Moller - Bug 421938: [1.8] ExecutionEnvironmentDescription#getVMArguments does not preserve VM arguments
 *******************************************************************************/
package org.eclipse.jdt.debug.tests.core;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.debug.testplugin.JavaTestPlugin;
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
import org.eclipse.jdt.internal.launching.EEVMType;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.jdt.launching.environments.ExecutionEnvironmentDescription;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;

/**
 * Tests for ".ee" files - installed JRE definition files
 */
public class EEDefinitionTests extends AbstractDebugTest {

	public static IPath TEST_EE_FILE = null;
	{
		if (Platform.OS_WIN32.equals(Platform.getOS())) {
			TEST_EE_FILE = new Path("testfiles/test-jre/bin/test-foundation11-win32.ee");
		} else {
			TEST_EE_FILE = new Path("testfiles/test-jre/bin/test-foundation11.ee");
		}
	}

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

	/**
	 * Tests that the EE file is a valid file
	 */
	public void testValidateDefinitionFile() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription description = new ExecutionEnvironmentDescription(file);
		IStatus status = EEVMType.validateDefinitionFile(description);
		assertTrue("Invalid install location", status.isOK());
	}

	/**
	 * Tests that the EE install location validation returns an INFO status.
	 */
	public void testValidateInstallLocation() {
		File file = getEEFile();
		IVMInstallType vmType = getVMInstallType();
		assertNotNull("Missing EE file", file);
		assertNotNull("Missing EE VM type", vmType);
		IStatus status = vmType.validateInstallLocation(file);
		assertEquals("Invalid install location", IStatus.INFO, status.getSeverity());
	}

	/**
	 * Tests libraries for the EE file
	 */
	public void testLibraries() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription description = new ExecutionEnvironmentDescription(file);
		LibraryLocation[] libs = description.getLibraryLocations();
		String[] expected = new String[]{"end.jar", "classes.txt", "others.txt", "add.jar", "ext1.jar", "ext2.jar", "opt-ext.jar"};
		assertEquals("Wrong number of libraries", expected.length, libs.length);
		for (int i = 0; i < expected.length; i++) {
			switch (i) {
			case 4:
				// ext1 and ext2 can be in either order due to file system ordering
				assertTrue("Wrong library", expected[i].equals(libs[i].getSystemLibraryPath().lastSegment()) ||
					expected[i].equals(libs[i+1].getSystemLibraryPath().lastSegment()));
				break;
			case 5:
				// ext1 and ext2 can be in either order due to file system ordering
				assertTrue("Wrong library", expected[i].equals(libs[i].getSystemLibraryPath().lastSegment()) ||
					expected[i].equals(libs[i-1].getSystemLibraryPath().lastSegment()));
				break;
			default:
				assertEquals("Wrong library", expected[i], libs[i].getSystemLibraryPath().lastSegment());
				break;
			}
			if ("classes.txt".equals(expected[i])) {
				assertEquals("source.txt", libs[i].getSystemLibrarySourcePath().lastSegment());
			}
		}
	}

	/**
	 * Tests source attachments
	 */
	public void testSourceAttachments() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription description = new ExecutionEnvironmentDescription(file);
		LibraryLocation[] libs = description.getLibraryLocations();
		String[] expected = new String[]{"end.txt", "source.txt", "source.txt", "sourceaddsource.jar", "extra1-src.txt", "extra2-src.txt", ""};
		assertEquals("Wrong number of libraries", expected.length, libs.length);
		for (int i = 0; i < expected.length; i++) {
			switch (i) {
			case 4:
				// ext1 and ext2 can be in either order due to file system ordering
				assertTrue("Wrong attachment", expected[i].equals(libs[i].getSystemLibrarySourcePath().lastSegment()) ||
					expected[i].equals(libs[i+1].getSystemLibrarySourcePath().lastSegment()));
				break;
			case 5:
				// ext1 and ext2 can be in either order due to file system ordering
				assertTrue("Wrong attachment", expected[i].equals(libs[i].getSystemLibrarySourcePath().lastSegment()) ||
					expected[i].equals(libs[i-1].getSystemLibrarySourcePath().lastSegment()));
				break;
			case 6:
				assertEquals("Wrong attachment", Path.EMPTY, libs[i].getSystemLibrarySourcePath());
				break;
			default:
				assertEquals("Wrong attachment", expected[i], libs[i].getSystemLibrarySourcePath().lastSegment());
				break;
			}
		}
	}

	/**
	 * Tests default libraries for an EE VM type are empty.
	 */
	public void testDefaultLibraries() {
		File file = getEEFile();
		IVMInstallType vmType = getVMInstallType();
		assertNotNull("Missing EE file", file);
		assertNotNull("Missing EE VM type", vmType);
		LibraryLocation[] libs = vmType.getDefaultLibraryLocations(file);
		assertEquals("Wrong number of libraries", 0, libs.length);
	}

	/**
	 * Tests that a javadoc location can be specified.
	 */
	public void testJavadocLocation() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file);
		URL location = EEVMType.getJavadocLocation(ee.getProperties());
		URL expectedLocation = null;
		try {
			expectedLocation = new URL("http://a.javadoc.location");
		} catch (MalformedURLException e) {
			fail();
		}
		assertEquals("Incorrect javadoc location", expectedLocation, location);
	}

	/**
	 * Tests asking for the index from the EE file
	 *
	 * @throws Exception
	 * @since 3.9.0
	 */
	public void testIndexLocation() throws Exception {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file);
		URL location = EEVMType.getIndexLocation(ee.getProperties());
		URL expectedLocation = null;
		try {
			expectedLocation = new URL("http://a.index.location");
		} catch (MalformedURLException e) {
			fail();
		}
		assertEquals("Incorrect index location", expectedLocation, location);
	}

	/**
	 * Tests that a name with spaces can be specified.
	 */
	public void testVMName() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file);
		String name = ee.getProperty(ExecutionEnvironmentDescription.EE_NAME);
		assertEquals("Incorrect vm name", "Eclipse JDT Test JRE Definition", name);
	}

	/**
	 * Tests default VM arguments. All arguments from the EE file should get passed through in the
	 * same order to the command line.
	 */
	public void testVMArguments() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription description = new ExecutionEnvironmentDescription(file);
		String defaultVMArguments = description.getVMArguments();
		String[] expected = new String[] {
				"-XspecialArg:123",
				"-XspecialArg2=456",
				"-XspecialArg3=789"
		};
		int prev = -1;
		for (int i = 0; i < expected.length; i++) {
			int next = defaultVMArguments.indexOf(expected[i]);
			assertTrue("Missing argument: " + expected[i] + ": was: " + defaultVMArguments,  next >= 0);
			assertTrue("Wrong argument order: " + expected[i] + ": " + defaultVMArguments,  next > prev);
			prev = next;
		}
	}

	/**
	 * Test compatible environments
	 */
	public void testCompatibleEEs() throws CoreException {
		IVMInstall install = null;
		EEVMType vmType = (EEVMType) getVMInstallType();
		try {
			File file = getEEFile();
			assertNotNull("Missing EE file", file);
			assertNotNull("Missing EE VM type", vmType);
			VMStandin standin = JavaRuntime.createVMFromDefinitionFile(file, "test-ee-file", "test-ee-file-id");
			install = standin.convertToRealVM();

			IExecutionEnvironmentsManager manager = JavaRuntime.getExecutionEnvironmentsManager();
			IExecutionEnvironment[] envs = manager.getExecutionEnvironments();
			boolean found11 = false;
			for (int i = 0; i < envs.length; i++) {
				IExecutionEnvironment env = envs[i];
				if (env.getId().equals("CDC-1.1/Foundation-1.1")) {
					found11 = true;
					assertTrue("Should be strictly compatible with " + env.getId(), env.isStrictlyCompatible(install));
				} else if (env.getId().indexOf("jdt.debug.tests") < 0) {
					assertFalse("Should *not* be strictly compatible with " + env.getId(), env.isStrictlyCompatible(install));
				}
			}
			assertTrue("Did not find foundation 1.1 environment", found11);
		} finally {
			if (install != null && vmType != null) {
				vmType.disposeVMInstall(install.getId());
			}
		}
	}

	protected File getEEFile() {
		return JavaTestPlugin.getDefault().getFileInPlugin(TEST_EE_FILE);
	}

	protected IVMInstallType getVMInstallType() {
		return JavaRuntime.getVMInstallType(EEVMType.ID_EE_VM_TYPE);
	}

	/**
	 * Tests raw EE properties map.
	 *
	 * @throws CoreException
	 */
	public void testParseProperties() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription desc = new ExecutionEnvironmentDescription(file);
		Map<String, String> map = desc.getProperties();

		// validate expected properties
		validateProperty(ExecutionEnvironmentDescription.EXECUTABLE, "jrew.txt" , map);
		validateProperty(ExecutionEnvironmentDescription.EXECUTABLE_CONSOLE, "jre.txt", map);
		validateProperty("-XspecialArg:123", "", map);
		validateProperty("-XspecialArg2", "456", map);

	}

	protected void validateProperty(String key, String value, Map<String, String> properties) {
		assertEquals("Unexpeted value for: " + key, value, properties.get(key));
	}

	/**
	 * Tests that a name with spaces can be specified.
	 */
	public void testEmptyProperty() throws CoreException {
		File file = getEEFile();
		assertNotNull("Missing EE file", file);
		ExecutionEnvironmentDescription ee = new ExecutionEnvironmentDescription(file);
		validateProperty("-Dee.empty", "", ee.getProperties());
	}
}
