package de.ugoe.cs.eventbench.swing;

import java.awt.EventQueue;
import java.awt.Rectangle;

import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JScrollBar;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JList;
import java.util.List;
import javax.swing.ListSelectionModel;
import javax.swing.border.SoftBevelBorder;
import javax.swing.border.BevelBorder;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;

import de.ugoe.cs.eventbench.data.Event;

import javax.swing.JLabel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JScrollPane;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;

/**
 * <p>
 * This class provides a dialog to have a look on the events of the sequence,
 * that was selected in {@link DlgSequences}. Furthermore, assertions can be
 * added.
 * </p>
 * 
 * @author Jeffrey Hall
 * @version 1.0
 */
public class DlgSequenceDetails {

	private JFrame frmEvents;
	private JFrame frmParent;

	/**
	 * <p>
	 * Create the dialog.
	 * </p>
	 * 
	 * @param parent
	 *            the parent window of type {@link DlgSequences}.
	 * @param events
	 *            list of events that is to be displayed.
	 */
	public DlgSequenceDetails(JFrame parent, final List<Event<?>> events) {
		frmParent = parent;
		initialize(events);
	}

	/**
	 * <p>
	 * Launch the dialog.
	 * </p>
	 * 
	 * @param parent
	 *            the parent window of type {@link DlgSequences}.
	 * @param events
	 *            list of events that is to be displayed.
	 */
	public void showDialog(JFrame parent, final List<Event<?>> events) {
		frmParent = parent;

		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					DlgSequenceDetails window = new DlgSequenceDetails(
							frmParent, events);
					window.frmEvents.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * <p>
	 * Initialize the contents of the frame.
	 * </p>
	 * 
	 * @param events
	 *            list of events that is to be displayed.
	 */
	private void initialize(final List<Event<?>> events) {

		final DefaultListModel modelListEvents = new DefaultListModel();
		final DefaultListModel modelListTargets = new DefaultListModel();
		final JList listEvents = new JList(modelListEvents);
		final JList listTargets = new JList(modelListTargets);

		frmEvents = new JFrame();
		frmEvents.setTitle("Sequence details");
		frmEvents.setBounds(100, 100, 738, 597);
		frmEvents.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frmEvents.getContentPane().setLayout(null);

		// before closing the window, set parent to visible
		frmEvents.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent arg0) {
				frmParent.setVisible(true);
				frmEvents.dispose();
			}
		});

		JPanel panel = new JPanel();
		panel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
		panel.setBounds(10, 498, 705, 52);
		frmEvents.getContentPane().add(panel);
		panel.setLayout(null);

		final JButton btnInsertBefore = new JButton("Insert before");
		btnInsertBefore.setEnabled(false);
		btnInsertBefore.setBounds(10, 11, 135, 31);
		panel.add(btnInsertBefore);

		final JButton btnInsertAfter = new JButton("Insert after");
		btnInsertAfter.setEnabled(false);
		btnInsertAfter.setBounds(155, 11, 135, 31);
		panel.add(btnInsertAfter);

		updateLists(events, modelListEvents, modelListTargets, listEvents,
				listTargets);

		// listener for clicking the "Insert before" button
		btnInsertBefore.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent arg0) {
				if (!btnInsertBefore.isEnabled())
					return;

				addAssertion(events, modelListEvents, modelListTargets,
						listEvents, listTargets, true);
			}
		});

		// listener for clicking the "Insert after" button
		btnInsertAfter.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent arg0) {
				if (!btnInsertAfter.isEnabled())
					return;

				addAssertion(events, modelListEvents, modelListTargets,
						listEvents, listTargets, false);
			}
		});

		final JButton btnClose = new JButton("Back to sequences");
		btnClose.setBounds(544, 11, 150, 31);
		panel.add(btnClose);

		JPanel panel_1 = new JPanel();
		panel_1.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
		panel_1.setBounds(10, 11, 705, 476);
		frmEvents.getContentPane().add(panel_1);
		panel_1.setLayout(null);

		final JScrollPane scrollPaneEvents = new JScrollPane();
		scrollPaneEvents.setBounds(10, 29, 209, 436);
		panel_1.add(scrollPaneEvents);
		scrollPaneEvents.setViewportView(listEvents);

		listEvents.setBorder(new SoftBevelBorder(BevelBorder.LOWERED, null,
				null, null, null));
		listEvents.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		final JScrollPane scrollPaneTargets = new JScrollPane();
		scrollPaneTargets.setBounds(229, 29, 466, 436);
		panel_1.add(scrollPaneTargets);

		// if there are more events than the list is able to display without
		// vertical scrollbar, the event list has to be resized to fit to the
		// target list
		Rectangle r = scrollPaneEvents.getBounds();
		r.height -= 18;
		if (scrollPaneEvents.getVerticalScrollBar().isVisible()) {
			scrollPaneEvents.setBounds(r);
		}

		// listener for selecting a target: select the fitting event
		listTargets.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent arg0) {
				if (listTargets.getSelectedIndex() > -1) {

					changeSelection(listEvents, listTargets, btnInsertBefore,
							btnInsertAfter, scrollPaneEvents, scrollPaneTargets);
				}
			}
		});
		scrollPaneTargets.setViewportView(listTargets);

		listTargets.setBorder(new SoftBevelBorder(BevelBorder.LOWERED, null,
				null, null, null));
		listTargets.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		JLabel lblEvents = new JLabel("Events:");
		lblEvents.setBounds(10, 11, 46, 14);
		panel_1.add(lblEvents);

		JLabel lblTargets = new JLabel("Targets:");
		lblTargets.setBounds(229, 11, 58, 14);
		panel_1.add(lblTargets);

		// listener for selecting an event: select the fitting target
		listEvents.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent arg0) {
				if (listEvents.getSelectedIndex() > -1) {

					changeSelection(listTargets, listEvents, btnInsertBefore,
							btnInsertAfter, scrollPaneTargets, scrollPaneEvents);
				}
			}
		});

		// before closing the window, set parent to visible
		btnClose.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent arg0) {
				frmParent.setVisible(true);
				frmEvents.dispose();
			}
		});
	}

	/**
	 * <p>
	 * the value of one of the lists changed so the other has to be set to the
	 * fitting selection.
	 * </p>
	 * 
	 * @param listValueHasToBeChanged
	 *            the selection of this list has to be corrected.
	 * @param listValueHasBeenChanged
	 *            the selection of this list is already correct.
	 * @param btnInsertBefore
	 *            to enable the "Insert before" button.
	 * @param btnInsertAfter
	 *            to enable the "Insert after" button.
	 * @param scrollPaneValueHasToBeChanged
	 *            the position of the scrollBar of this scrollPane has to be
	 *            corrected.
	 * @param scrollPaneValueHasBeenChanged
	 *            the position of the scrollBar of this scrollPane is already
	 *            correct.
	 */
	private void changeSelection(final JList listValueHasToBeChanged,
			final JList listValueHasBeenChanged, final JButton btnInsertBefore,
			final JButton btnInsertAfter,
			final JScrollPane scrollPaneValueHasToBeChanged,
			final JScrollPane scrollPaneValueHasBeenChanged) {

		JScrollBar bar1 = scrollPaneValueHasBeenChanged.getVerticalScrollBar();
		JScrollBar bar2 = scrollPaneValueHasToBeChanged.getVerticalScrollBar();
		bar2.setValue(bar1.getValue());

		listValueHasToBeChanged.setSelectedIndex(listValueHasBeenChanged
				.getSelectedIndex());

		btnInsertBefore.setEnabled(true);
		btnInsertAfter.setEnabled(true);
	}

	/**
	 * <p>
	 * updates both lists, eventList and targetList to display the current
	 * status.
	 * </p>
	 * 
	 * @param events
	 *            list of the current events that has to be displayed.
	 * @param modelListEvents
	 *            DefaultListModel to display the events.
	 * @param modelListTargets
	 *            DefaultListModel to display the targets.
	 * @param listEvents
	 *            the listEvents to store and reset the selection.
	 * @param listTargets
	 *            the listTargets to reset the selection after updating.
	 */
	private void updateLists(final List<Event<?>> events,
			final javax.swing.DefaultListModel modelListEvents,
			final javax.swing.DefaultListModel modelListTargets,
			JList listEvents, JList listTargets) {

		int selectedIndex = listEvents.getSelectedIndex();

		modelListEvents.clear();
		modelListTargets.clear();
		for (int i = 0; i < events.size(); i++) {
			modelListEvents.addElement(events.get(i).getType());
			modelListTargets.addElement(events.get(i).getTarget());
		}

		if (selectedIndex > -1) {
			listEvents.setSelectedIndex(selectedIndex);
			listTargets.setSelectedIndex(selectedIndex);
		}
	}

	/**
	 * <p>
	 * opens {@link DlgInsert} dialog to insert an assertion and updates the
	 * lists.
	 * </p>
	 * 
	 * @param events
	 *            list of the current events.
	 * @param modelListEvents
	 *            needed to call updateList.
	 * @param modelListTargets
	 *            needed to call updateList.
	 * @param listEvents
	 *            needed to get the currently selected index and to update the
	 *            lists.
	 * @param listTargets
	 *            needed to update the lists.
	 * @param insertBefore
	 *            to decide if the assertions has to be inserted before or after
	 *            the current selection.
	 */
	private void addAssertion(final List<Event<?>> events,
			final DefaultListModel modelListEvents,
			final DefaultListModel modelListTargets, final JList listEvents,
			final JList listTargets, boolean insertBefore) {

		int selectedIndex = listEvents.getSelectedIndex();
		DlgInsert.showDialog(events, selectedIndex, insertBefore);
		updateLists(events, modelListEvents, modelListTargets, listEvents,
				listTargets);
	}
}
