/*******************************************************************************
 * Copyright (c) 2000, 2017 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.jface.tests.viewers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Widget;

public abstract class AbstractTreeViewerTest extends StructuredItemViewerTest {

	AbstractTreeViewer fTreeViewer;

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

	protected void assertEqualsArray(String s, Object[] a1, Object[] a2) {
		int s1 = a1.length;
		int s2 = a2.length;
		assertEquals(s, s1, s2);
		for (int i = 0; i < s1; i++) {
			assertEquals(s, a1[i], a2[i]);
		}
	}

	@Override
	protected void assertSelectionEquals(String message, TestElement expected) {
		IStructuredSelection selection = fViewer.getStructuredSelection();
		List<TestElement> expectedList = new ArrayList<>();
		expectedList.add(expected);
		assertEquals("selectionEquals - " + message, expectedList, selection.toList());
	}

	protected abstract int getItemCount(TestElement element); //was IElement

	public void testBulkExpand() {
		// navigate
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		TestElement last = fRootElement.getLastChild();

		// expand a few nodes
		fTreeViewer.expandToLevel(first, 2);
		fTreeViewer.expandToLevel(first2, 2);
		fTreeViewer.expandToLevel(last, 2);
		// get expand state
		Object[] list1 = fTreeViewer.getExpandedElements();
		// flush viewer
		setInput();
		processEvents();

		// restore old expand state
		fTreeViewer.collapseAll();
		fTreeViewer.expandToLevel(first, 2);
		fTreeViewer.expandToLevel(first2, 2);
		fTreeViewer.expandToLevel(last, 2);

		Object[] list2 = fTreeViewer.getExpandedElements();

		assertEqualsArray("old and new expand state are the same", list1, list2);
	}

	public void testDeleteChildExpanded() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		fTreeViewer.expandToLevel(first2, 0);

		assertNotNull("first child is visible", fViewer.testFindItem(first2));
		first.deleteChild(first2);
		assertNull("first child is not visible", fViewer.testFindItem(first2));
	}

	public void testDeleteChildren() {
		TestElement first = fRootElement.getFirstChild();
		first.deleteChildren();
		assertTrue("no children", getItemCount(first) == 0);
	}

	public void testDeleteChildrenExpanded() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		fTreeViewer.expandToLevel(first2, 0);
		assertNotNull("first child is visible", fViewer.testFindItem(first2));

		first.deleteChildren();
		assertTrue("no children", getItemCount(first) == 0);
	}

	public void testExpand() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		assertNull("first child is not visible", fViewer.testFindItem(first2));
		fTreeViewer.expandToLevel(first2, 0);
		assertNotNull("first child is visible", fViewer.testFindItem(first2));
	}

	public void testExpandElement() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		TestElement first3 = first2.getFirstChild();
		fTreeViewer.expandToLevel(first3, 0);
		assertNotNull("first3 is visible", fViewer.testFindItem(first3));
		assertNotNull("first2 is visible", fViewer.testFindItem(first2));
	}

	public void testExpandToLevel() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		TestElement first3 = first2.getFirstChild();
		fTreeViewer.expandToLevel(3);
		processEvents();
		assertNotNull("first2 is visible", fViewer.testFindItem(first2));
		assertNotNull("first3 is visible", fViewer.testFindItem(first3));
	}

	public void testFilterExpanded() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		fTreeViewer.expandToLevel(first2, 0);

		fTreeViewer.addFilter(new TestLabelFilter());
		assertTrue("filtered count", getItemCount() == 5);
	}

	public void testInsertChildReveal() {
		TestElement first = fRootElement.getFirstChild();
		TestElement newElement = first.addChild(TestModelChange.INSERT
				| TestModelChange.REVEAL);
		assertNotNull("new sibling is visible", fViewer
				.testFindItem(newElement));
	}

	public void testInsertChildRevealSelect() {
		TestElement last = fRootElement.getLastChild();
		TestElement newElement = last.addChild(TestModelChange.INSERT
				| TestModelChange.REVEAL | TestModelChange.SELECT);
		assertNotNull("new sibling is visible", fViewer
				.testFindItem(newElement));
		assertSelectionEquals("new element is selected", newElement);
	}

	public void testInsertChildRevealSelectExpanded() {
		TestElement first = fRootElement.getFirstChild();
		TestElement newElement = first.addChild(TestModelChange.INSERT
				| TestModelChange.REVEAL | TestModelChange.SELECT);
		assertNotNull("new sibling is visible", fViewer
				.testFindItem(newElement));
		assertSelectionEquals("new element is selected", newElement);
	}

	/**
	 * Regression test for 1GDN0PX: ITPUI:WIN2000 - SEVERE  - AssertionFailure when expanding Navigator
	 * Problem was:
	 *   - before addition, parent item had no children, and was expanded
	 *   - after addition, during refresh(), updatePlus() added dummy node even though parent item was expanded
	 *   - in updateChildren, it wasn't handling a dummy node
	 */
	public void testRefreshWithAddedChildren() {
		TestElement parent = fRootElement.addChild(TestModelChange.INSERT);
		TestElement child = parent.addChild(TestModelChange.INSERT);
		((AbstractTreeViewer) fViewer).setExpandedState(parent, true);
		parent.deleteChild(child);
		child = parent.addChild(TestModelChange.STRUCTURE_CHANGE);
		// On some platforms (namely GTK), removing all children causes the
		// parent to collapse (actually it's worse than that: GTK doesn't
		// allow there to be an empty expanded tree item, even if you do a
		// setExpanded(true)).
		// This behaviour makes it impossible to do this regression test.
		// See bug 40797 for more details. Because GTK 3 takes longer to
		// process, a wait statement is needed so that the assert will be done
		// correctly without failing.
		waitForJobs(300, 1000);
		processEvents();
		if (((AbstractTreeViewer) fViewer).getExpandedState(parent)) {
			assertNotNull("new child is visible", fViewer.testFindItem(child));
		}
	}

	/**
	 * Regression test for 1GBDB5A: ITPUI:WINNT - Exception in AbstractTreeViewer update.
	 * Problem was:
	 *   node has child A
	 *   node gets duplicate child A
	 *   viewer is refreshed rather than using add for new A
	 *   AbstractTreeViewer.updateChildren(...) was not properly handling it
	 */
	public void testRefreshWithDuplicateChild() {
		TestElement first = fRootElement.getFirstChild();
		TestElement newElement = (TestElement) first.clone();
		fRootElement.addChild(newElement, new TestModelChange(
				TestModelChange.STRUCTURE_CHANGE, fRootElement));
		assertNotNull("new sibling is visible", fViewer
				.testFindItem(newElement));
	}

	/**
	 * Regression test for Bug 3840 [Viewers] free expansion of jar happening when deleting projects (1GEV2FL)
	 * Problem was:
	 *   - node has children A and B
	 *   - A is expanded, B is not
	 *   - A gets deleted
	 *   - B gets expanded because it reused A's item
	 */
	public void testRefreshWithReusedItems() {
		//        TestElement a= fRootElement.getFirstChild();
		//        TestElement aa= a.getChildAt(0);
		//        TestElement ab= a.getChildAt(1);
		//        fTreeViewer.expandToLevel(aa, 1);
		//		List expandedBefore = Arrays.asList(fTreeViewer.getExpandedElements());
		//		assertTrue(expandedBefore.contains(a));
		//		assertTrue(expandedBefore.contains(aa));
		//		assertFalse(expandedBefore.contains(ab));
		//        a.deleteChild(aa, new TestModelChange(TestModelChange.STRUCTURE_CHANGE, a));
		//        List expandedAfter = Arrays.asList(fTreeViewer.getExpandedElements());
		//        assertFalse(expandedAfter.contains(ab));
	}

	public void testRenameChildElement() {
		TestElement first = fRootElement.getFirstChild();
		TestElement first2 = first.getFirstChild();
		fTreeViewer.expandToLevel(first2, 0);
		assertNotNull("first child is visible", fViewer.testFindItem(first2));

		String newLabel = first2.getLabel() + " changed";
		first2.setLabel(newLabel);
		Widget widget = fViewer.testFindItem(first2);
		assertTrue(widget instanceof Item);
		assertEquals("changed label", first2.getID() + " " + newLabel,
				((Item) widget).getText());
	}

	/**
	 * Regression test for Bug 26698 [Viewers] stack overflow during debug session, causing IDE to crash
	 * Problem was:
	 *   - node A has child A
	 *   - setExpanded with A in the list caused an infinite recursion
	 */
	public void testSetExpandedWithCycle() {
		TestElement first = fRootElement.getFirstChild();
		first.addChild(first, new TestModelChange(TestModelChange.INSERT,
				first, first));
		fTreeViewer.setExpandedElements(new Object[] { first });

	}

	/**
	 * Test for Bug 41710 - assertion that an object may not be added to a given
	 * TreeItem more than once.
	 */
	public void testSetDuplicateChild() {
		//Widget root = fViewer.testFindItem(fRootElement);
		//assertNotNull(root);
		TestElement parent = fRootElement.addChild(TestModelChange.INSERT);
		TestElement child = parent.addChild(TestModelChange.INSERT);
		int initialCount = getItemCount(parent);
		fRootElement.addChild(child, new TestModelChange(
				TestModelChange.INSERT, fRootElement, child));
		int postCount = getItemCount(parent);
		assertEquals("Same element added to a parent twice.", initialCount,
				postCount);
	}

	@Override
	public void tearDown() {
		super.tearDown();
		fTreeViewer = null;
	}
}
