/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2007 Approximatrix, LLC
 *  http://www.approximatrix.com
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.main.ui;

import java.awt.event.KeyEvent;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Component;

import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;

import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;

import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import com.approximatrix.draco.analysis.ui.ANOVADialog;
import com.approximatrix.draco.application.ApplicationManager;
import com.approximatrix.draco.application.ChildFrame;
import com.approximatrix.draco.fit.ui.LeastSquaresFit;
import com.approximatrix.draco.fit.ui.NonLinearLSFit;
import com.approximatrix.draco.fit.ui.SimpleIterativeFit;
import com.approximatrix.draco.fit.ui.TSLeastSquaresFit;
import com.approximatrix.draco.general.ui.GeneralWorker;
import com.approximatrix.draco.general.ui.JCSelectTable;
import com.approximatrix.draco.general.ui.PluginMenuHandler;
import com.approximatrix.draco.general.ui.StatTable;
import com.approximatrix.draco.io.ClipboardSupport;
import com.approximatrix.draco.io.ImportExport;
import com.approximatrix.draco.io.ui.PasteDialog;
import com.approximatrix.draco.io.ui.PasteWithInterpDialog;
import com.approximatrix.draco.plot.ui.PlotColumnData;
import com.approximatrix.draco.plot.ui.SetupPlotHisto;
import com.approximatrix.draco.datamodel.StatTableModel;
import com.approximatrix.draco.datamodel.VarViewTableModel;

public class DataDialog extends ChildFrame implements ListSelectionListener, ActionListener, ClipboardOwner, ChangeListener {

	private static int WIDTH = 500;
	private static int HEIGHT = 400;
	private static final String DIALOG_NAME = "Data"; 
	
	/** The string for requesting topical help */
	private static final String HELP_KEY = "data";
	
	/** The icon to be displayed for this child window */
	private static final String PATH_TO_ICON = "/com/approximatrix/draco/main/ui/images/icons/data.png";
	
	private static final int MIN_COL_WIDTH = 20;
	
	private static final int PLOT_TYPE_DATA = 1;
	private static final int PLOT_TYPE_HIST = 2;
	
	private JTabbedPane jTabbedPane = null;
	private JScrollPane jVarScrollPane = null;
	private JTable jVarTable = null;
	private StatTable jDataTable = null;
	private JScrollPane jDataScrollPane = null;
	
	private StatTableModel model = null;
	
	private PluginMenuHandler pluginHandler = null;
	
	// Menu items
	private JMenu edit_menu = null;
		private JMenuItem m_copy = null;
		private JMenuItem m_paste = null;
		private JMenuItem m_paste_interp = null;
		private JMenuItem data_new_ws_menuitem = null;
	
	private JMenu data_menu = null;
		private JMenu column_menu = null;
			private JMenuItem m_set_column = null;
			private JMenuItem m_column_stats = null;
			private JMenuItem m_set_width = null;
			private JMenuItem m_set_one_width = null;
			private JMenuItem m_add_column = null;
			private JMenuItem m_rename_column = null;
			private JMenuItem m_set_description = null;
			private JMenuItem m_set_column_fmt = null;
			private JMenuItem m_remove_column = null;
			private JMenuItem m_gen_lag_variable = null;
		private JMenu row_menu = null;
			private JMenuItem m_add_row = null;
			private JMenuItem m_insert_row = null;
			private JMenuItem m_remove_row = null;
		private JMenu sheet_menu = null;
			private JMenu sort_menu = null;
				private JMenuItem m_sort_ascending = null;
				private JMenuItem m_sort_descending = null;
		private JMenuItem m_ANOVA = null;
	
	private JMenu regress_menu = null;
		private JMenuItem m_ls_fit = null;
		private JMenuItem m_tsls_fit = null;
		private JMenuItem m_logit_fit = null;
		private JMenuItem m_probit_fit = null;
		private JMenuItem m_nonlinear_ls_fit = null;

	private JMenu plot_menu = null;
		private JMenuItem m_plot_column = null;
		private JMenuItem m_plot_histo = null;
			
	// Toolbar buttons
	private JButton copy_button = null;
	private JButton paste_button = null;
	private JButton copytows_button = null;
	private JButton add_button = null;
	private JButton delete_button = null;
	
	private JButton move_up_button = null;
	private JButton move_down_button = null;
	
	private JButton set_values_button = null;
	private JButton stats_button = null;
	private JButton plot_data_button = null;
	private JButton plot_histo_button = null;

	/** Constructs a new data dialog based on the passed model
	 * 
	 * @param model the StatTableModel object containing all data
	 */
	public DataDialog(StatTableModel model) {
		super(DIALOG_NAME, 
				true, //resizable
				false, //closable
				true, //maximizable
				true);

		initialize();
		this.setDataModel(model);
	}
	
	public void initialize() {
		this.setSize(WIDTH, HEIGHT);
		//this.setTitle(DIALOG_NAME);
		
		// Initialize the data panels
		JPanel content = new JPanel();
		content.setLayout(new BorderLayout());
		this.setContentPane(content);
		content.add(getJTabbedPane(),BorderLayout.CENTER);
		
		// Initialize the icon
		super.setIcon(PATH_TO_ICON);
		
		// Set the Plugin handler
		pluginHandler = new PluginMenuHandler(this);
	}
	
	/** When called, the plugin menu handler is asked for a new list of plugins,
	 * and all menus are updated
	 */
	public void notifyPluginsUpdated() {
		removePluginsFromMenu(this.getData_menu());
		List<JMenuItem> plugins = pluginHandler.getAnalysisPluginMenuItems(); 
		for(JMenuItem plugin : plugins)
			this.getData_menu().add(plugin);
		
		removePluginsFromMenu(this.getRegress_menu());
		plugins = pluginHandler.getRegressionPluginMenuItems(); 
		for(JMenuItem plugin : plugins)
			this.getRegress_menu().add(plugin);
	}
	
	/** Removes plugin items from a given menu */
	private void removePluginsFromMenu(JMenu menu) {
		Component[] c = menu.getMenuComponents();
		if(c == null) return;
		for(int i=0;i<c.length;i++) {
			if(c[i] instanceof PluginMenuHandler.PluginMenuItem)
				menu.remove(c[i]);
		}
	}
	
	/**
	 * This method initializes jTabbedPane	
	 * 	
	 * @return javax.swing.JTabbedPane	
	 */
	private JTabbedPane getJTabbedPane() {
		if (jTabbedPane == null) {
			jTabbedPane = new JTabbedPane();
			jTabbedPane.setPreferredSize(new java.awt.Dimension(500,500));
			jTabbedPane.setTabPlacement(javax.swing.JTabbedPane.TOP);
			jTabbedPane.addTab("Data View", null, getJDataScrollPane(), null);
			jTabbedPane.addTab("Variable View", null, getJVarScrollPane(), null);
			jTabbedPane.addChangeListener(this);
		}
		return jTabbedPane;
	}
	
	/**
	 * This method initializes jVarScrollPane	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getJVarScrollPane() {
		if (jVarScrollPane == null) {
			jVarScrollPane = new JScrollPane();
			jVarScrollPane.setViewportView(getJVarTable());
		}
		return jVarScrollPane;
	}
	
	/**
	 * This method initializes jScrollPane	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */    
	private JScrollPane getJDataScrollPane() {
		if (jDataScrollPane == null) {
			jDataScrollPane = new JScrollPane();
			jDataScrollPane.setViewportView(getJDataTable());
		}
		return jDataScrollPane;
	}
	
	/**
	 * This method initializes jVarTable	
	 * 	
	 * @return javax.swing.JTable	
	 */
	private JTable getJVarTable() {
		if (jVarTable == null) {
			jVarTable = new JTable() {
				protected java.awt.Color colorForRow(int row) {
					return (row % 2 == 0) ? getBackground().darker() : getBackground();
				}
				public java.awt.Component prepareRenderer(javax.swing.table.TableCellRenderer renderer, int row, int column) {
					java.awt.Component c = super.prepareRenderer(renderer, row, column);
					if (isCellSelected(row, column) == false) {
						c.setBackground(colorForRow(row));
						c.setForeground(UIManager.getColor("Table.foreground"));
					} else {
						c.setBackground(UIManager.getColor("Table.selectionBackground"));
						c.setForeground(UIManager.getColor("Table.selectionForeground"));
					}
					return c;
				}
			};
			jVarTable.setShowGrid(false);
			jVarTable.setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
			jVarTable.setRowSelectionAllowed(true);
			jVarTable.getSelectionModel().addListSelectionListener(this);

			//jVarTable.addKeyListener(this);
		}
		return jVarTable;
	}
	
	/**
	 * This method initializes jTable	
	 * 	
	 * @return javax.swing.JTable	
	 */    
	private JTable getJDataTable() {
		if (jDataTable == null) {
			jDataTable = new StatTable();
			jDataTable.setShowGrid(true);
			jDataTable.setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
			jDataTable.setColumnSelectionAllowed(true);
			//main_table.setRowSelectionAllowed(false);
			//main_table.setColumnSelectionInterval(0,main_table.getModel().getRowCount()-1);
			jDataTable.setSelectionForeground(java.awt.Color.white);
			jDataTable.setBackground(new java.awt.Color(235,235,235));
			jDataTable.setGridColor(java.awt.Color.gray);
			jDataTable.getTableHeader().setReorderingAllowed(false);
			
			jDataTable.getSelectionModel().addListSelectionListener(this);
			
			// Disable Table's default Ctrl-C handling
			InputMap im = jDataTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
			KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK);
			im.put(ks, "none");
			
			//jDataTable.addKeyListener(this);
			
		}
		return jDataTable;
	}
	
	/** Routine implementing ListSelectionListener's necessary method for handling selctions
	 * on the two main tables of the window
	 */
	public void valueChanged(ListSelectionEvent e) {

		if(e.getSource() == getJDataTable().getSelectionModel()) {

			if(getJTabbedPane().getSelectedComponent() == getJDataScrollPane()) {
				getJVarTable().clearSelection();
			}

		} else if(e.getSource() == getJVarTable().getSelectionModel()) {
			int[] var = getJVarTable().getSelectedRows();

			getJDataTable().clearSelection();

			if(var == null) return;
			if(var.length == 0) return;

			try {
				//getMain_table().setCol

				getJDataTable().setRowSelectionInterval(0,getJDataTable().getRowCount()-1);
				for(int i=0; i<var.length; i++) {
					getJDataTable().addColumnSelectionInterval(var[i],var[i]);
				}

			} catch(java.lang.IllegalArgumentException iae) {
				// Nothing
			}

		}
	}
	
	/** Sets the data model used by the tables in this dialog to
	 * the passed model
	 * 
	 * @param model a valid StatTableModel object
	 */
	public void setDataModel(StatTableModel model) {
		if(model == null) return;
		this.model = model;
		this.getJDataTable().setModel(this.model);
		
		// Set up the Variable View data model
		this.getJVarTable().setModel(new VarViewTableModel(this.model));
		this.model.addUpdater((VarViewTableModel)getJVarTable().getModel());
		
		this.model.fireTableDataChanged();
		this.model.fireTableStructureChanged();
	}
	
	/** Returns the current table model being displayed in this window.
	 * 
	 * @return the StatTableModel object being used in this window
	 */
	public StatTableModel getDataModel() {
		return this.model;
	}
	
	/** Handles all menu and button events right here
	 * 
	 */
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		
		// Sort through the possible sources
		// ---------------------------- Edit Menu
		if(source == m_copy)
			copyText();	
		else if(source == m_paste)
			pasteText(false);
		else if(source == m_paste_interp)
			pasteText(true);
		else if(source == data_new_ws_menuitem)
			sendtoNewWorksheet();
		
		// ---------------------------- Plot Menu
		else if(source == m_plot_column) 
			openPlot(PLOT_TYPE_DATA);
		else if(source == m_plot_histo) 
			openPlot(PLOT_TYPE_HIST);
		
		// ---------------------------- Regress Menu
		else if(source == m_ls_fit) 
			getMainWindow().openWindow(new LeastSquaresFit(model));
		else if(source == m_tsls_fit) 
			getMainWindow().openWindow(new TSLeastSquaresFit(model));
		else if(source == m_logit_fit) 
			getMainWindow().openWindow(new SimpleIterativeFit(model,SimpleIterativeFit.LOGIT_REGRESSION));
		else if(source == m_probit_fit) 
			getMainWindow().openWindow(new SimpleIterativeFit(model,SimpleIterativeFit.PROBIT_REGRESSION));
		else if(source == m_nonlinear_ls_fit) 
			getMainWindow().openWindow(new NonLinearLSFit(model));
		
		// ---------------------------- Data/Column Menu
		else if(source == m_set_column)
			setColumnValues();
		else if(source == m_column_stats)
			getColumnStats();
		else if(source == m_add_column) {
			model.addColumn();
			((VarViewTableModel)getJVarTable().getModel()).fireTableStructureChanged();
		} else if(source == m_remove_column)
			removeColumn();
		else if(source == m_set_description)
			setColumnDescription();
		else if(source == m_set_column_fmt)
			setColumnFormat();
		else if(source == m_rename_column)
			renameColumn();
		else if(source == m_gen_lag_variable)
			generateLaggedVariable();
		else if(source == m_set_width)
			setColumnWidth(false);
		else if(source == m_set_width)
			setColumnWidth(true);
		
		// ---------------------------- Data/Row Menu
		else if(source == m_add_row)
			model.addRow();
		else if(source == m_insert_row)
			model.insertRow(getJDataTable().getSelectedRow());
		else if(source == m_remove_row)
			model.removeRow(getJDataTable().getSelectedRow());

		// ---------------------------- Data/Sheet/Sort Menu
		else if(source == m_sort_ascending)
			sortByColumn(true);
		else if(source == m_sort_descending)
			sortByColumn(false);
		
		// ---------------------------- Data
		else if(source == m_ANOVA)
			getMainWindow().openWindow(new ANOVADialog(model));
		
		// ---------------------------- Movement buttons
		else if(source == move_up_button)
			this.moveSelectionUp();
		else if(source == move_down_button)
			this.moveSelectionDown();
		
		// ---------------------------- Error case
		else {
			System.err.println("Mysterious source for menu option...");
			System.err.println(source.toString());
		}
		
	}
	
	private JMenu getEdit_menu() {
		if(edit_menu == null) {
			edit_menu = DynamicMainMenuBar.GetGenericEditMenu();
			
			m_copy = new JMenuItem("Copy");
			m_copy.addActionListener(this);
			edit_menu.add(m_copy);
			
			m_paste = new JMenuItem("Paste...");
			m_paste.addActionListener(this);
			edit_menu.add(m_paste);
			
			m_paste_interp = new JMenuItem("Paste with Interpolation...");
			m_paste_interp.addActionListener(this);
			edit_menu.add(m_paste_interp);
			
			edit_menu.addSeparator();
			
			data_new_ws_menuitem = new JMenuItem("Send Selection to new worksheet");
			data_new_ws_menuitem.addActionListener(this);
			edit_menu.add(data_new_ws_menuitem);
		}
		return edit_menu;
	}
	
	private JMenu getData_menu() {
		if(data_menu == null) {
			data_menu = DynamicMainMenuBar.GetGenericDataMenu();
			
			data_menu.add(getColumn_submenu());
			data_menu.add(getRow_submenu());
			data_menu.add(getSheet_submenu());
			
			data_menu.addSeparator();
			
			m_ANOVA = new JMenuItem("One-Factor ANOVA...");
			m_ANOVA.addActionListener(this);
			data_menu.add(m_ANOVA);
			
			List<JMenuItem> plugins = pluginHandler.getAnalysisPluginMenuItems(); 
			for(JMenuItem plugin : plugins)
				data_menu.add(plugin);

		}
		return data_menu;
	}
	
	private JMenu getColumn_submenu() {
		if(column_menu == null) {
			column_menu = new JMenu("Column Operations");

			m_add_column = new JMenuItem("Add Column");
			m_add_column.addActionListener(this);
			column_menu.add(m_add_column);
			
			m_remove_column = new JMenuItem("Remove Column");
			m_remove_column.addActionListener(this);
			column_menu.add(m_remove_column);
			
			column_menu.addSeparator();
			
			m_rename_column = new JMenuItem("Rename Column...");
			m_rename_column.addActionListener(this);
			column_menu.add(m_rename_column);
			
			m_set_description = new JMenuItem("Set Column Description...");
			m_set_description.addActionListener(this);
			column_menu.add(m_set_description);
			
			m_set_column_fmt = new JMenuItem("Set Numeric Display Format...");
			m_set_column_fmt.addActionListener(this);
			column_menu.add(m_set_column_fmt);
			
			column_menu.addSeparator();
			
			m_set_column = new JMenuItem("Set Column Values...");
			m_set_column.addActionListener(this);
			column_menu.add(m_set_column);
			
			m_gen_lag_variable = new JMenuItem("Quick-generate Lagged Variable");
			m_gen_lag_variable.addActionListener(this);
			column_menu.add(m_gen_lag_variable);
			
			column_menu.addSeparator();
			
			m_set_width = new JMenuItem("Set Column Widths...");
			m_set_width.addActionListener(this);
			column_menu.add(m_set_width);
			
			m_set_one_width = new JMenuItem("Set Selected Column Width...");
			m_set_one_width.addActionListener(this);
			column_menu.add(m_set_one_width);
			
			column_menu.addSeparator();
			
			m_column_stats = new JMenuItem("Column Statistics...");
			m_column_stats.addActionListener(this);
			column_menu.add(m_column_stats);
		}
		return column_menu;
	}
	
	private JMenu getRow_submenu() {
		if(row_menu == null) {
			row_menu = new JMenu("Row Operations");

			m_add_row = new JMenuItem("Add Row");
			m_add_row.addActionListener(this);
			row_menu.add(m_add_row);
			
			m_insert_row = new JMenuItem("Insert Row");
			m_insert_row.addActionListener(this);
			row_menu.add(m_insert_row);
			
			m_remove_row = new JMenuItem("Remove Row");
			m_remove_row.addActionListener(this);
			row_menu.add(m_remove_row);

		}
		return row_menu;
	}
	
	private JMenu getSheet_submenu() {
		if(sheet_menu == null) {
			sheet_menu = new JMenu("Sheet Operations");
			sheet_menu.add(getSort_submenu());
		}
		return sheet_menu;
	}
	
	private JMenu getSort_submenu() {
		if(sort_menu == null) {
			sort_menu = new JMenu("Sort by Selected Column");

			m_sort_ascending = new JMenuItem("Ascending");
			m_sort_ascending.addActionListener(this);
			sort_menu.add(m_sort_ascending);
			
			m_sort_descending = new JMenuItem("Descending");
			m_sort_descending.addActionListener(this);
			sort_menu.add(m_sort_descending);
		}
		return sort_menu;
	}
	
	private JMenu getRegress_menu() {
		if(regress_menu == null) {
			regress_menu = new JMenu("Regress");

			m_ls_fit = new JMenuItem("Least Squares Fit...");
			m_ls_fit.addActionListener(this);
			regress_menu.add(m_ls_fit);
			
			m_tsls_fit = new JMenuItem("Two-Stage Least Squares...");
			m_tsls_fit.addActionListener(this);
			regress_menu.add(m_tsls_fit);
			
			m_logit_fit = new JMenuItem("Logit...");
			m_logit_fit.addActionListener(this);
			regress_menu.add(m_logit_fit);
			
			m_probit_fit = new JMenuItem("Probit...");
			m_probit_fit.addActionListener(this);
			regress_menu.add(m_probit_fit);
			
			m_nonlinear_ls_fit = new JMenuItem("Nonlinear Least Squares...");
			m_nonlinear_ls_fit.addActionListener(this);
			regress_menu.add(m_nonlinear_ls_fit);
			
			List<JMenuItem> plugins = pluginHandler.getRegressionPluginMenuItems();
			if(plugins.size() > 0)
				regress_menu.addSeparator();
			for(JMenuItem plugin : plugins)
				regress_menu.add(plugin);
		}
		return regress_menu;
	}
	
	private JMenu getPlot_menu() {
		if(plot_menu == null) {
			plot_menu = new JMenu("Plot");

			m_plot_column = new JMenuItem("Plot column data...");
			m_plot_column.addActionListener(this);
			plot_menu.add(m_plot_column);
			
			m_plot_histo = new JMenuItem("Plot histogram...");
			m_plot_histo.addActionListener(this);
			plot_menu.add(m_plot_histo);
		}
		return plot_menu;
	}
	
	/** Constructs if necessary and returns an array of JMenu objects
	 * to be integrated into the MainWindow menus
	 * 
	 * @return a valid array of JMenu objects, completely constructed
	 */
	public JMenu[] getMenuItems() {
		if(menuItems == null) {
			ArrayList<JMenu> menus = new ArrayList<JMenu>();
			
			menus.add(getEdit_menu());
			menus.add(getData_menu());
			menus.add(getRegress_menu());
			menus.add(getPlot_menu());
			
			menuItems = new JMenu[menus.size()];
			menus.toArray(menuItems);
		}
		return menuItems;
	}

	/** Constructs (if necessary) and returns an array of components for display
	 * on the main window toolbar.  In this class, the array contains buttons and
	 * separators.
	 * 
	 * @return an array of toolbar items
	 */
	public Component[] getToolbarButtons() {
		if(toolbarButtons == null) {
			
			// Make sure our menus are initialized - they're necessary here
			this.getMenuItems();
			
			ArrayList<Component> buttons = new ArrayList<Component>();
			
			copy_button = DynamicToolbar.GetGenericCopyButton(this.getMainWindow());
			copy_button.addMouseListener(getAdapter(m_copy));
			buttons.add(copy_button);
			
			paste_button = DynamicToolbar.GetGenericPasteButton(this.getMainWindow());
			paste_button.addMouseListener(getAdapter(m_paste));
			buttons.add(paste_button);

			copytows_button = new JButton();
			copytows_button.setToolTipText("Send selection to new worksheet");
			copytows_button.setIcon(ApplicationManager.GetIcon("EDITTOSHEET"));
			copytows_button.addMouseListener(getAdapter(data_new_ws_menuitem));
			buttons.add(copytows_button);
			
			add_button = new JButton();
			add_button.setToolTipText("Add a new column/variable");
			add_button.setIcon(ApplicationManager.GetIcon("ADD"));
			add_button.addMouseListener(getAdapter(m_add_column));
			buttons.add(add_button);
			
			delete_button = new JButton();
			delete_button.setToolTipText("Delete selected column/variable");
			delete_button.setIcon(ApplicationManager.GetIcon("REMOVE"));
			delete_button.addMouseListener(getAdapter(m_remove_column));
			buttons.add(delete_button);

			buttons.add(new javax.swing.JToolBar.Separator());
			
			move_up_button = new JButton();
			move_up_button.addActionListener(this);
			buttons.add(move_up_button);
			
			move_down_button = new JButton();
			move_down_button.addActionListener(this);
			buttons.add(move_down_button);
			
			setMoveLeftRight();
			
			buttons.add(new javax.swing.JToolBar.Separator());

			set_values_button = new JButton();
			set_values_button.setToolTipText("Set column/variable values...");
			set_values_button.setIcon(ApplicationManager.GetIcon("FUNCTION"));
			set_values_button.addMouseListener(getAdapter(m_set_column));
			buttons.add(set_values_button);
			
			stats_button = new JButton();
			stats_button.setToolTipText("Variable statistics...");
			stats_button.setIcon(ApplicationManager.GetIcon("STATS"));
			stats_button.addMouseListener(getAdapter(m_column_stats));
			buttons.add(stats_button);
			
			plot_data_button = new JButton();
			plot_data_button.setToolTipText("Plot data...");
			plot_data_button.setIcon(ApplicationManager.GetIcon("PLOTLINES"));
			plot_data_button.addMouseListener(getAdapter(m_plot_column));
			buttons.add(plot_data_button);
			
			plot_histo_button = new JButton();
			plot_histo_button.setToolTipText("Plot histogram...");
			plot_histo_button.setIcon(ApplicationManager.GetIcon("PLOTBAR"));
			plot_histo_button.addMouseListener(getAdapter(m_plot_histo));
			buttons.add(plot_histo_button);
			
			toolbarButtons = new Component[buttons.size()];
			buttons.toArray(toolbarButtons);
		}
		return toolbarButtons;
	}

	/** Returns false for this dialog because the data dialog is a basic
	 * necessity.  It cannot be renamed.
	 * 
	 * @return false because this dialog is not renameable
	 */
	public boolean isRenameable() {
		return false;
	}
	
	/** Copies selected data to the clipboard in tab-delimited, spreadsheet-
	 * compatible form.
	 */
	private void copyText() {
		int[] rows = getJDataTable().getSelectedRows();
		int[] columns = getJDataTable().getSelectedColumns();

		if(rows == null || columns == null) return;

		String cliptext = ImportExport.GenerateCSVData(model,rows,columns,"\t");
		ClipboardSupport.putClipboard(cliptext,this);
	}

	/** Handles receiving paste text from the clipboard, opening the
	 * appropriate paste window. 
	 * 
	 * @param with_interp true to signal paste with interpolation, false to signal (not-so-)simple pasting
	 */
	private void pasteText(boolean with_interp) {
		String cliptext = ClipboardSupport.GetTabularStringData();				
		
		if(cliptext != null) {
			
			if(!with_interp) {
				int row = getJDataTable().getSelectedRow();
		
				// Need clever new window handling
				PasteDialog pd = new PasteDialog(this.getMainWindow(), (StatTableModel)this.getDataModel(), cliptext, row);
				pd.setVisible(true);
			} else {
				
				// Need clever new window handling
				PasteWithInterpDialog this_dialog = new PasteWithInterpDialog(this.getMainWindow(),cliptext,(StatTableModel)this.getDataModel());
				
				if(this_dialog.isReady())
					this_dialog.setVisible(true);
				else
					this_dialog.dispose();
			}
		}
		
	}
	
	/** Opens the necessary dialogs to create a plot from the data.  The
	 * type integer signifies the type of plot to create.
	 * 
	 * @param type the type of plot to generate
	 */
	private void openPlot(int type) {
		switch(type) {
			case PLOT_TYPE_DATA:
				// Need clever new window handling
				//new PlotColumnData(getWindowTableModel(),taskbar);
				getMainWindow().openWindow(new PlotColumnData(getMainWindow().getWindowTableModel()));
				break;
			case PLOT_TYPE_HIST:
				StatTableModel wmodel = getMainWindow().getWindowTableModel();
				if(getJDataTable().getSelectedColumn() != -1) {					
					getMainWindow().openWindow(new SetupPlotHisto(wmodel,
									wmodel.getColumnName(getJDataTable().getSelectedColumn())));
				} else {
					//new SetupPlotHisto(model,taskbar);
					getMainWindow().openWindow(new SetupPlotHisto(wmodel));
				}
				break;
		}
	}
	
	/** Wrapper to open the "Set Column Values" dialog with some error
	 * checking to ensure that a column has been selected.
	 *
	 */
	private void setColumnValues() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		if(column_to_pass != -1) {
			getMainWindow().openWindow(new ColumnEquationsDialog(model,model.getColumnName(column_to_pass)));
		} else
			showNoneSelectedWarning();
	}
	
	/** Wrapper to open the column statistics dialog with error checking
	 * to ensure a column has been selected.
	 *
	 */
	private void getColumnStats() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		System.out.println(column_to_pass);
		if(column_to_pass != -1) {
			getMainWindow().openWindow(new ColumnStats(model,model.getColumnName(column_to_pass)));
		} else
			showNoneSelectedWarning();
	}
	
	/** Wrapper that controls the removal of the selected column from
	 * the main data table.
	 *
	 */
	private void removeColumn() {
		if(getJDataTable().getSelectedColumn() >= 0) {
			int selected = JOptionPane.showConfirmDialog(this,"Confirm Column Delete?",ApplicationManager.APP_NAME+" Confirmation", JOptionPane.YES_NO_OPTION);
			if(selected == JOptionPane.YES_OPTION) {
				model.removeColumn(getJDataTable().getSelectedColumn());
				((VarViewTableModel)getJVarTable().getModel()).fireTableStructureChanged();
			}
		}
	}
	
	/** Opens the description dialog if a column is selected, allowing a user
	 * to change the description of a variable
	 *
	 */
	private void setColumnDescription() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		if(column_to_pass != -1) {
			String describe = model.getDescription(column_to_pass);
			JTextField description = new JTextField(describe);
			int rv = JOptionPane.showConfirmDialog(this,description,
					"Description: "+model.getColumnName(column_to_pass),
					JOptionPane.OK_CANCEL_OPTION);
			if(rv == JOptionPane.YES_OPTION) {
				model.setDescription(column_to_pass,description.getText());
			}
		} else
			showNoneSelectedWarning();
	}
	
	private void setColumnFormat() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		if(column_to_pass != -1) {
			DecimalFormat df = model.getVariable(column_to_pass).getDecimalFormatting();
			String value = "";
			if(df != null)
				value = df.toPattern();
			
			final String no_fmt = "No Formatting";
			
			String[] values = { value, no_fmt };
			
			JComboBox cb = new JComboBox(values);
			cb.setEditable(true);
			cb.setSelectedItem(value);
			
			int rv = JOptionPane.showConfirmDialog(this,cb,
					"Formatting: "+model.getColumnName(column_to_pass),
					JOptionPane.OK_CANCEL_OPTION);
			if(rv == JOptionPane.YES_OPTION) {
				
				String selection = cb.getSelectedItem().toString();
				if(selection.equals(no_fmt))
					model.getVariable(column_to_pass).setDecimalFormatting(null);
				else
					model.getVariable(column_to_pass).setDecimalFormatting(selection);
				
			}
		} else
			showNoneSelectedWarning();
	}
	
	/** Opens the rename dialog if a column is selected.
	 */
	private void renameColumn() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		if(column_to_pass != -1) {
			String describe = model.getColumnName(column_to_pass);
			JTextField varname = new JTextField(describe);
			int rv = JOptionPane.showConfirmDialog(this,varname,
					"Variable Name: "+model.getColumnName(column_to_pass),
					JOptionPane.OK_CANCEL_OPTION);
			if(rv == JOptionPane.YES_OPTION) {
				model.renameColumn(column_to_pass,varname.getText());
			}
		} else
			showNoneSelectedWarning();
	}
	
	/** Wrapper that generates a lagged variable based on a selected column.
	 */
	private void generateLaggedVariable() {
		int column_to_pass = getJDataTable().getSelectedColumn();
		if(column_to_pass != -1) {
			String col_name = getJDataTable().getColumnName(column_to_pass);

			StringBuffer eqn = new StringBuffer("back(");
			eqn.append(col_name);
			eqn.append(",1)");

			model.addColumn(eqn.toString(),col_name+"_lagged");

			((VarViewTableModel)getJVarTable().getModel()).fireTableStructureChanged();
		} else
			showNoneSelectedWarning(); 
	}
	
	/** Wrapper to handle changing column widths.  The parameter passed in 
	 * represents whether this is a global change or a change to a single column.
	 * 
	 * @param just_one true if just one column is to be changed, false otherwise
	 */
	private void setColumnWidth(boolean just_one) {
		if(just_one) {
			if(getJDataTable().getSelectedColumn() >= 0) {
				new ColumnWidthDialog(getMainWindow(),getJDataTable(),getJDataTable().getSelectedColumn()).setVisible(true);
			} else 
				showNoneSelectedWarning(); 
		} else
			new ColumnWidthDialog(getMainWindow(),getJDataTable()).setVisible(true);
	}
	
	/** Wrapper for processing sort requests using the internal SortWorker class
	 * 
	 * @param ascending true if ascending, false if descending
	 */
	private void sortByColumn(boolean ascending) {
		int direction = SortWorker.DESCENDING_SORT;
		if(ascending)
			direction = SortWorker.ASCENDING_SORT;
		
		if(getJDataTable().getSelectedColumn() >= 0)
			new GeneralWorker(getMainWindow(),
							  ApplicationManager.APP_NAME+" - Ascending Sort",
							  "Performing Sort - Please Stand by...",
							  new SortWorker(getMainWindow(),getJDataTable().getSelectedColumn(),direction));
		else
			showNoneSelectedWarning(); 
	}
	
	/** Sends the selected data to a new worksheet in a separate
	 * main window.
	 */
	public void sendtoNewWorksheet() {
		int[] columns = getJDataTable().getSelectedColumns();
		int[] rows = getJDataTable().getSelectedRows();
		
		if(rows == null || columns == null) return;
		if(rows.length == 0 || columns.length == 0) return;
		
		Object[][] selected_data = ((StatTableModel)getJDataTable().getModel()).getData(rows,columns);
		
		String[] column_names = new String[columns.length];
		for(int j=0;j<columns.length;j++) {
			column_names[j] = getJDataTable().getColumnName(columns[j]);
		}
		
		// Start the new worksheet with this data
		ApplicationManager.RequestNewWindow(new StatTableModel(selected_data,column_names));
	}
	
	/** Provides quick access to a simple dialog stating that a column
	 * has not yet been selected.  This warning is used by multiple menu functions.
	 */
	private void showNoneSelectedWarning() {
		JOptionPane.showMessageDialog(this,"Please select a column first", "Error", JOptionPane.ERROR_MESSAGE);
	}
	
	/** Necessary implemented method for ClipboardOwner interface that
	 * is not really used here.
	 * 
	 * @see java.awt.datatransfer.ClipboardOwner#lostOwnership(java.awt.datatransfer.Clipboard, java.awt.datatransfer.Transferable)
	 */
	public void lostOwnership(Clipboard arg0, Transferable arg1) {
		// Nothing to do...
	}
	
	/** Moves the selected variables/columns up/left in the list of variables
	 */
	public void moveSelectionUp() {
		int[] movers = this.getJDataTable().getSelectedColumns();
		
		// Safety measures
		if(movers == null) return;
		if(movers.length == 0) return;
	
		// Check for any invalid blocsk to move
		for(int i=0;i<movers.length;i++) {
			if(movers[i] ==0) return;
		}

	
		for(int i=0;i<movers.length;i++) {
			model.moveColumn(movers[i], movers[i]-1);
			movers[i]--;
		}
		
		// Reset the selection
		for(int i=movers.length-1;i>=0;i--) {
			this.getJDataTable().addColumnSelectionInterval(movers[i], movers[i]);
			this.getJDataTable().addRowSelectionInterval(0,this.getJDataTable().getRowCount()-1);
			
			if(this.getJTabbedPane().getSelectedComponent() == this.getJVarScrollPane())
				this.getJVarTable().addRowSelectionInterval(movers[i], movers[i]);
		}
	}
	
	/** Moves the selected variables/columns down/right in the list of variables
	 */
	public void moveSelectionDown() {
		int[] movers = this.getJDataTable().getSelectedColumns();
		
		// Safety measures
		if(movers == null) return;
		if(movers.length == 0) return;
		
		
		// Check for any invalid blocsk to move
		for(int i=0;i<movers.length;i++) {
			if(movers[i] >= model.getColumnCount()-1) return;
		}
	
		for(int i=movers.length-1;i>=0;i--) {
			model.moveColumn(movers[i], movers[i]+1);
			movers[i]++;
		}
		
		// Reset the selection
		for(int i=movers.length-1;i>=0;i--) {
			this.getJDataTable().addColumnSelectionInterval(movers[i], movers[i]);
			this.getJDataTable().addRowSelectionInterval(0,this.getJDataTable().getRowCount()-1);
			
			if(this.getJTabbedPane().getSelectedComponent() == this.getJVarScrollPane())
				this.getJVarTable().addRowSelectionInterval(movers[i], movers[i]);
		}
	}
	
	/** Sets the movement buttons to Up/Down state when the Variable View is selected
	 *
	 */
	public void setMoveUpDown() {
		move_up_button.setIcon(ApplicationManager.GetIcon("ARROWUP"));
		move_up_button.setToolTipText("Move variable up");
		move_up_button.repaint();
		move_down_button.setIcon(ApplicationManager.GetIcon("ARROWDOWN"));
		move_down_button.setToolTipText("Move variable down");
		move_down_button.repaint();
	}
	
	/** Sets the movement buttons to Left/Right state when the Data View is selected
	 * 
	 *
	 */
	public void setMoveLeftRight() {
		move_up_button.setIcon(ApplicationManager.GetIcon("ARROWLEFT"));
		move_up_button.setToolTipText("Move variable left");
		move_up_button.repaint();
		move_down_button.setIcon(ApplicationManager.GetIcon("ARROWRIGHT"));
		move_down_button.setToolTipText("Move variable right");
		move_down_button.repaint();
	}

	/** Change listener for the tabs between data and variable views used to
	 * change the icons on the movement toolbar buttons.
	 */
	public void stateChanged(javax.swing.event.ChangeEvent e) {
		if(this.getJTabbedPane().getSelectedComponent() == this.getJVarScrollPane())
			setMoveUpDown();
		else
			setMoveLeftRight();
	}
	
	/** Small class used for the time-consuming column sort
	 * 
	 * @author Jeffrey Armstrong
	 */
	private class SortWorker implements GeneralWorker.WorkRoutine {
		public static final int ASCENDING_SORT = 1;
		public static final int DESCENDING_SORT = 2;
		private WorksheetHandler parent = null;
		private int technique = 0;
		private int column = 0;
		public SortWorker(WorksheetHandler base, int column_of_choice, int ascending_or_descending) {
			parent = base;
			technique = ascending_or_descending;
			column = column_of_choice;
		}
		public void doWork() {
			switch(technique) {
				case ASCENDING_SORT:
					parent.getWindowTableModel().sortAscending(column);
					break;
				case DESCENDING_SORT:
					parent.getWindowTableModel().sortDescending(column);
					break;	
			}
		}
	}
	
	/** Returns a MouseAdapter based on the necessary menu item for calling when the
	 * button is clicked.
	 * 
	 * @param item the equivalent menu choice
	 * @return a MouseAdapter that will call the proper menu item
	 */
	private java.awt.event.MouseAdapter getAdapter(final JMenuItem item) {
		return new java.awt.event.MouseAdapter() { 
			public void mouseClicked(java.awt.event.MouseEvent e) {
				if(item != null) {
					java.awt.event.ActionListener[] listeners = item.getListeners(java.awt.event.ActionListener.class);
					if(listeners != null) {
						
						ActionEvent ae = new ActionEvent(item,ActionEvent.ACTION_PERFORMED,null);
						for(int i=0;i<listeners.length;i++) {
							if(listeners[i] != null) 
								listeners[i].actionPerformed(ae);
						}
					}
				}
			}
		};
	}
	
	/** Hides the current data tables while a model is being loaded
	 * so that no Swing exceptions occur while the data is being
	 * constructed.
	 * 
	 * @param value false to block swing updates, true to allow them
	 */
	public void setUpdatableTable(boolean value) {
		this.getJDataTable().setVisible(value);
		this.getJVarTable().setVisible(value);
	}
	
	/** Returns the help key for the Data window
	 * 
	 * @return the proper help key, defined by HELP_KEY
	 * @see #HELP_KEY
	 * @see com.approximatrix.draco.application.ChildFrame#getHelpKey()
	 */
	public String getHelpKey() {
		return HELP_KEY;
	}
	
	public MainWindow getMainWindow() {
		return super.getMainWindow();
	}
}
