package jp.ac.kyutech.mns.ist.util;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.EventObject;

import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;


public class SelectableJTable<E> extends JTable {
	private static final long serialVersionUID = -5182013441020991946L;
	public static Font sans30 = new Font("sansserif", Font.BOLD, 16);

	ArrayList<E> adata;
	String[] columnNames;
	String[] fieldNames;
	String[] buttonNames;
	SingleTableSelector<E> sel;
	int[] colw;

	public SelectableJTable(ArrayList<E> d, String[] cn, String[] fn, String[] bn,
			int[] colwidth, SingleTableSelector<E> _sel){
		adata = d;

		columnNames = cn;
		fieldNames = fn;
		buttonNames = bn;
		colw = colwidth;
		sel = _sel;
		SelectableTableModel<E> model = new SelectableTableModel<E>(adata, columnNames,fieldNames);
		setModel(model);
		setRowHeight(40);
		setFont(sans30);
		setAutoCreateRowSorter(true);
		//table.addMouseListener(new CellButtonsMouseListener());
		//ButtonsEditorRenderer er = new ButtonsEditorRenderer(table);

		TableColumn column = getColumnModel().getColumn(fn.length-1);
		column.setCellRenderer(new ButtonsRenderer<E>(this));
		column.setCellEditor(new ButtonsEditor<E>(this));

		int xsum = 0;
		for (int i = 0 ; i < getColumnModel().getColumnCount() ; i++){
			column = getColumnModel().getColumn(i);
			//		    column.setWidth(colwidth[i]);
			column.setPreferredWidth(colwidth[i]);
			xsum += colwidth[i];
		}

		column = getColumnModel().getColumn(0);
		column.setMaxWidth(colwidth[0]);
		column = getColumnModel().getColumn(fn.length-1);
		column.setMaxWidth(colwidth[fn.length-1]);

		Dimension dim = getIntercellSpacing();
		setIntercellSpacing(new Dimension(2,dim.height));

		setDefaultRenderer(Object.class, new StripeTableRenderer());
		setDefaultRenderer(Integer.class, new StripeTableRenderer());

//		System.out.println(adata.size());
		int ysum = adata.size()*40;
//		if (ysum > xsum) ysum = xsum;

//		setPreferredSize(new Dimension(xsum, ysum));

		changeSelection(0, fn.length-1, false, false);

		addKeyListener(new KeyAdapter(){
			public void keyPressed(KeyEvent ev){
				if (ev.getKeyCode() == KeyEvent.VK_SPACE || ev.getKeyCode() == KeyEvent.VK_ENTER){
					int r = getSelectedRow();
					actionPerformed(r, "", true);
					ev.consume();
				}
				if (ev.getKeyCode() == KeyEvent.VK_ESCAPE){
					if (sel != null){
						sel.selectedObj = null;
						sel.setVisible(false);
					}
				}
				if (ev.getKeyCode() >= KeyEvent.VK_0 && ev.getKeyCode() <= KeyEvent.VK_9){
					setRowSelectedByObjID(ev.getKeyCode()-KeyEvent.VK_0, "id");
				}
			}
		});
		addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e){
				if (e.getClickCount() == 2 ){
					int r = getSelectedRow();
					actionPerformed(r, "dblclick", false);
				}
			}
		});
	}
	public Dimension getPreferredSize(){
		int xsum = 0;
		TableColumn column;
		for (int i = 0 ; i < getColumnModel().getColumnCount() ; i++){
			column = getColumnModel().getColumn(i);
			column.setPreferredWidth(colw[i]);
			xsum += colw[i];
		}

		int ysum = adata.size()*40;
//		if (ysum > xsum) ysum = xsum;
//		System.out.println(xsum+" x "+ysum);

		return new Dimension(xsum, ysum);
	}
	//オブジェクトが一致する行をハイライト
	public void setRowSelectedByObj(Object obj){
		if (obj == null) {
			changeSelection(-1,-1,false,false);
			return;
		}
		int pos = -1;
		for(int i=0;i<adata.size();i++){
			if (obj == adata.get(i)) pos = i;
		}
		if (pos == -1) changeSelection(-1,-1,false,false);
		else changeSelection(pos, fieldNames.length-1, false, false);
	}
	//0~9の数字で選択
	public void setRowSelectedByObjID(int keynum, String field){
		changeSelection(keynum, fieldNames.length-1, false, false);
	}
	public void actionPerformed(int row, String label, boolean isCloseDialog){
		E a = adata.get(row);

		if (isCloseDialog && sel != null){
			sel.selectedObj = a;
			sel.setVisible(false);
		}

		Method m = null;
		try {
			m = a.getClass().getMethod("action", String.class);
		} catch (NoSuchMethodException e) {
			//			e.printStackTrace();
			System.out.println("No method action on "+a.getClass());
			return;
		} catch (SecurityException e) {
			e.printStackTrace();
			return;
		}
		try {
			m.invoke(a, label);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}


class ButtonsPanel<E> extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static Font sans30 = new Font("sansserif", Font.BOLD, 16);
	ArrayList<JButton> buttons;
	SelectableJTable<E> sjt;
	public ButtonsPanel(SelectableJTable<E> _sjt) {
		super();
		sjt = _sjt;
		setOpaque(true);
		buttons = new ArrayList<JButton>();
		for(String bl: sjt.buttonNames){
			JButton b = new JButton(bl);
			b.setFocusable(false);
			b.setRolloverEnabled(false);
			b.setFont(sans30);
			add(b);
			buttons.add(b);
		}
	}
	//     @Override public void updateUI() {
	//         super.updateUI();
	//     }
}
class ButtonsRenderer<E> extends ButtonsPanel<E> implements TableCellRenderer {
	private static final long serialVersionUID = -6042967661406626776L;
	private static final Color evenColor = new Color(240, 240, 255);
	public ButtonsRenderer(SelectableJTable<E> _sjt) {
		super(_sjt);
		setName("Table.cellRenderer");
	}
	@Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
		//		this.setBackground(isSelected?table.getSelectionBackground():table.getBackground());
		//		return this;

		if(isSelected) {
			setForeground(table.getSelectionForeground());
			setBackground(table.getSelectionBackground());
		}else{
			setForeground(table.getForeground());
			setBackground((row%2==0)?evenColor:table.getBackground());
		}
		//		setHorizontalAlignment((value instanceof Number)?RIGHT:LEFT);
		return this;

	}
}
class StripeTableRenderer extends DefaultTableCellRenderer {
	private static final long serialVersionUID = -4108708987885878028L;
	private static final Color evenColor = new Color(240, 240, 255);
	@Override public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus,
			int row, int column) {
		super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
		if(isSelected) {
			setForeground(table.getSelectionForeground());
			setBackground(table.getSelectionBackground());
		}else{
			setForeground(table.getForeground());
			setBackground((row%2==0)?evenColor:table.getBackground());
		}
		//		setHorizontalAlignment((value instanceof Number)?RIGHT:LEFT);
		setHorizontalAlignment((value instanceof Number)?CENTER:LEFT);

		return this;
	}
}
class ButtonsEditor<T> extends ButtonsPanel<T> implements TableCellEditor, ActionListener {
	private static final long serialVersionUID = 1810054362980131119L;
	SelectableJTable<T> sjt;
	public ButtonsEditor(final SelectableJTable<T> _sjt) {
		super(_sjt);
		sjt = _sjt;
		//---->
		//DEBUG: view button click -> control key down + edit button(same cell) press -> remain selection color
		MouseListener ml = new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				ButtonModel m = ((JButton)e.getSource()).getModel();
				if(m.isPressed() && _sjt.isRowSelected(_sjt.getEditingRow()) && e.isControlDown()) {
					setBackground(_sjt.getBackground());
				}
			}
		};
		for(JButton jb : buttons){
			jb.addMouseListener(ml);
			jb.addActionListener(this);
		}
		//<----
		addMouseListener(new MouseAdapter() {
			@Override public void mousePressed(MouseEvent e) {
				fireEditingStopped();
			}
		});
	}
	@Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
		this.setBackground(table.getSelectionBackground());
		return this;
	}
	@Override public Object getCellEditorValue() {
		return "";
	}

	//Copid from AbstractCellEditor
	//protected EventListenerList listenerList = new EventListenerList();
	transient protected ChangeEvent changeEvent = null;

	@Override public boolean isCellEditable(EventObject e) {
		return true;
	} 
	@Override public boolean shouldSelectCell(EventObject anEvent) {
		return true;
	}
	@Override public boolean stopCellEditing() {
		fireEditingStopped();
		return true;
	}
	@Override public void  cancelCellEditing() {
		fireEditingCanceled();
	}
	@Override public void addCellEditorListener(CellEditorListener l) {
		listenerList.add(CellEditorListener.class, l);
	}
	@Override public void removeCellEditorListener(CellEditorListener l) {
		listenerList.remove(CellEditorListener.class, l);
	}
	public CellEditorListener[] getCellEditorListeners() {
		return listenerList.getListeners(CellEditorListener.class);
	}
	protected void fireEditingStopped() {
		// Guaranteed to return a non-null array
		Object[] listeners = listenerList.getListenerList();
		// Process the listeners last to first, notifying
		// those that are interested in this event
		for(int i = listeners.length-2; i>=0; i-=2) {
			if(listeners[i]==CellEditorListener.class) {
				// Lazily create the event:
				if(changeEvent == null) changeEvent = new ChangeEvent(this);
				((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
			}
		}
	}
	protected void fireEditingCanceled() {
		// Guaranteed to return a non-null array
		Object[] listeners = listenerList.getListenerList();
		// Process the listeners last to first, notifying
		// those that are interested in this event
		for(int i = listeners.length-2; i>=0; i-=2) {
			if(listeners[i]==CellEditorListener.class) {
				// Lazily create the event:
				if(changeEvent == null) changeEvent = new ChangeEvent(this);
				((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
			}
		}
	}
	@Override
	public void actionPerformed(ActionEvent arg0) {
		int row = sjt.convertRowIndexToModel(sjt.getEditingRow());
		fireEditingStopped();
		sjt.actionPerformed(row, arg0.getActionCommand(), true);
	}
}
