/* $Id: SpreadSelectionListsInTable.java 623 2013-01-10 12:40:07Z ohura $ */
/*
Definitive Guide to Swing for Java 2, Second Edition
By John Zukowski     
ISBN: 1-893115-78-X
Publisher: APress
 */
package smart_gs.spread_selection;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

import smart_gs.logical.SpreadSelectionList;
import smart_gs.logical.SpreadSelectionListManager;
import smart_gs.swingui.WorkspaceWindow;

public class SpreadSelectionListsInTable {
	
	private CustomTableModel model;
	private JTable table;		
	private Vector keys = new Vector();
	private Vector values = new Vector();
	private Vector switches = new Vector();
	private JButton SSLTreeFramesShowButton;
	private JTextField scopefield;
	boolean calledFromSearchPanel;
	
	public SpreadSelectionListsInTable (JButton bttn){
		SSLTreeFramesShowButton = bttn;
		calledFromSearchPanel = false;
	}
	
	public SpreadSelectionListsInTable (JButton bttn, JTextField txtfld){
		SSLTreeFramesShowButton = bttn;
		scopefield = txtfld;
		calledFromSearchPanel = true;
	}
	
	class CustomTableModel extends AbstractTableModel {

		
		private final String columnNames[] = { "Name", "Selection List", "" };

		public int getColumnCount() {
			return columnNames.length;
		}

		public String getColumnName(int column) {
			return columnNames[column];
		}

		public int getRowCount() {
			return keys.size();
		}

		public Object getValueAt(int row, int column) {
			Object returnValue = null;
			if (column == 0) {
				returnValue = keys.elementAt(row);
			} else if (column == 1) {
				returnValue = values.elementAt(row);
			} else if (column == 2){
				returnValue = switches.elementAt(row);
			}		
			return returnValue;
		}

		public void setValueAt(Object value, int row, int column) {
			if (column == 2) switches.set(row, value);
		}
		

		public synchronized void spreadSelectionListsUpdate() {
			Enumeration<SpreadSelectionList> newSpreadSelectionLists =  Collections.enumeration(SpreadSelectionListManager.getInstance().getSpreadSelectionLists());
			keys.removeAllElements();
			values.removeAllElements();
			switches.removeAllElements();

			while (newSpreadSelectionLists.hasMoreElements()) {
				SpreadSelectionList next = newSpreadSelectionLists.nextElement();
				keys.addElement(next.getName());
				values.addElement(next.getSpreadSelectionListSpec());
				switches.addElement(new Boolean(false));
			}

			fireTableDataChanged();
			WorkspaceWindow.setUpdated(true);
		}
	}

	private class ShowFrameListener extends WindowAdapter {
		public void windowClosing(WindowEvent e){
			SSLTreeFramesShowButton.setEnabled(true);
		}
	}

	
	public void show (){
		final JFrame frame = new JFrame("Selection Lists");
	    frame.setResizable(true);
	    frame.addWindowListener(new ShowFrameListener());

		model = new CustomTableModel();
		model.spreadSelectionListsUpdate();
		TableSorter sorter = new TableSorter(model);

		table = new JTable(sorter);
		TableHeaderSorter.install(sorter, table);		
	    TableColumn name_col, spec_col, checkbox_col;
	    name_col = table.getColumnModel().getColumn(0);
	    spec_col = table.getColumnModel().getColumn(1);
	    checkbox_col = table.getColumnModel().getColumn(2);
	    name_col.setPreferredWidth(100);
	    spec_col.setPreferredWidth(200);
	    checkbox_col.setPreferredWidth(10);

		
		Container content = frame.getContentPane();
		JScrollPane scrollPane = new JScrollPane(table);
		content.add(scrollPane, BorderLayout.CENTER);
		
		JPanel button_panel = new JPanel();
		if (calledFromSearchPanel) {
			JButton load = new JButton("Load");
			load.addActionListener(new GUILoadSpreadSelectionListAction());
			button_panel.add(load);
		}
		JButton delete = new JButton("Delete");
		delete.addActionListener(new GUIDeleteSpreadSelectionListAction());
		button_panel.add(delete);
		frame.getContentPane().add(button_panel, BorderLayout.SOUTH);
		frame.setSize(400, 400);
		frame.setVisible(true);
		
		
		
	}
	
	private class GUILoadSpreadSelectionListAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			String scope_spec = "";
			int size = keys.size();
			SpreadSelectionListManager SSLM = SpreadSelectionListManager.getInstance();
			for (int i = size-1; i>=0 ;i--){
				if ( ((Boolean)switches.get(i)).booleanValue() ) {
					scope_spec = 
						scope_spec 
						+ SSLM.getSpreadSelectionListString((String)keys.get(i))
						+ ",";
				}
			}

			int length = scope_spec.length();
			if (scope_spec.charAt(length-1) == ',') {
				scope_spec = scope_spec.substring(0, length-1);
			}
			
			scopefield.setText(scope_spec);
		}

	}
	
	private class GUIDeleteSpreadSelectionListAction implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			int size = keys.size();
			SpreadSelectionListManager SSLM = SpreadSelectionListManager.getInstance();
			for (int i = 0; i<size;i++){
				if ( ((Boolean)switches.get(i)).booleanValue() ) {
					SSLM.deleteSpreadSelectionList((String)keys.get(i));
				}
			}
			model.spreadSelectionListsUpdate();
			model.fireTableStructureChanged();
			table.repaint();
		}

	}
}

class TableSorter extends TableMap implements TableModelListener {
	int indexes[] = new int[0];

	Vector sortingColumns = new Vector();

	boolean ascending = true;

	public TableSorter() {
	}

	public TableSorter(TableModel model) {
		setModel(model);
	}

	public void setModel(TableModel model) {
		super.setModel(model);
		reallocateIndexes();
		sortByColumn(0);
		fireTableDataChanged();
	}

	public int compareRowsByColumn(int row1, int row2, int column) {
		Class type = this.getColumnClass(column);
		TableModel data = model;

		// Check for nulls

		Object o1 = data.getValueAt(row1, column);
		Object o2 = data.getValueAt(row2, column);

		// If both values are null return 0
		if (o1 == null && o2 == null) {
			return 0;
		} else if (o1 == null) { // Define null less than everything.
			return -1;
		} else if (o2 == null) {
			return 1;
		}

		if (type.getSuperclass() == Number.class) {
			Number n1 = (Number) data.getValueAt(row1, column);
			double d1 = n1.doubleValue();
			Number n2 = (Number) data.getValueAt(row2, column);
			double d2 = n2.doubleValue();

			if (d1 < d2)
				return -1;
			else if (d1 > d2)
				return 1;
			else
				return 0;
		} else if (type == String.class) {
			String s1 = (String) data.getValueAt(row1, column);
			String s2 = (String) data.getValueAt(row2, column);
			int result = s1.compareTo(s2);

			if (result < 0)
				return -1;
			else if (result > 0)
				return 1;
			else
				return 0;
		} else if (type == java.util.Date.class) {
			Date d1 = (Date) data.getValueAt(row1, column);
			long n1 = d1.getTime();
			Date d2 = (Date) data.getValueAt(row2, column);
			long n2 = d2.getTime();

			if (n1 < n2)
				return -1;
			else if (n1 > n2)
				return 1;
			else
				return 0;
		} else if (type == Boolean.class) {
			Boolean bool1 = (Boolean) data.getValueAt(row1, column);
			boolean b1 = bool1.booleanValue();
			Boolean bool2 = (Boolean) data.getValueAt(row2, column);
			boolean b2 = bool2.booleanValue();

			if (b1 == b2)
				return 0;
			else if (b1) // Define false < true
			return 1;
			else
				return -1;
		} else {
			Object v1 = data.getValueAt(row1, column);
			String s1 = v1.toString();
			Object v2 = data.getValueAt(row2, column);
			String s2 = v2.toString();
			int result = s1.compareTo(s2);

			if (result < 0)
				return -1;
			else if (result > 0)
				return 1;
			else
				return 0;
		}
	}

	public int compare(int row1, int row2) {
		for (int level = 0, n = sortingColumns.size(); level < n; level++) {
			Integer column = (Integer) sortingColumns.elementAt(level);
			int result = compareRowsByColumn(row1, row2, column.intValue());
			if (result != 0) {
				return (ascending ? result : -result);
			}
		}
		return 0;
	}

	public void reallocateIndexes() {
		int rowCount = model.getRowCount();
		indexes = new int[rowCount];
		for (int row = 0; row < rowCount; row++) {
			indexes[row] = row;
		}
	}

	public void tableChanged(TableModelEvent tableModelEvent) {
		super.tableChanged(tableModelEvent);
		reallocateIndexes();
		sortByColumn(0);
		fireTableStructureChanged();
	}

	public void checkModel() {
		if (indexes.length != model.getRowCount()) {
			System.err.println("Sorter not informed of a change in model.");
		}
	}

	public void sort() {
		checkModel();
		shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
		fireTableDataChanged();
	}

	public void shuttlesort(int from[], int to[], int low, int high) {
		if (high - low < 2) {
			return;
		}
		int middle = (low + high) / 2;
		shuttlesort(to, from, low, middle);
		shuttlesort(to, from, middle, high);

		int p = low;
		int q = middle;

		for (int i = low; i < high; i++) {
			if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
				to[i] = from[p++];
			} else {
				to[i] = from[q++];
			}
		}
	}

	private void swap(int first, int second) {
		int temp = indexes[first];
		indexes[first] = indexes[second];
		indexes[second] = temp;
	}

	public Object getValueAt(int row, int column) {
		checkModel();
		return model.getValueAt(indexes[row], column);
	}

	public void setValueAt(Object aValue, int row, int column) {
		checkModel();
		model.setValueAt(aValue, indexes[row], column);
	}

	public void sortByColumn(int column) {
		sortByColumn(column, true);
	}

	public void sortByColumn(int column, boolean ascending) {
		this.ascending = ascending;
		sortingColumns.removeAllElements();
		sortingColumns.addElement(new Integer(column));
		sort();
		super.tableChanged(new TableModelEvent(this));
	}
}

class TableHeaderSorter extends MouseAdapter {

	private TableSorter sorter;

	private JTable table;

	private TableHeaderSorter() {
	}

	public static void install(TableSorter sorter, JTable table) {
		TableHeaderSorter tableHeaderSorter = new TableHeaderSorter();
		tableHeaderSorter.sorter = sorter;
		tableHeaderSorter.table = table;
		JTableHeader tableHeader = table.getTableHeader();
		tableHeader.addMouseListener(tableHeaderSorter);
	}

	public void mouseClicked(MouseEvent mouseEvent) {
		TableColumnModel columnModel = table.getColumnModel();
		int viewColumn = columnModel.getColumnIndexAtX(mouseEvent.getX());
		int column = table.convertColumnIndexToModel(viewColumn);
		if (mouseEvent.getClickCount() == 1 && column != -1) {
			System.out.println("Sorting ...");
			int shiftPressed = (mouseEvent.getModifiers() & InputEvent.SHIFT_MASK);
			boolean ascending = (shiftPressed == 0);
			sorter.sortByColumn(column, ascending);
		}
	}
}

class TableMap extends AbstractTableModel implements TableModelListener {

	TableModel model;

	public TableModel getModel() {
		return model;
	}

	public void setModel(TableModel model) {
		if (this.model != null) {
			this.model.removeTableModelListener(this);
		}
		this.model = model;
		if (this.model != null) {
			this.model.addTableModelListener(this);
		}
	}
	
    public Class getColumnClass(int c) {
    	Class cls = null;
    	if (c == 0 || c == 1) cls = String.class;
    	if (c == 2) cls = Boolean.class;
    	return cls;
    }

	public int getColumnCount() {
		return ((model == null) ? 0 : model.getColumnCount());
	}

	public String getColumnName(int column) {
		return model.getColumnName(column);
	}

	public int getRowCount() {
		return ((model == null) ? 0 : model.getRowCount());
	}

	public Object getValueAt(int row, int column) {
		return model.getValueAt(row, column);
	}

	public void setValueAt(Object value, int row, int column) {
		model.setValueAt(value, row, column);
	}

    public boolean isCellEditable(int row, int col) {
        if (col != 2) {
            return false;
        } else {
            return true;
        }
    }

	public void tableChanged(TableModelEvent tableModelEvent) {
		fireTableChanged(tableModelEvent);
	}
}

