package junit.swingui;

/*-
 * #%L
 * org.eclipse.jdt.ui.junit.sampleproject
 * %%
 * Copyright (C) 2020 Eclipse Foundation
 * %%
 * This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * #L%
 */

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import junit.runner.*;

/**
 * A test class selector. A simple dialog to pick the name of a test suite.
 */
class TestSelector extends JDialog {
	private JButton fCancel;
	private JButton fOk;
	private JList fList;
	private JScrollPane fScrolledList;
	private JLabel fDescription;
	private String fSelectedItem;

	/**
	 * Renders TestFailures in a JList
	 */
	static class TestCellRenderer extends DefaultListCellRenderer {
		Icon fLeafIcon;
		Icon fSuiteIcon;

		public TestCellRenderer() {
			fLeafIcon = UIManager.getIcon("Tree.leafIcon");
			fSuiteIcon = UIManager.getIcon("Tree.closedIcon");
		}

		public Component getListCellRendererComponent(JList list, Object value, int modelIndex, boolean isSelected,
				boolean cellHasFocus) {
			Component c = super.getListCellRendererComponent(list, value, modelIndex, isSelected, cellHasFocus);
			String displayString = displayString((String) value);

			if (displayString.startsWith("AllTests"))
				setIcon(fSuiteIcon);
			else
				setIcon(fLeafIcon);

			setText(displayString);
			return c;
		}

		public static String displayString(String className) {
			int typeIndex = className.lastIndexOf('.');
			if (typeIndex < 0)
				return className;
			return className.substring(typeIndex + 1) + " - " + className.substring(0, typeIndex);
		}

		public static boolean matchesKey(String s, char ch) {
			return ch == Character.toUpperCase(s.charAt(typeIndex(s)));
		}

		private static int typeIndex(String s) {
			int typeIndex = s.lastIndexOf('.');
			int i = 0;
			if (typeIndex > 0)
				i = typeIndex + 1;
			return i;
		}
	}

	protected class DoubleClickListener extends MouseAdapter {
		public void mouseClicked(MouseEvent e) {
			if (e.getClickCount() == 2) {
				okSelected();
			}
		}
	}

	protected class KeySelectListener extends KeyAdapter {
		public void keyTyped(KeyEvent e) {
			keySelectTestClass(e.getKeyChar());
		}
	}

	public TestSelector(Frame parent, TestCollector testCollector) {
		super(parent, true);
		setSize(350, 300);
		setResizable(false);
		// setLocationRelativeTo only exists in 1.4
		try {
			setLocationRelativeTo(parent);
		} catch (NoSuchMethodError e) {
			centerWindow(this);
		}
		setTitle("Test Selector");

		Vector list = null;
		try {
			parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			list = createTestList(testCollector);
		} finally {
			parent.setCursor(Cursor.getDefaultCursor());
		}
		fList = new JList(list);
		fList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		fList.setCellRenderer(new TestCellRenderer());
		fScrolledList = new JScrollPane(fList);

		fCancel = new JButton("Cancel");
		fDescription = new JLabel("Select the Test class:");
		fOk = new JButton("OK");
		fOk.setEnabled(false);
		getRootPane().setDefaultButton(fOk);

		defineLayout();
		addListeners();
	}

	public static void centerWindow(Component c) {
		Dimension paneSize = c.getSize();
		Dimension screenSize = c.getToolkit().getScreenSize();
		c.setLocation((screenSize.width - paneSize.width) / 2, (screenSize.height - paneSize.height) / 2);
	}

	private void addListeners() {
		fCancel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				dispose();
			}
		});

		fOk.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				okSelected();
			}
		});

		fList.addMouseListener(new DoubleClickListener());
		fList.addKeyListener(new KeySelectListener());
		fList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				checkEnableOK(e);
			}
		});

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				dispose();
			}
		});
	}

	private void defineLayout() {
		getContentPane().setLayout(new GridBagLayout());
		GridBagConstraints labelConstraints = new GridBagConstraints();
		labelConstraints.gridx = 0;
		labelConstraints.gridy = 0;
		labelConstraints.gridwidth = 1;
		labelConstraints.gridheight = 1;
		labelConstraints.fill = GridBagConstraints.BOTH;
		labelConstraints.anchor = GridBagConstraints.WEST;
		labelConstraints.weightx = 1.0;
		labelConstraints.weighty = 0.0;
		labelConstraints.insets = new Insets(8, 8, 0, 8);
		getContentPane().add(fDescription, labelConstraints);

		GridBagConstraints listConstraints = new GridBagConstraints();
		listConstraints.gridx = 0;
		listConstraints.gridy = 1;
		listConstraints.gridwidth = 4;
		listConstraints.gridheight = 1;
		listConstraints.fill = GridBagConstraints.BOTH;
		listConstraints.anchor = GridBagConstraints.CENTER;
		listConstraints.weightx = 1.0;
		listConstraints.weighty = 1.0;
		listConstraints.insets = new Insets(8, 8, 8, 8);
		getContentPane().add(fScrolledList, listConstraints);

		GridBagConstraints okConstraints = new GridBagConstraints();
		okConstraints.gridx = 2;
		okConstraints.gridy = 2;
		okConstraints.gridwidth = 1;
		okConstraints.gridheight = 1;
		okConstraints.anchor = java.awt.GridBagConstraints.EAST;
		okConstraints.insets = new Insets(0, 8, 8, 8);
		getContentPane().add(fOk, okConstraints);

		GridBagConstraints cancelConstraints = new GridBagConstraints();
		cancelConstraints.gridx = 3;
		cancelConstraints.gridy = 2;
		cancelConstraints.gridwidth = 1;
		cancelConstraints.gridheight = 1;
		cancelConstraints.anchor = java.awt.GridBagConstraints.EAST;
		cancelConstraints.insets = new Insets(0, 8, 8, 8);
		getContentPane().add(fCancel, cancelConstraints);
	}

	public void checkEnableOK(ListSelectionEvent e) {
		fOk.setEnabled(fList.getSelectedIndex() != -1);
	}

	public void okSelected() {
		fSelectedItem = (String) fList.getSelectedValue();
		dispose();
	}

	public boolean isEmpty() {
		return fList.getModel().getSize() == 0;
	}

	public void keySelectTestClass(char ch) {
		ListModel model = fList.getModel();
		if (!Character.isJavaIdentifierStart(ch))
			return;
		for (int i = 0; i < model.getSize(); i++) {
			String s = (String) model.getElementAt(i);
			if (TestCellRenderer.matchesKey(s, Character.toUpperCase(ch))) {
				fList.setSelectedIndex(i);
				fList.ensureIndexIsVisible(i);
				return;
			}
		}
		Toolkit.getDefaultToolkit().beep();
	}

	public String getSelectedItem() {
		return fSelectedItem;
	}

	private Vector createTestList(TestCollector collector) {
		Enumeration each = collector.collectTests();
		Vector v = new Vector(200);
		Vector displayVector = new Vector(v.size());
		while (each.hasMoreElements()) {
			String s = (String) each.nextElement();
			v.addElement(s);
			displayVector.addElement(TestCellRenderer.displayString(s));
		}
		if (v.size() > 0)
			Sorter.sortStrings(displayVector, 0, displayVector.size() - 1, new ParallelSwapper(v));
		return v;
	}

	private class ParallelSwapper implements Sorter.Swapper {
		Vector fOther;

		ParallelSwapper(Vector other) {
			fOther = other;
		}

		public void swap(Vector values, int left, int right) {
			Object tmp = values.elementAt(left);
			values.setElementAt(values.elementAt(right), left);
			values.setElementAt(tmp, right);
			Object tmp2 = fOther.elementAt(left);
			fOther.setElementAt(fOther.elementAt(right), left);
			fOther.setElementAt(tmp2, right);
		}
	}
}
