/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.jdt.ui.tests.refactoring;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.zip.ZipInputStream;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.junit.Assert;

import org.eclipse.jdt.testplugin.JavaProjectHelper;
import org.eclipse.jdt.testplugin.JavaTestPlugin;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;

import org.eclipse.ltk.core.refactoring.CheckConditionsOperation;
import org.eclipse.ltk.core.refactoring.PerformRefactoringOperation;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;

import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;

import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
import org.eclipse.jdt.internal.corext.refactoring.generics.InferTypeArgumentsRefactoring;

import org.eclipse.jdt.ui.tests.refactoring.infra.ZipTools;

public class InferTypeArgumentsTests extends RefactoringTest {

	private static final boolean DETERMINE_ELEMENT_TYPE_FROM_CAST= false;
	private static final boolean BUG_map_entrySet_iterator= true;

	private static final Class clazz= InferTypeArgumentsTests.class;
	private static final String REFACTORING_PATH= "InferTypeArguments/";

	private boolean fAssumeCloneReturnsSameType= true;
	private boolean fLeaveUnconstrainedRaw= true;

	public static Test suite() {
		return setUpTest(new TestSuite(clazz));
	}

	public static Test setUpTest(Test someTest) {
		return new Java16Setup(someTest);
	}

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

	protected String getRefactoringPath() {
		return REFACTORING_PATH;
	}

	private void performCu(int expectedInitialStatus, int expectedFinalStatus) throws Exception {
		IPackageFragment packageP= getPackageP();
		String cuName="A";
		ICompilationUnit cu= packageP.getCompilationUnit(cuName + ".java");
		if (!cu.exists())
			cu= createCUfromTestFile(packageP, cuName);
		IJavaElement[] elements= { cu };
		boolean performed= perform(elements, expectedInitialStatus, expectedFinalStatus);
		if (! performed)
			return;

		String expected= getFileContents(getOutputTestFileName("A"));
		String actual= cu.getSource();
		assertEqualLines(expected, actual);
	}

	/**
	 * @param elements
	 * @param expectedInitialStatus
	 * @param expectedFinalStatus
	 * @return <code>true</code> iff performed
	 * @throws CoreException
	 */
	private boolean perform(IJavaElement[] elements, int expectedInitialStatus, int expectedFinalStatus) throws CoreException {
		InferTypeArgumentsRefactoring refactoring= ((RefactoringAvailabilityTester.isInferTypeArgumentsAvailable(elements)) ? new InferTypeArgumentsRefactoring(elements) : null);

		NullProgressMonitor pm= new NullProgressMonitor();
		RefactoringStatus initialStatus= refactoring.checkInitialConditions(pm);
		assertEquals("wrong initial condition status: " + initialStatus, expectedInitialStatus, initialStatus.getSeverity());
		if (! initialStatus.isOK())
			return false;

		refactoring.setAssumeCloneReturnsSameType(fAssumeCloneReturnsSameType);
		refactoring.setLeaveUnconstrainedRaw(fLeaveUnconstrainedRaw);

		PerformRefactoringOperation op= new PerformRefactoringOperation(
				refactoring, CheckConditionsOperation.FINAL_CONDITIONS);
		JavaCore.run(op, new NullProgressMonitor());
		RefactoringStatus finalStatus= op.getConditionStatus();
		assertEquals("wrong final condition status: " + finalStatus, expectedFinalStatus, finalStatus.getSeverity());
		if (finalStatus.getSeverity() == RefactoringStatus.FATAL)
			return false;

		assertTrue("Validation check failed: " + op.getValidationStatus(), !op.getValidationStatus().hasFatalError());
		assertNotNull("No Undo", op.getUndoChange());
		return true;
	}

	private void performCuOK() throws Exception {
		performCu(RefactoringStatus.OK, RefactoringStatus.OK);
	}

	public void compareWithZipFile(IPackageFragmentRoot src, String zipFileName) throws Exception {
		String fullName= TEST_PATH_PREFIX + getRefactoringPath() + zipFileName;
		ZipInputStream zis= new ZipInputStream(getFileInputStream(fullName));
		ZipTools.compareWithZipped(src, zis, JavaProjectHelper.JUNIT_SRC_ENCODING);
	}

	public void testCuQualifiedName() throws Exception {
		performCuOK();
	}

	public void testCuAnonymous01() throws Exception {
		performCuOK();
	}

	public void testCuTypeParams9() throws Exception {
		performCuOK();
	}

	public void testCuExistingParameterized01() throws Exception {
		performCuOK();
	}

	public void testCuGetClass() throws Exception {
		performCuOK();
	}

	public void testCuGetClass2() throws Exception {
		// Test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=211037
		// In 1.6, Object#getClass() declares return type Class<?>, but in 1.5, it's Class<? extends Object>.
		performCuOK();

		// Test the same with 1.5:
		IJavaProject project= RefactoringTestSetup.getProject();

		ArrayList classpath= new ArrayList(Arrays.asList(project.getRawClasspath()));
		IClasspathEntry jreEntry= RefactoringTestSetup.getJRELibrary().getRawClasspathEntry();
		classpath.remove(jreEntry);
		IClasspathEntry[] noRTJarCPEs= (IClasspathEntry[])classpath.toArray(new IClasspathEntry[classpath.size()]);

		project.setRawClasspath(noRTJarCPEs, new NullProgressMonitor());
		JavaProjectHelper.addRTJar15(project);

		try {
			performCuOK();
		} finally {
			project.setRawClasspath(noRTJarCPEs, new NullProgressMonitor());
			JavaProjectHelper.addRTJar16(project);
		}
	}

	public void testCuGetSuperclass() throws Exception {
		performCuOK();
	}

	public void testCuTypeLiteral() throws Exception {
		performCuOK();
	}

	public void testCuMethodTypeParam() throws Exception {
		performCuOK();
	}

	public void testCuGetTakeClassStayRaw() throws Exception {
		performCuOK();
	}

	public void testCuGetClassNewInstance() throws Exception {
		performCuOK();
	}

	public void testCuSynchronizedList() throws Exception {
		fLeaveUnconstrainedRaw= false;
		performCuOK();
	}

	public void testCuAddAll() throws Exception {
		performCuOK();
	}

	public void testCuNestedCells1() throws Exception {
		createCUfromTestFile(getPackageP(), "Cell");
		fLeaveUnconstrainedRaw= false;
		performCuOK();
	}

	public void testCuNestedVectors0() throws Exception {
		fLeaveUnconstrainedRaw= false;
		performCuOK();
	}

	public void testCuNestedVectors1() throws Exception {
		performCuOK();
	}

	public void testCuInferTypeVariable01() throws Exception {
		performCuOK();
	}

	public void testCuBoxing01() throws Exception {
		performCuOK();
	}

	public void testCuBoxing02() throws Exception {
		performCuOK();
	}

	public void testCuConstructor01() throws Exception {
		performCuOK();
	}

	public void testJUnit() throws Exception {
		fAssumeCloneReturnsSameType= false;
		fLeaveUnconstrainedRaw= true;
		IJavaProject javaProject= JavaProjectHelper.createJavaProject("InferTypeArguments", "bin");
		try {
			IPackageFragmentRoot jdk= JavaProjectHelper.addRTJar(javaProject);
			Assert.assertNotNull(jdk);

			File junitSrcArchive= JavaTestPlugin.getDefault().getFileInPlugin(JavaProjectHelper.JUNIT_SRC_381);
			Assert.assertTrue(junitSrcArchive != null && junitSrcArchive.exists());

			IPackageFragmentRoot src= JavaProjectHelper.addSourceContainerWithImport(javaProject, "src", junitSrcArchive, JavaProjectHelper.JUNIT_SRC_ENCODING);

			boolean performed= perform(new IJavaElement[] { javaProject }, RefactoringStatus.OK, RefactoringStatus.OK);
			assertTrue(performed);

			compareWithZipFile(src, "junit381-noUI-generified-src.zip");
		} finally {
			if (javaProject != null && javaProject.exists())
				JavaProjectHelper.delete(javaProject);
		}

	}

	public void testJUnitWithCloneNotRaw() throws Exception {
		fAssumeCloneReturnsSameType= true;
		fLeaveUnconstrainedRaw= false;

		IJavaProject javaProject= JavaProjectHelper.createJavaProject("InferTypeArguments", "bin");
		try {
			IPackageFragmentRoot jdk= JavaProjectHelper.addRTJar(javaProject);
			Assert.assertNotNull(jdk);

			File junitSrcArchive= JavaTestPlugin.getDefault().getFileInPlugin(JavaProjectHelper.JUNIT_SRC_381);
			Assert.assertTrue(junitSrcArchive != null && junitSrcArchive.exists());

			IPackageFragmentRoot src= JavaProjectHelper.addSourceContainerWithImport(javaProject, "src", junitSrcArchive, JavaProjectHelper.JUNIT_SRC_ENCODING);

			boolean performed= perform(new IJavaElement[] { javaProject }, RefactoringStatus.OK, RefactoringStatus.OK);
			assertTrue(performed);

			compareWithZipFile(src, "junit381-noUI-clone-not-raw-src.zip");
		} finally {
			if (javaProject != null && javaProject.exists())
				JavaProjectHelper.delete(javaProject);
		}

	}

	public void testCuTwoVectorElements() throws Exception {
		performCuOK();
	}

	public void testCuHalfPair() throws Exception {
		performCuOK();
	}

	public void testCuMethodAndTypeGeneric01() throws Exception {
		performCuOK();
	}

	public void testCuMethodAndTypeGeneric02() throws Exception {
		performCuOK();
	}

	public void testPairDance() throws Exception {
		createCUfromTestFile(getPackageP(), "Pair");
		createCUfromTestFile(getPackageP(), "InvertedPair");
		performCuOK();
		// deleted in tearDown
	}

	public void testCuAddString() throws Exception {
		performCuOK();
	}

	public void testCuAddString2() throws Exception {
		performCuOK();
	}

	public void testCuIntermediateLocal() throws Exception {
		performCuOK();
	}

	public void testCuSuperAndSub() throws Exception {
		performCuOK();
	}

	public void testCuCommonSuper() throws Exception {
		performCuOK();
	}

	public void testCuAddGetString() throws Exception {
		performCuOK();
	}

	public void testCuAddIntegerGetNumber() throws Exception {
		performCuOK();
	}

	public void testCuAddGetIterator() throws Exception {
		performCuOK();
	}

	public void testCuContains() throws Exception {
		performCuOK();
	}

	public void testCuMethodParam() throws Exception {
		performCuOK();
	}

	public void testCuMethodReturns() throws Exception {
		performCuOK();
	}

	public void testCuCollectionsMin() throws Exception {
		performCuOK();
	}

	public void testCuAddStringInteger() throws Exception {
		printTestDisabledMessage("currently, we don't follow flow through variables of type Object");
//		performCuOK(); //TODO
	}

	public void testCuAddStringIntegerA() throws Exception {
		performCuOK();
	}

	public void testCuInferFromCast() throws Exception {
		printTestDisabledMessage("not implemented yet");
//		performCuOK(); //TODO
	}

	public void testCuRippleMethods() throws Exception {
		performCuOK();
	}

	public void testCuRippleMethods2() throws Exception {
		performCuOK();
	}

	public void testCuCannotStringDouble() throws Exception {
		printTestDisabledMessage("not implemented yet");
//		performCuOK();
	}

	public void testCuRippleMethods3() throws Exception {
		printTestDisabledMessage("not implemented yet");
//		performCuOK();
	}

	public void testCuVarargs01() throws Exception {
		performCuOK();
	}

	public void testCuArrays01() throws Exception {
		performCuOK();
	}

	public void testCuArrays02() throws Exception {
		performCuOK();
	}

	public void testCuArrays03() throws Exception {
		performCuOK();
	}

	public void testCuArrays04() throws Exception {
		performCuOK();
	}

	public void testCuArrays05() throws Exception {
		if (! DETERMINE_ELEMENT_TYPE_FROM_CAST) {
			printTestDisabledMessage("DETERMINE_ELEMENT_TYPE_FROM_CAST");
			return;
		}
		performCuOK();
	}

	public void testCuArrays06() throws Exception {
		performCuOK();
	}

	public void testCuArrays07() throws Exception {
		performCuOK();
	}

	public void testCuToArray01() throws Exception {
		performCuOK();
	}

	public void testCuToArray02() throws Exception {
		performCuOK();
	}

	public void testCuMapEntry01() throws Exception {
		if (BUG_map_entrySet_iterator) {
			printTestDisabledMessage("BUG_map_entrySet_iterator");
			return;
		}
		performCuOK();
	}

	public void testCuFieldAccess01() throws Exception {
		performCuOK();
	}

	public void testCuFieldAccess02() throws Exception {
		performCuOK();
	}

	public void testCuMemberOfRaw() throws Exception {
		// regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=110594
		performCuOK();
	}

	public void testCuParameterizedTypes1() throws Exception {
		// regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=176742
		performCuOK();
	}

	public void testCuParameterizedTypes2() throws Exception {
		// regression test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=216627
		performCuOK();
	}
	
}
