import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

/**
 * Application demonstrating Swing fastness to toggle between a main form and a
 * split pane containing the main form and a properties form. The time can be
 * compared to the equivalent {@link SlowJavaFXDemo JavaFX demo}.
 */
public final class FastSwingDemo {

	private static final int COMPLEXITY = 50;

	public static void main(String[] args) {
		SwingUtilities.invokeLater(() -> {
			final JFrame frame = new JFrame("Fast Swing Demo");
			buildRoot(frame.getContentPane());
			frame.setSize(800, 600);
			frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
			frame.setVisible(true);
		});
	}

	private static void buildRoot(final Container borderPane) {
		final Box content = new Box(BoxLayout.Y_AXIS);
		final JScrollPane mainForm = new JScrollPane(content);
		final JPanel mainPane = new JPanel(new BorderLayout());
		mainPane.add(mainForm, BorderLayout.CENTER);

		final JLabel executionTime = new JLabel();

		final JToggleButton togglePropertiesForm = new JToggleButton("Show / Hide properties form");
		togglePropertiesForm.addActionListener(actionEvent -> {
			final long start = System.currentTimeMillis();
			if (togglePropertiesForm.isSelected()) {
				final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainForm,
						new JLabel("Very simple properties form"));
				mainPane.removeAll();
				mainPane.add(splitPane, BorderLayout.CENTER);
				mainPane.revalidate();
				executionTime.setText("Properties form opened in ");
			} else {
				mainPane.removeAll();
				mainPane.add(mainForm, BorderLayout.CENTER);
				mainPane.revalidate();
				executionTime.setText("Properties form closed in ");
			}
			SwingUtilities.invokeLater(() -> executionTime
					.setText(executionTime.getText() + (System.currentTimeMillis() - start) + " ms"));
		});

		final JToolBar toolBar = new JToolBar();
		toolBar.add(togglePropertiesForm);
		toolBar.add(executionTime);

		content.add(toolBar);
		content.add(createGridPane());

		borderPane.add(mainPane, BorderLayout.CENTER);
	}

	private static JPanel createGridPane() {
		final Box innerBox = new Box(BoxLayout.Y_AXIS);
		createOuterBoxes().forEach(innerBox::add);
		innerBox.validate();

		final GridLayout gridLayout = new GridLayout(0, 2);
		gridLayout.setHgap(5);

		final JPanel gridPane = new JPanel();
		gridPane.add(innerBox);
		gridPane.add(new Box(BoxLayout.Y_AXIS));
		return gridPane;
	}

	private static List<OuterBox> createOuterBoxes() {
		final List<OuterBox> outerBoxes = new ArrayList<>();
		for (int i = 0; i < COMPLEXITY; i++) {
			final OuterBox box = new OuterBox();
			outerBoxes.add(box);
			for (int j = 0; j < COMPLEXITY; j++) {
				box.addInnerBox(new InnerBox());
			}
		}
		return outerBoxes;
	}

	private static class OuterBox extends Box {

		private OuterBox() {
			super(BoxLayout.Y_AXIS);
			final JLabel label1 = new JLabel("Label1");
			final JLabel label2 = new JLabel("Label2");
			this.add(label1);
			this.add(label2);
		}

		private void addInnerBox(InnerBox box) {
			this.add(box);
		}
	}

	private static class InnerBox extends Box {

		private InnerBox() {
			super(BoxLayout.X_AXIS);
			for (int i = 0; i < 10; i++) {
				final JLabel label = new JLabel("Label[" + i + "]");
				label.setToolTipText("Tooltip");
				this.add(label);
			}
		}
	}
}