package asandatabasebrowser.view;

import java.util.Comparator;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

import net.sourceforge.swingx.SxOptionPane;

import asandatabasebrowser.*;
import asandatabasebrowser.action.CreateInsertAction;
import asandatabasebrowser.action.CreateUpdateAction;
import asandatabasebrowser.model.*;



public class DbResultSetView extends RitchTable {
	static final int FONT_WIDTH = 8;
	static final int MAX_HEADER_WIDTH = 300;
	/** TODO:null可にすべき */
	public DbData data;
	private final DefaultTableModel model = new DefaultTableModel();
	private final DbColumnValueRenderer columnValueRenderer;

	DbResultSetView() {
		this.setModel(model);
		//System.out.println("autoResizeMode="+getAutoResizeMode());
		setAutoResizeMode(AUTO_RESIZE_OFF);
		getTableHeader().addMouseListener(new DbTableHeaderMouseListener());
		this.getTableHeader().setReorderingAllowed(false); // ヘッダのドラッグは無効 
		this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		this.setColumnSelectionAllowed(true);
		this.setRowSelectionAllowed(true);
		setToolTipText("てーぶる");
		// レンダラー
		columnValueRenderer = new DbColumnValueRenderer();
		this.setDefaultRenderer(Object.class, columnValueRenderer);
		addMouseListener(new RecordMouseListener());

		updateHeader();
	}
	/** ヘッダを更新する. */
	void updateHeader() {
		model.setColumnCount(0);
		if (data != null) {
			for (int i=0; i<data.tableInfo.getColumnCount(); i++) {
				model.addColumn(data.tableInfo.getColumnName(i));
			}
			// 最適なサイズを指定
			for (int c=0; c<getColumnModel().getColumnCount(); c++) {
				int w = getFitWidth(c);
				getColumnModel().getColumn(c).setPreferredWidth(w);
			}
		}
	}
	/** SQLの検索結果を更新する。 */
	void updateData(DbData data) {
		// カラム情報が変わったか？
		boolean changed = false;
		if ((this.data == null) != (data == null)) {
			changed = true;	// 一方のみがnullのときは、変わった！
		}
		if (this.data != null && data != null) {
			if (! this.data.tableInfo.equals(data.tableInfo)) {
				changed = true;
			}
		}
		this.data = data;
		// 変わったら、ヘッダを更新する
		if (changed) {
			updateHeader();
		}
		model.setRowCount(data.getRecordCount());
		for (int r=0; r<data.getRecordCount(); r++) {
			Object[] record = data.getRecord(r);
			for (int c=0; c<record.length; c++) {
				this.setValueAt(record[c], r, c);
			}
		}
	}
	private int getFitWidth(int col) {
		int prefWidth = 0;
		if (true) {		// ヘッダの幅も合わせるか？
			int len = data.tableInfo.getColumnName(col).length();
			prefWidth = len * FONT_WIDTH + 4;
		}
		for (int r=0; r<data.getRecordCount(); r++) {
			Object[] record = data.getRecord(r);
			int len = (record[col] != null) ? record[col].toString().length() : 4;
			int size = len * FONT_WIDTH + 4;
			if (size > prefWidth) {
				prefWidth = size;
				if (prefWidth >= MAX_HEADER_WIDTH) break;
			}
		}
		return Math.min(prefWidth, MAX_HEADER_WIDTH);		// +2は左右のPadding
	}
	/** セルは編集不可能. */
	public boolean isCellEditable(int row, int column) {
		 return false;
	}
	
	/** マウス位置のセルごとにツールチップの文字列を返す。*/
	public String getToolTipText(MouseEvent e) {
		int col = columnAtPoint(e.getPoint());
		if (col == -1) return null;
		int row = rowAtPoint(e.getPoint());
		if (row == -1) return null;
		Object value = model.getValueAt(row, col);
		if (value == null) return "<html><font color=\"GRAY\">NULL</font></html>";
		return value.toString();
	}
	private int getHeaderBorder(int x) {
		int total = 0;
		for (int c=0; c<getColumnModel().getColumnCount(); c++) {
			int w = getColumnModel().getColumn(c).getWidth();
			total += w;
			if (Math.abs(total - x) <= 3)
				return c;
		}
		return -1;
	}

	class DbTableHeaderMouseListener extends MouseAdapter implements Comparator {
		private int lastCol = -1;
		private boolean asc = false;
		/** クリックされたらソートを行う。*/
		public void mouseClicked(MouseEvent e) {
			System.out.println(e);
			if (e.isConsumed()) return;	// すでにイベントが消費していたら
			// 左ボタンがシングルクリックされたら
			if (e.getClickCount()==1 && (e.getModifiers()&MouseEvent.BUTTON1_MASK)!=0) {
				// カラムの境界のときは何もしない。
				if (getHeaderBorder(e.getX()) != -1) return;
				int col = getTableHeader().columnAtPoint(e.getPoint());
				//System.out.println("sort("+e.getX()+","+e.getY()+") col="+col);
				if (col!=-1) {
					if (col==lastCol) {
						asc = ! asc;
					}
					else {
						lastCol = col;
						asc = true;
					}
					data.sort(this);
					updateData(data);
					model.fireTableDataChanged();
					getSelectionModel().clearSelection();
					e.consume();
				}
			}
			// 左ボタンがダブルクリックされたら、
			else if (e.getClickCount()==2 && e.getButton()==MouseEvent.BUTTON1) {
				// カラムの境界か？
				int col = getHeaderBorder(e.getX());
				System.out.println(col);
				if (col != -1) {
					int w = getFitWidth(col);
					getColumnModel().getColumn(col).setPreferredWidth(w);
					getColumnModel().getColumn(col).setWidth(w);
//					System.out.println("col="+col+" w="+w+
//							" width="+getColumnModel().getColumn(col).getWidth()+
//					" pref="+getColumnModel().getColumn(col).getPreferredWidth());
				}
				e.consume();
			}
		}
		// Comparatorインターフェースのメソッド
		public int compare(Object o1, Object o2) {
			return asc ? compare0(o1, o2) : -compare0(o1, o2);
		}
		public int compare0(Object o1, Object o2) {
			Object[] rec1 = (Object[]) o1;
			Object[] rec2 = (Object[]) o2;
			Object fld1 = rec1[lastCol];
			Object fld2 = rec2[lastCol];
			if (fld1==fld2) return 0;
			if (fld1==null) return -1;
			if (fld2==null) return 1;
			if (fld1 instanceof Comparable) {
				Comparable c1 = (Comparable) fld1;
				Comparable c2 = (Comparable) fld2;
				return c1.compareTo(c2);
			}
			System.out.println("比較できない型です "+fld1.getClass());
			return 0;
		}
		// Comparatorインターフェースのメソッド
		public boolean equals(Object obj) {
			return this==obj;
		}
	}

	/**
	 * カラム名をダブルクリックしたら、カラム情報を開く。
	 */
	class RecordMouseListener extends MouseAdapter {
		public void mouseClicked(MouseEvent e) {
			//System.out.println(e);
			try {
				// 左ボタンがダブルクリックされたら、LargeObjectの場合、その内容を表示する。
				if (e.getClickCount()==2 && (e.getModifiers()&MouseEvent.BUTTON1_MASK)!=0) {
					int col = columnAtPoint(e.getPoint());
					if (col == -1) return;
					int row = rowAtPoint(e.getPoint());
					if (row == -1) return;
					//System.out.println("col="+col+" row="+row);
					ITableInfo table = data.getTableInfo();
					Object value = getValueAt(row, col);
					//　BLOB/CLOBなどのとき、内容を表示する。 
					AsanDatabaseBrowser.theApp.openLargeObject(value, table, data.getRecord(row), col);
				}
			}
			catch (Exception ex) {
				AsanDatabaseBrowser.theApp.showMessage(DbResultSetView.this, "エラーが発生しました",
						ex, SxOptionPane.ERROR_MESSAGE);
			}
		}
		
		public void mouseReleased(MouseEvent e) {
			// ポップアップメニューを表示する。
			if (e.isPopupTrigger()) {
				mousePopup(e);
			}
		}
		public void mousePressed(MouseEvent e) {
			// ポップアップメニューを表示する。
			if (e.isPopupTrigger()) {
				mousePopup(e);
			}
		}
		void mousePopup(MouseEvent e) {
			//System.out.println("Popup!");
			JPopupMenu popup = new JPopupMenu();
			popup.add(new CopyClipboardAction(DbResultSetView.this));
			popup.add(new SelectAllAction(DbResultSetView.this));
			if (data.tableInfo instanceof TableInfo) {
				popup.addSeparator();
				VvDatabase db = data.tableInfo.getDatabase();
				popup.add(new CreateInsertAction(AsanDatabaseBrowser.theApp, db, DbResultSetView.this));
				popup.add(new CreateUpdateAction(AsanDatabaseBrowser.theApp, db, DbResultSetView.this));
			}
			popup.show(DbResultSetView.this, e.getX(), e.getY());
		}
	}
}


