package asandatabasebrowser.view;

import java.awt.Component;
import java.awt.event.*;
import java.util.*;
import java.sql.*;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;

import asandatabasebrowser.*;
import asandatabasebrowser.model.*;

import net.sourceforge.swingx.SxOptionPane;
import net.sourceforge.swingx.SxTextUtilities;


/**
 * DBの中身をツリー状に表示する.
 * 大規模なシステムだとテーブルの数だけでも１００を超え、自分の興味のあるテーブルが
 * すぐには見つからない。また、スキーマやカタログとは別に、テーブルを分類したい。
 * DBごとに設定ファイルを作り、それに従って便利なビューを提供する。
 */
public class FavoriteTree extends JTree {
    AsanDatabaseBrowser tools;
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("お気に入り");
    private DbTreeView.CellRednerer renderer = new DbTreeView.CellRednerer();
    /** ツリーを作成する. */
    public FavoriteTree(AsanDatabaseBrowser tools) {
        assert tools!=null;
        this.tools = tools;
        setToolTipText("つりー");   // 何か設定しないと、getToolTipText(MouseEvent)が呼ばれない
        setCellRenderer(renderer);
        setCellEditor(new FavTreeCellEditor(renderer));
        setEditable(true);

        setModel(new DefaultTreeModel(root));
        this.addMouseListener(new FavListMouseListener(this));
        addTreeSelectionListener(new TreeSelectionListener() {
        	public void valueChanged(TreeSelectionEvent e) {
        		FavoriteTree.this.tools.getTreeFrame().updateMainFrame();
        	}
        });
        buildTree();
    }
    
	public void buildTree() {
    	root.removeAllChildren();
        List favList = tools.doc.favList;
        for (int i=0; i<favList.size(); i++) {
        	FavoriteGroup g = (FavoriteGroup) favList.get(i);
        	DefaultMutableTreeNode group = new DefaultMutableTreeNode(g);
            root.add(group);
            for (int j=0; j<g.list.size(); j++) {
            	IFavorite t = g.get(j);
                group.add(new DefaultMutableTreeNode(t, false));
            }
        }
    	DefaultTreeModel model = (DefaultTreeModel) getModel();
        model.reload();
		expandRow(0);
    }
    
    /** ノードごとに異なるツールチップを返す. */
    public String getToolTipText(MouseEvent e) {
        TreePath path = getPathForLocation(e.getX(), e.getY());
        if (path==null) return null;
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        Object obj = node.getUserObject();
        if (obj instanceof String) {
        	return (String) obj;
        } else if (obj instanceof FavoriteGroup) {
        	FavoriteGroup group = (FavoriteGroup) obj;
        	return group.name;
        } else if (obj instanceof FavoriteTable) {
        	FavoriteTable fav = (FavoriteTable) obj;
        	return  "<html>"+
        		"DB:<b>"+fav.dbName+"</b><br>"+
		        "カタログ:<b>"+fav.tableCatalog+"</b><br>"+
		        "スキーマ:<b>"+fav.tableSchema+"</b><br>"+
		        "テーブル名:<b><font color=\"BLUE\">"+fav.tableName+"</font></b><br>"+
		        "タイプ:<b>"+fav.tableType+"</b><br>"+
		        //"remarks:<b>"+info.getRemarks()+"</b>"+
		        "</html>";
        } else if (obj instanceof FavoriteSQL) {
        	FavoriteSQL sql = (FavoriteSQL) obj;
        	return sql.name;
        } else {
        }
        return "?";
    }
    /**
     * 選択されたノードに登録されたユーザオブジェクトを返します.
     * 選択されていない場合はnullを返します。
     * @return	ユーザオブジェクト	
     */
    public Object getSelectionNodeObject() {
    	TreePath path = getSelectionPath();
    	if (path == null) return null;
    	DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
    	return node.getUserObject();
    }
    /**
     * 選択された複数のノードに登録されたユーザオブジェクトを返します.
     * 選択されていない場合はnullを返します。
     * @return	ユーザオブジェクト	
     */
    public Object[] getSelectionNodeObjects() {
		TreePath[] paths = getSelectionPaths();
		if (paths == null || paths.length == 0) return null;
		ArrayList result = new ArrayList();
		for (int i=0; i<paths.length; i++) {
			TreePath path = paths[i];
	    	DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
	    	result.add(node.getUserObject());
		}
    	return result.toArray(new Object[result.size()]);
    }
    /**
     * 指定されたノード配下のノードをリストにして返します.
     * 展開されていないノードもすべて返します.
     * @param	node	null不可
     * @return	
     */
    public ArrayList getAllNode(DefaultMutableTreeNode node) {
    	assert node != null;
    	ArrayList result = new ArrayList();
    	result.add(node);
    	for (int i=0; i<node.getChildCount(); i++) {
    		DefaultMutableTreeNode n = (DefaultMutableTreeNode) node.getChildAt(i);
    		result.addAll(getAllNode(n));
    	}
    	return result;
    }
    /**
     * すべてのノードの中から、指定されたユーザオブジェクトを持つノードを返します。
     * ノードが見つからなかった場合、null を返します。 
     */
    public DefaultMutableTreeNode getNodeByUserObject(Object userObj) {
    	assert userObj != null;
    	ArrayList list = getAllNode(root);
    	for (int i=0; i<list.size(); i++) {
    		DefaultMutableTreeNode node = (DefaultMutableTreeNode) list.get(i);
    		if (userObj.equals(node.getUserObject())) {
    			return node;
    		}
    	}
    	return null;	// 見つからなかった
    }
    /**
     * 指定されたノードが何列目かを返す。
     * 見つからなかった場合、(-1)を返す。
     */
    public int getRow(DefaultMutableTreeNode node) {
    	for (int r=0; r<getRowCount(); r++) {
    		Object obj = getPathForRow(r).getLastPathComponent();
     		if (obj == node) return r;
    	}
    	return -1;	// 見つからなかった
    }
    /** 編集できるか？ */
    public boolean isPathEditable(TreePath path) {
    	DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
    	return
    		(node.getUserObject() instanceof FavoriteGroup) ||
    		(node.getUserObject() instanceof FavoriteTable);
    }
    public void startEditingAtPath(TreePath path) {
    	System.out.println("startEditingAtPath()"+path);
    	super.startEditingAtPath(path);
    }
    public boolean stopEditing() {
    	System.out.println("stopEditing()");
    	return super.stopEditing();
    }
    class FavTreeCellEditor extends DefaultTreeCellEditor {
    	DefaultMutableTreeNode node;
    	FavTreeCellEditor(DefaultTreeCellRenderer renderer) {
    		super(FavoriteTree.this, renderer); 
    	}
    	public Component getTreeCellEditorComponent(JTree tree, Object value, 
    			boolean isSelected, boolean expanded, boolean leaf, int row) {
    		this.node = (DefaultMutableTreeNode) value;
    		Object obj = node.getUserObject();
    		String name = null;
    		if (obj instanceof FavoriteGroup) {
    			name = ((FavoriteGroup) obj).name;
    		} else if (obj instanceof FavoriteTable) {
    			name = ((FavoriteTable) obj).name;
    		} else {
    			assert false: obj.getClass();
    		}
    		Component comp = super.getTreeCellEditorComponent(tree, name, isSelected, expanded, leaf, row);
    		System.out.println(comp);
    		
    		return comp;    		
    	}
    	public Object getCellEditorValue() {
    		Object value = super.getCellEditorValue();
    		System.out.println(value);
    		Object obj = node.getUserObject();
    		if (obj instanceof FavoriteGroup) {
    			((FavoriteGroup) obj).name = (String) value;
    		} else if (obj instanceof FavoriteTable) {
    			((FavoriteTable) obj).name = (String) value;
    		} else {
    			assert false: obj.getClass();
    		}
    		tools.doc.setModified(true);
    		tools.getTreeFrame().updateMainFrame();
    		return obj;
    	}
    }

    /**
     * テーブルをダブルクリックしたら、テーブルフレームを開く。
     */
    class FavListMouseListener extends MouseAdapter {
        FavoriteTree tree;
        FavListMouseListener(FavoriteTree tree) {
            this.tree = tree;
        }
        public void mouseClicked(MouseEvent e) {
            //System.out.println(e);
            // 左ボタンがダブルクリックされたら
            if (e.getClickCount()==2 && (e.getModifiers()&MouseEvent.BUTTON1_MASK)!=0) {
                TreePath selPath = getPathForLocation(e.getX(), e.getY());
                if (selPath==null) return;
                Object leaf = selPath.getLastPathComponent();
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) leaf;
                Object userObject = node.getUserObject();
                //System.out.println(leaf.getClass());
                if (userObject instanceof FavoriteTable) {
                    FavoriteTable fav = (FavoriteTable) userObject;
                    System.out.println(node);
                    // 接続先を取得する。
                    VvDatabase db = null;
                    for (int i=0; i<AsanDatabaseBrowser.theApp.doc.databaseList.size(); i++) {
                    	VvDatabase d = (VvDatabase) AsanDatabaseBrowser.theApp.doc.databaseList.get(i);
                    	if (d.name.equals(fav.dbName)) {
                    		db = d;
                    		break;
                    	}
                    }
                    if (db == null) return;	// 接続先が存在しない！？
                    // コネクションを取得する。
                    Connection conn = db.getConnection();
                    // 接続されていなかったら接続する。
                    if (conn == null) {
                    	try {
							tools.connect(db);
							conn = db.getConnection();
							assert conn != null;
							// Databaseツリー側の枝を生成させる。
					    	DbTreeView dbtree = tools.getTreeFrame().getTree();
				        	DefaultMutableTreeNode dbnode = dbtree.getNodeByUserObject(db);
							dbtree.接続したので枝を作る(dbnode, db);
						} catch (DbException e1) {
							tools.showMessage(tree, "データベースの接続に失敗しました", e1, SxOptionPane.ERROR_MESSAGE);
							return;
						}
                    }
                    try {
	                    // 該当するカタログ/スキーマを取得する。
	                    SchemaInfo schema = null;
	                    ArrayList list = db.getSchemaInfoList();
	                    for (int i=0; i<list.size(); i++) {
	                    	SchemaInfo s = (SchemaInfo) list.get(i);
	                    	if (SxTextUtilities.equalsString(fav.tableCatalog, s.getCatalogName()) &&
	                    			SxTextUtilities.equalsString(fav.tableSchema, s.getSchemaName()) ) {
	                    		schema = s;
	                    		break;
	                    	}
	                    }
	                    if (schema == null) throw new DbException("そのようなカタログ/スキーマは存在しません。catalog="+
	                    		fav.tableCatalog+" schema="+fav.tableSchema);
	                    // 該当するテーブルを取得する。
	                    TableInfo table = schema.getTableTypeInfo(fav.tableType).getTableInfo(fav.tableName);
	                    tools.openTableFrame(table);
                    }catch (DbException ex) {
                    	tools.showMessage(tree, "テーブルを開くことができません", ex, SxOptionPane.ERROR_MESSAGE);
                    }
                }
                else if (userObject instanceof FavoriteSQL) {
                	FavoriteSQL fav = (FavoriteSQL) userObject;
                    System.out.println(node);
                    // 接続先を取得する。
                    VvDatabase db = null;
                    for (int i=0; i<AsanDatabaseBrowser.theApp.doc.databaseList.size(); i++) {
                    	VvDatabase d = (VvDatabase) AsanDatabaseBrowser.theApp.doc.databaseList.get(i);
                    	if (d.name.equals(fav.dbName)) {
                    		db = d;
                    		break;
                    	}
                    }
                    if (db == null) return;	// 接続先が存在しない！？
                    // コネクションを取得する。
                    Connection conn = db.getConnection();
                    // 接続されていなかったら接続する。
                    if (conn == null) {
                    	try {
							tools.connect(db);
							conn = db.getConnection();
							assert conn != null;
							// Databaseツリー側の枝を生成させる。
					    	DbTreeView dbtree = tools.getTreeFrame().getTree();
				        	DefaultMutableTreeNode dbnode = dbtree.getNodeByUserObject(db);
							dbtree.接続したので枝を作る(dbnode, db);
						} catch (DbException e1) {
							tools.showMessage(tree, "データベースの接続に失敗しました", e1, SxOptionPane.ERROR_MESSAGE);
							return;
						}
                    }
                    try {
	                    // 該当するカタログ/スキーマを取得する。
	                    tools.openSqlFrame(db, fav);
                    }catch (DbException ex) {
                    	tools.showMessage(tree, "テーブルを開くことができません", 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) {        
            // 指している場所は？
            TreePath selPath = getPathForLocation(e.getX(), e.getY());
            if (selPath==null) return;
            // 現在選択しているノードの中に、マウス位置のノードは含まれているか？
            TreePath[] selPaths = getSelectionPaths();
            boolean contain = false;
            for (int i=0; i<selPaths.length; i++) {
            	if (selPaths[i] == selPath) {
            		contain = true;
            		break;
            	}
            }
            System.out.println("contain="+contain);
            // 含まれていないときには、選択しなおす。
            if (! contain) {
                setSelectionPath(selPath);
            }
            // ポップアップ・メニューを表示する。
           	DbTreeFrame frame = tools.getTreeFrame();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent();
            Object obj = node.getUserObject();
            if (obj instanceof FavoriteTable) {
                 // ポップアップ・メニューを表示する。


               	JPopupMenu popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(frame.openTableAction);
                popup.add(frame.openSqlFrameAction);
                popup.add(frame.openExecuteFrameAction);
                popup.addSeparator();
                JMenu sendFav = new JMenu("お気に入りに送る");
                sendFav.setIcon(Global.favoriteIcon);
                DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
                FavoriteGroup srcg = (FavoriteGroup) parent.getUserObject();
                FavoriteTable favTable = (FavoriteTable) node.getUserObject();
                for (int i=0; i<tools.doc.favList.size(); i++) {
                    FavoriteGroup dstg = (FavoriteGroup) tools.doc.favList.get(i);
                    sendFav.add(new JMenuItem(new MoveFavTreeAction(srcg, dstg, favTable)));
                }
                popup.add(sendFav);
                popup.add(new RemoveFavTableAction(srcg, favTable));
                popup.addSeparator();
                popup.add(frame.countListAction);
                popup.add(frame.openTableDefinitionAction);
                popup.add(frame.openDatabaseDefinitionAction);
                popup.add(frame.createDdlAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
            else if (obj instanceof String && obj.equals("お気に入り")) {
            	// お気に入り
                // ポップアップ・メニューを表示する。
               	JPopupMenu popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(new CreateGroupAction());
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
            else if (obj instanceof FavoriteGroup) {
            	FavoriteGroup group = (FavoriteGroup) obj;
                // ポップアップ・メニューを表示する。
               	JPopupMenu popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(new RemoveFavGroupAction(group));
                popup.add(frame.countListAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
            }
        }
	}
    class CreateGroupAction extends AbstractAction {
    	CreateGroupAction() {
    		super("新しいグループの作成", Global.groupIcon);
    	}
    	public void actionPerformed(ActionEvent e) {
    		FavoriteGroup group = new FavoriteGroup("newGroup");
    		tools.doc.favList.add(group);
    	    DefaultTreeModel model = (DefaultTreeModel) getModel();
    	    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) model.getRoot();
    	    DefaultMutableTreeNode new_node = new DefaultMutableTreeNode(group);
    	    model.insertNodeInto(new_node, parent, parent.getChildCount());
    	    model.reload();    		
    	}
    }
    class MoveFavTreeAction extends AbstractAction {
    	FavoriteTable favTable;
        FavoriteGroup srcgroup;
        FavoriteGroup dstgroup;
        MoveFavTreeAction(FavoriteGroup srcg, FavoriteGroup dstg, FavoriteTable favTable) {
              super(dstg.name, Global.groupIcon);
              this.srcgroup = srcg;
              this.dstgroup = dstg;
              this.favTable = favTable;
              setEnabled(! srcgroup.equals(dstgroup));
        }
        public void actionPerformed(ActionEvent e) {
            srcgroup.list.remove(favTable);
            dstgroup.add(favTable);
            tools.getTreeFrame().favTree.buildTree();
            tools.doc.setModified(true);
            tools.getTreeFrame().updateMainFrame();
        }
    }
    class RemoveFavTableAction extends AbstractAction {
    	FavoriteGroup group;
    	FavoriteTable table;
    	RemoveFavTableAction(FavoriteGroup group, FavoriteTable table) {
    		super("お気に入りの削除 ...");
    		this.group = group;
    		this.table = table;
    	}
        public void actionPerformed(ActionEvent e) {
        	// 選択されたお気に入りを集める。
    		TreePath[] paths = getSelectionPaths();
    		if (paths == null || paths.length == 0) return;
    		ArrayList list = new ArrayList();
        	String message = "本当に消していい？";
        	for (int i=0; i<paths.length; i++) {
    			TreePath path = paths[i];
    			if (path.getPathCount() == 3) {
    				DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
    				list.add(node);
    				FavoriteTable table = (FavoriteTable) node.getUserObject();
    				message += "\n" + table.name;
    			}
     		}
        	// お気に入りが選択されていなければダメ
        	if (list.size() == 0) {
        		tools.showMessage(FavoriteTree.this, 
        				"お気に入りが選択されていません", 
        				null, JOptionPane.ERROR_MESSAGE);
        		return;
        	}
        	// 本当に消していいかどうか確認する。
        	int rc = JOptionPane.showConfirmDialog(tools.getTreeFrame(), 
        			message,
                    "お気に入りの削除", JOptionPane.YES_NO_OPTION);
            if (rc != JOptionPane.YES_OPTION) return;
            // 削除
    	    DefaultTreeModel model = (DefaultTreeModel) getModel();
    	    for (int i=0; i<list.size(); i++) {
            	DefaultMutableTreeNode node = (DefaultMutableTreeNode) list.get(i);
            	DefaultMutableTreeNode gnode = (DefaultMutableTreeNode) node.getParent();
            	FavoriteGroup group = (FavoriteGroup) gnode.getUserObject();
            	FavoriteTable table = (FavoriteTable) node.getUserObject();
            	group.list.remove(table);
        	    model.removeNodeFromParent(node);
            }
    	    model.reload(); 
    	    tools.doc.setModified(true);
            tools.getTreeFrame().updateMainFrame();
        }
	}
    class RemoveFavGroupAction extends AbstractAction {
    	FavoriteGroup group;
    	RemoveFavGroupAction(FavoriteGroup group) {
    		super("お気に入りのグループの削除 ...");
    		this.group = group;
    	}
        public void actionPerformed(ActionEvent e) {
        	int rc = JOptionPane.showConfirmDialog(tools.getTreeFrame(), 
        			"本当に消していい？\n"+
        			group.name,
                    "お気に入りの削除", JOptionPane.YES_NO_OPTION);
            if (rc != JOptionPane.YES_OPTION) return;
            if (group.list.size() > 0) {
	        	rc = JOptionPane.showConfirmDialog(tools.getTreeFrame(), 
	        			"まだ、中身にお気に入りがあるよ。\n"+
	        			"本当に消していいの？？？\n"+
	        			group.name,
	                    "お気に入りの削除", JOptionPane.YES_NO_OPTION);
	            if (rc != JOptionPane.YES_OPTION) return;
            }
            // 削除
    		tools.doc.favList.remove(group);
            tools.getTreeFrame().favTree.buildTree();
            tools.doc.setModified(true);
            tools.getTreeFrame().updateMainFrame();
        }
	}
}

