/* File: ListPane.java
 * Author: Jason Gookins
 * Description: This class creates the pane that slides out from the left in the main window
 *              and displays lists of the nodes and their data.
 */

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Comparable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JEditorPane;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;

public class ListPane extends JPanel implements ActionListener
{
	/************************
	 ** Instance Variables **
	 ************************/

	private DefaultListModel nameList;
	private JList nameListDisplay;
	private DefaultListModel distTypeList;
	private JList distTypeListDisplay;
	private DefaultListModel priorityList;
	private JList priorityListDisplay;
	private DefaultListModel operationList;
	private JList operationListDisplay;
	private DefaultListModel changeList;
	private JList changeListDisplay;

	private ArrayList<JLabel> bins;
	private JLabel leftBoundLabel;
	private JLabel rightBoundLabel;
	private JLabel mutabilityLabel;
	private JEditorPane descriptionTextArea;



	/**********************
	 ** Main Constructor **
	 **********************/

	public ListPane(Belief belief)
	{
		setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		JPanel buttonPanel = new JPanel(null);

		JButton nameButton = new JButton("Name");
		nameButton.setBounds(0, 0, 140, 16);
		nameButton.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		nameButton.setActionCommand("nameButton");
		nameButton.addActionListener(this);
		buttonPanel.add(nameButton);

		JButton distTypeButton = new JButton("Type");
		distTypeButton.setBounds(140, 0, 41, 16);
		distTypeButton.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		distTypeButton.setToolTipText("Distribution Type");
		distTypeButton.setActionCommand("distTypeButton");
		distTypeButton.addActionListener(this);
		buttonPanel.add(distTypeButton);

		JButton priorityButton = new JButton("Pri.");
		priorityButton.setBounds(181, 0, 41, 16);
		priorityButton.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		priorityButton.setToolTipText("Priority");
		priorityButton.setActionCommand("priorityButton");
		priorityButton.addActionListener(this);
		buttonPanel.add(priorityButton);

		JButton operationButton = new JButton("Op.");
		operationButton.setBounds(222, 0, 41, 16);
		operationButton.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		operationButton.setToolTipText("Operation");
		operationButton.setActionCommand("operationButton");
		operationButton.addActionListener(this);
		buttonPanel.add(operationButton);

		JButton changeButton = new JButton("Chg.");
		changeButton.setBounds(263, 0, 41, 16);
		changeButton.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		changeButton.setToolTipText("Change");
		changeButton.setActionCommand("changeButton");
		changeButton.addActionListener(this);
		buttonPanel.add(changeButton);

		buttonPanel.setPreferredSize(new Dimension(310, 16));
		buttonPanel.setMinimumSize(new Dimension(310, 16));
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.fill = GridBagConstraints.BOTH;
		add(buttonPanel, c);

		JPanel listPanel = new JPanel(new GridBagLayout());
		GridBagConstraints d = new GridBagConstraints();

		nameList = new DefaultListModel();
		nameListDisplay = new JList(nameList);
		nameListDisplay.setFixedCellWidth(138);
		nameListDisplay.setFixedCellHeight(18);
		nameListDisplay.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		nameListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		nameListDisplay.setCellRenderer(new NameListCellRenderer());
		nameListDisplay.addListSelectionListener(belief.getBeliefListener());
		d.gridx = 0;
		d.gridy = 0;
		d.gridwidth = 1;
		d.gridheight = 1;
		d.weighty = 1.0;
		d.weightx = 0.456;
		d.fill = GridBagConstraints.BOTH;
		listPanel.add(nameListDisplay, d);

		distTypeList = new DefaultListModel();
		distTypeListDisplay = new JList(distTypeList);
		distTypeListDisplay.setFixedCellHeight(18);
		distTypeListDisplay.setFixedCellWidth(40);
		distTypeListDisplay.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		distTypeListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		DefaultListCellRenderer distTypeListRenderer = (DefaultListCellRenderer)distTypeListDisplay.getCellRenderer();
		distTypeListRenderer.setHorizontalAlignment(JLabel.CENTER);
		distTypeListDisplay.addListSelectionListener(belief.getBeliefListener());
		d.gridx = 1;
		d.weightx = 0.136;
		listPanel.add(distTypeListDisplay, d);

		priorityList = new DefaultListModel();
		priorityListDisplay = new JList(priorityList);
		priorityListDisplay.setFixedCellHeight(18);
		priorityListDisplay.setFixedCellWidth(40);
		priorityListDisplay.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		priorityListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		DefaultListCellRenderer priorityListRenderer = (DefaultListCellRenderer)priorityListDisplay.getCellRenderer();
		priorityListRenderer.setHorizontalAlignment(JLabel.CENTER);
		priorityListDisplay.addListSelectionListener(belief.getBeliefListener());
		d.gridx = 2;
		d.weightx = 0.136;
		listPanel.add(priorityListDisplay, d);

		operationList = new DefaultListModel();
		operationListDisplay = new JList(operationList);
		operationListDisplay.setFixedCellHeight(18);
		operationListDisplay.setFixedCellWidth(40);
		operationListDisplay.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
		operationListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		DefaultListCellRenderer operationListRenderer = (DefaultListCellRenderer)operationListDisplay.getCellRenderer();
		operationListRenderer.setHorizontalAlignment(JLabel.CENTER);
		operationListDisplay.addListSelectionListener(belief.getBeliefListener());
		d.gridx = 3;
		d.weightx = 0.136;
		listPanel.add(operationListDisplay, d);

		changeList = new DefaultListModel();
		changeListDisplay = new JList(changeList);
		changeListDisplay.setFixedCellHeight(18);
		changeListDisplay.setFixedCellWidth(40);
		changeListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		changeListDisplay.setCellRenderer(new ChangeListCellRenderer());
		changeListDisplay.addListSelectionListener(belief.getBeliefListener());
		d.gridx = 4;
		d.weightx = 0.131;
		listPanel.add(changeListDisplay, d);

		JScrollPane listScrollPane = new JScrollPane(listPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		c.gridy = 1;
		c.weightx = 1.0;
		c.weighty = 1.0;
		add(listScrollPane, c);

		JTabbedPane dataPanel = new JTabbedPane();
		dataPanel.setPreferredSize(new Dimension(300, 150));
		dataPanel.setMinimumSize(new Dimension(300, 150));

		JPanel distributionPanel = new JPanel(null);

		bins = new ArrayList<JLabel>();
		for (int i = 0; i < 10; i++)
		{
			JLabel bin = new JLabel();
			bin.setBounds(12 + (i * 29), 17, 30, 30);
			bin.setOpaque(true);
			bin.setBackground(Color.WHITE);
			bin.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			bin.setHorizontalAlignment(JLabel.CENTER);
			distributionPanel.add(bin);
			bins.add(bin);
		}

		JLabel leftBoundLine = new JLabel();
		leftBoundLine.setBounds(12, 47, 1, 30);
		leftBoundLine.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.BLACK));
		distributionPanel.add(leftBoundLine);
		JLabel rightBoundLine = new JLabel();
		rightBoundLine.setBounds(302, 47, 1, 30);
		rightBoundLine.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.BLACK));
		distributionPanel.add(rightBoundLine);

		leftBoundLabel = new JLabel();
		leftBoundLabel.setBounds(12, 77, 30, 30);
		leftBoundLabel.setOpaque(true);
		leftBoundLabel.setBackground(Color.WHITE);
		leftBoundLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		leftBoundLabel.setHorizontalAlignment(JLabel.CENTER);
		distributionPanel.add(leftBoundLabel);
		rightBoundLabel = new JLabel();
		rightBoundLabel.setBounds(273, 77, 30, 30);
		rightBoundLabel.setOpaque(true);
		rightBoundLabel.setBackground(Color.WHITE);
		rightBoundLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		rightBoundLabel.setHorizontalAlignment(JLabel.CENTER);
		distributionPanel.add(rightBoundLabel);

		JLabel minimumLabel = new JLabel(" << Min", JLabel.LEFT);
		minimumLabel.setBounds(42, 78, 77, 30);
		distributionPanel.add(minimumLabel);
		mutabilityLabel = new JLabel();
		mutabilityLabel.setBounds(120, 78, 77, 30);
		mutabilityLabel.setHorizontalAlignment(JLabel.CENTER);
		distributionPanel.add(mutabilityLabel);
		JLabel maximumLabel = new JLabel("Max >> ", JLabel.RIGHT);
		maximumLabel.setBounds(196, 78, 77, 30);
		distributionPanel.add(maximumLabel);

		JPanel descriptionPanel = new JPanel(null);

		descriptionTextArea = new JEditorPane();
		descriptionTextArea.setContentType("text/html");
		descriptionTextArea.setBorder(BorderFactory.createEmptyBorder(1, 3, 3, 3));
		descriptionTextArea.setEditable(false);

		JScrollPane descriptionScrollPane = new JScrollPane(descriptionTextArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		descriptionScrollPane.setBounds(4, 5, 307, 114);
		descriptionPanel.add(descriptionScrollPane);

		dataPanel.addTab("Distribution", distributionPanel);
		dataPanel.addTab("Description", descriptionPanel);
		c.gridy = 2;
		c.weightx = 0.0;
		c.weighty = 0.0;
		add(dataPanel, c);
	}



	/***************
	 ** Accessors **
	 ***************/

	public DefaultListModel getNameList()
	{
		return this.nameList;
	}

	public JList getNameListDisplay()
	{
		return this.nameListDisplay;
	}

	public DefaultListModel getDistTypeList()
	{
		return this.distTypeList;
	}

	public JList getDistTypeListDisplay()
	{
		return this.distTypeListDisplay;
	}

	public DefaultListModel getPriorityList()
	{
		return this.priorityList;
	}

	public JList getPriorityListDisplay()
	{
		return this.priorityListDisplay;
	}

	public DefaultListModel getOperationList()
	{
		return this.operationList;
	}

	public JList getOperationListDisplay()
	{
		return this.operationListDisplay;
	}

	public DefaultListModel getChangeList()
	{
		return this.changeList;
	}

	public JList getChangeListDisplay()
	{
		return this.changeListDisplay;
	}

	public JEditorPane getDescriptionTextArea()
	{
		return this.descriptionTextArea;
	}



	/*******************
	 ** Action Events **
	 *******************/

	public void actionPerformed(ActionEvent evt)
	{
		if (evt.getActionCommand().equals("nameButton"))
		{
			sortNameList();
		}
		else if (evt.getActionCommand().equals("distTypeButton"))
		{
			sortDistTypeList();
		}
		else if (evt.getActionCommand().equals("priorityButton"))
		{
			sortPriorityList();
		}
		else if (evt.getActionCommand().equals("operationButton"))
		{
			sortOperationList();
		}
		else if (evt.getActionCommand().equals("changeButton"))
		{
			sortChangeList();
		}
	}



	/**************************
	 ** List Sorting Methods **
	 **************************/

	public void sortNameList()
	{
		sortLists(0);
	}

	public void sortDistTypeList()
	{
		sortLists(1);
	}

	public void sortPriorityList()
	{
		sortLists(2);
	}

	public void sortOperationList()
	{
		sortLists(3);
	}

	public void sortChangeList()
	{
		sortLists(4);
	}

	private void sortLists(int column)
	{
		ArrayList<ArrayList<String>> modelsData = readModelsIntoList();

		Collections.sort(modelsData, new ListComparator(column));

		readListIntoModels(modelsData);
	}

	private ArrayList<ArrayList<String>> readModelsIntoList()
	{
		ArrayList<ArrayList<String>> modelsData = new ArrayList<ArrayList<String>>();

		for (int i = 0; i < nameList.size(); i++)
		{
			ArrayList<String> row = new ArrayList<String>();

			row.add(nameList.get(i).toString());
			row.add(distTypeList.get(i).toString());
			row.add(priorityList.get(i).toString());
			row.add(operationList.get(i).toString());
			row.add(changeList.get(i).toString());

			modelsData.add(row);
		}

		return modelsData;
	}

	private void readListIntoModels(ArrayList<ArrayList<String>> modelData)
	{
		for (int i = 0; i < modelData.size(); i++)
		{
			nameList.set(i, modelData.get(i).get(0));
			distTypeList.set(i, modelData.get(i).get(1));
			priorityList.set(i, modelData.get(i).get(2));
			operationList.set(i, modelData.get(i).get(3));
			changeList.set(i, modelData.get(i).get(4));
		}
	}



	/*******************************
	 ** List Population/Selection **
	 *******************************/

	public void addNodeToLists(Node node)
	{
		nameList.addElement(node.getName());
		if (node.getHasGiven() == 1)
		{
			distTypeList.addElement("Given");
		}
		else if (node.getHasGoal() == 1)
		{
			distTypeList.addElement("Goal");
		}
		else
		{
			distTypeList.addElement("N/A");
		}
		priorityList.addElement(node.getPriority());
		operationList.addElement(node.getOperation());
		changeList.addElement("0%");
	}

	public void updateNodeInLists(String nodeName, Node node)
	{
		int index = nameList.indexOf(nodeName);

		nameList.set(index, node.getName());
		if (node.getHasGiven() == 1)
		{
			distTypeList.set(index, "Given");
		}
		else if (node.getHasGoal() == 1)
		{
			distTypeList.set(index, "Goal");
		}
		else
		{
			distTypeList.set(index, "N/A");
		}
		priorityList.set(index, node.getPriority());
		operationList.set(index, node.getOperation());
		changeList.set(index, node.getChange() + "%");
	}

	public void removeNodeFromLists(Node node)
	{
		int index = nameList.indexOf(node.getName());

		nameList.remove(index);
		distTypeList.remove(index);
		priorityList.remove(index);
		operationList.remove(index);
		changeList.remove(index);
	}

	public void selectListCells(int index)
	{
		nameListDisplay.setSelectedIndex(index);
		distTypeListDisplay.setSelectedIndex(index);
		priorityListDisplay.setSelectedIndex(index);
		operationListDisplay.setSelectedIndex(index);
		changeListDisplay.setSelectedIndex(index);
	}

	public void deselectListCells()
	{
		nameListDisplay.removeSelectionInterval(nameListDisplay.getMinSelectionIndex(), nameListDisplay.getMaxSelectionIndex());
		distTypeListDisplay.removeSelectionInterval(distTypeListDisplay.getMinSelectionIndex(), distTypeListDisplay.getMaxSelectionIndex());
		priorityListDisplay.removeSelectionInterval(priorityListDisplay.getMinSelectionIndex(), priorityListDisplay.getMaxSelectionIndex());
		operationListDisplay.removeSelectionInterval(operationListDisplay.getMinSelectionIndex(), operationListDisplay.getMaxSelectionIndex());
		changeListDisplay.removeSelectionInterval(changeListDisplay.getMinSelectionIndex(), changeListDisplay.getMaxSelectionIndex());
	}

	public void displayDistributionData(ArrayList<Integer> distribution, int minimum, int maximum, String mutability)
	{
		for (int i = 0; i < 10; i++)
		{
			bins.get(i).setText(Integer.toString(distribution.get(i)));
		}

		leftBoundLabel.setText(Integer.toString(minimum));
		rightBoundLabel.setText(Integer.toString(maximum));

		if (mutability != null)
		{
			if (mutability.equals("opt"))
			{
				mutabilityLabel.setText("Mutable");
			}
			else
			{
				mutabilityLabel.setText("Fixed");
			}
		}
	}

	public void clearDistributionData()
	{
		for (JLabel bin : bins)
		{
			bin.setText(null);
		}

		leftBoundLabel.setText(null);
		rightBoundLabel.setText(null);
		mutabilityLabel.setText(null);
	}

	public void clearLists()
	{
		nameList.clear();
		distTypeList.clear();
		priorityList.clear();
		operationList.clear();
		changeList.clear();
	}



	/**********************
	 ** Internal Classes **
	 **********************/

	private class NameListCellRenderer extends JLabel implements ListCellRenderer
	{
		public NameListCellRenderer()
		{
			super();
		}

		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
		{
			String s = value.toString();

			setText(s);

			if (isSelected)
			{
				setBackground(list.getSelectionBackground());
				setForeground(list.getSelectionForeground());
				setBorder(BorderFactory.createMatteBorder(0, 5, 0, 0, new Color(184, 207, 229)));
			}
			else
			{
				setBackground(list.getBackground());
				setForeground(list.getForeground());
				setBorder(BorderFactory.createMatteBorder(0, 5, 0, 0, Color.WHITE));
			}

			setEnabled(list.isEnabled());
			setFont(list.getFont());
			setOpaque(true);

			return this;
		}		
	}

	private class ChangeListCellRenderer extends JLabel implements ListCellRenderer
	{
		public ChangeListCellRenderer()
		{
			super();
		}

		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
		{
			String s = value.toString();

			setText(s);

			setBackground(isSelected ? list.getSelectionBackground() : list.getBackground());

			if (!s.equals("0%"))
			{
				if (distTypeList.getElementAt(index).equals("Goal"))
				{
					setForeground(new Color(0.5f + (Float.parseFloat(s.substring(0, s.indexOf("%"))) / 200), 0.0f, 0.0f));
				}
				else if (distTypeList.getElementAt(index).equals("Given"))
				{
					setForeground(new Color(0.0f, 0.5f + (Float.parseFloat(s.substring(0, s.indexOf("%"))) / 200), 0.0f));
				}
			}
			else
			{
				setForeground(list.getSelectionForeground());
			}

			setEnabled(list.isEnabled());
			setFont(list.getFont());
			setHorizontalAlignment(JLabel.CENTER);
			setOpaque(true);

			return this;
		}
	}

	private class ListComparator implements Comparator<ArrayList<String>>
	{
		private int sortIndex;
		private boolean sortIndexAsc;

		public ListComparator(int sortIndex)
		{
			this.sortIndex = sortIndex;

			if (sortIndex == 2 || sortIndex == 4)
			{
				sortIndexAsc = false;
			}
			else
			{
				sortIndexAsc = true;
			}
		}

		public int compare(ArrayList<String> x, ArrayList<String> y)
		{
			int result = helperCompare(x.get(sortIndex), y.get(sortIndex), sortIndexAsc);

			return result;
		}

		public int helperCompare(String x, String y, boolean ascending)
		{
			return ascending ? x.compareToIgnoreCase(y) : y.compareToIgnoreCase(x);
		}
	}
}