/*
 * 作成日: 2005/11/16
 */
package asandatabasebrowser.view;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.AbstractAction;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import net.sourceforge.swingx.SxOptionPane;

import asandatabasebrowser.Global;
import asandatabasebrowser.AsanDatabaseBrowser;
import asandatabasebrowser.model.DbException;
import asandatabasebrowser.model.FavoriteGroup;
import asandatabasebrowser.model.FavoriteSQL;
import asandatabasebrowser.model.FavoriteTable;
import asandatabasebrowser.model.SchemaInfo;
import asandatabasebrowser.model.TableInfo;
import asandatabasebrowser.model.TableTypeInfo;
import asandatabasebrowser.model.VvDatabase;
import asandatabasebrowser.model.VvDocument;




/**
 * DBやテーブルをツリー状に表示する。
 */
public class DbTreeView extends JTree {
	private static final int NODE_DEPTH_ROOT 	= 1;	// るーと
	public static final int NODE_DEPTH_DB 		= 2;	// データベース
	private static final int NODE_DEPTH_SCHEMA 	= 3;	// スキーマ/カタログ
	private static final int NODE_DEPTH_FOLDER 	= 4;	// フォルダ
	private static final int NODE_DEPTH_TABLE 	= 5;	// テーブル/ビューなど
	
    
	AsanDatabaseBrowser dbTools;
	DefaultTreeModel model;
    DefaultMutableTreeNode root;
    /** ツリーを作成する. */
    public DbTreeView(AsanDatabaseBrowser dbTools) {
    	assert dbTools!=null;
        this.dbTools = dbTools;
        VvDocument doc = AsanDatabaseBrowser.theApp.doc;
        // 最初のノードを作る
        root = new DefaultMutableTreeNode("るーと");
        for (int i=0; i<doc.databaseList.size(); i++) {
        	root.add(new DefaultMutableTreeNode(doc.databaseList.get(i)));
        }
        setCellRenderer(new CellRednerer());
        model = new DefaultTreeModel(root);
        setModel(model);
        setSelectionRow(0);
        addMouseListener(new TreeMouseListener(this));
        setToolTipText("つりー");   // 何か設定しないと、getToolTipText(MouseEvent)が呼ばれない
        addTreeSelectionListener(new TreeSelectionListener() {
        	public void valueChanged(TreeSelectionEvent e) {
        		DbTreeView.this.dbTools.getTreeFrame().updateMainFrame();
        	}
        });
    }

    public void 接続したので枝を作る(DefaultMutableTreeNode node, VvDatabase db) 
    						throws DbException {
    	System.out.println("createDatabaseNodes()");
        VvDocument doc = AsanDatabaseBrowser.theApp.doc;
           	// スキーマorカタログを作成
		ArrayList list = db.getSchemaInfoList();
		for (int i=0; i<list.size(); i++) {
			SchemaInfo schema = (SchemaInfo) list.get(i);
	    	DefaultMutableTreeNode schema_node = new DefaultMutableTreeNode(schema);
	    	model.insertNodeInto(schema_node, node, node.getChildCount());
	    	// テーブルの型ごとのフォルダを作成
	    	for (int k=0; k<schema.getCount(); k++) {
	    		TableTypeInfo type = schema.getTableTypeInfo(k);
	    		DefaultMutableTreeNode type_node = new DefaultMutableTreeNode(type);
    	    	model.insertNodeInto(type_node, schema_node, schema_node.getChildCount());

	    		for (int j=0; j<type.getCount(); j++) {
	            	TableInfo table = type.getTableInfo(j);
	            	// オラクルのリサイクルビン テーブルのときには表示しない。
	            	if (type.type.equals("TABLE") && 
	            			db.hideOracleBinTable &&
	            			table.getTableName().matches("^BIN\\$.*==\\$0$")) {
	            		continue;
	            	}
	            	
	            	// 枝を作成
	            	model.insertNodeInto(new DefaultMutableTreeNode(table), type_node, type_node.getChildCount());
	            }
	    		
	    	}
		}
		int row = getRow(node);
		if (row != -1) this.expandRow(row);
    }

    public void 切断したので枝を削除する(DefaultMutableTreeNode node) {
    	for (int i=node.getChildCount()-1; i>=0; i--) {	// 後ろから
    		model.removeNodeFromParent((DefaultMutableTreeNode) node.getChildAt(i));
    	}
	}
    
    public void 新しいＤＢを追加する(VvDatabase db) {
    	model.insertNodeInto(new DefaultMutableTreeNode(db), root, root.getChildCount());
    }
    
    public void ＤＢを削除する(DefaultMutableTreeNode node) {
    	model.removeNodeFromParent(node);
    }
    public void 名前が変わった() {
    	model.reload();
    }
   
    /** ノードごとに異なるツールチップを返す. */
    public String getToolTipText(MouseEvent e) {
        TreePath path = getPathForLocation(e.getX(), e.getY());
        if (path==null) return null;
    	DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        switch (path.getPathCount()) {
        case NODE_DEPTH_ROOT:	
        	return "<html>るーと</html>";
        case NODE_DEPTH_DB:
        	VvDatabase db = (VvDatabase) node.getUserObject();
            return "<html>driver:<b>"+db.driverName + 
            "</b><br>URL:<b>" + db.url + 
            "</b><br>name:<b>" + db.user + 
            "</b></html>";
        case NODE_DEPTH_SCHEMA:
        	SchemaInfo schema = (SchemaInfo) node.getUserObject();
            return "<html>"+
            	"スキーマ:<b>"+schema.getSchemaName()+"</b><br>"+
				"カタログ:<b>"+schema.getCatalogName()+"</b>"+
				"</html>";
        case NODE_DEPTH_FOLDER:
        	TableTypeInfo type = (TableTypeInfo) node.getUserObject();
        	return "<html>タイプ:<b>"+type.type+"</b><br></html>";
        case NODE_DEPTH_TABLE:
        	TableInfo info = (TableInfo) node.getUserObject();
            return  "<html>"+
            "カタログ:<b>"+info.getCatalogName()+"</b><br>"+
            "スキーマ:<b>"+info.getSchemaName()+"</b><br>"+
            "テーブル名:<b><font color=\"BLUE\">"+info.getTableName()+"</font></b><br>"+
            "タイプ:<b>"+info.getTableType()+"</b><br>"+
            "remarks:<b>"+info.getRemarks()+"</b>"+
            "</html>";
        default:
        	assert false: path.getPathCount();
            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()]);
    }
    public VvDatabase getSelectedDatabase(boolean searchParent) {
        TreePath path = this.getSelectionPath();
        if (path==null) return null;
        if (path.getPathCount() <= NODE_DEPTH_ROOT) return null;
        if (! searchParent) {
    	    if (path.getPathCount() != NODE_DEPTH_DB) return null;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getPathComponent(NODE_DEPTH_DB-1);
        VvDatabase db = (VvDatabase) node.getUserObject();
        return db;
    }

    public SchemaInfo getSelectedSchemaInfo() {
        TreePath path = this.getSelectionPath();
        if (path==null) return null;
        if (path.getPathCount() != NODE_DEPTH_SCHEMA) return null;
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        SchemaInfo info = (SchemaInfo) node.getUserObject();
         return info;
    }
    public TableInfo getSelectedTableInfo() {
        TreePath path = this.getSelectionPath();
        if (path==null) return null;
        if (path.getPathCount() != NODE_DEPTH_TABLE) return null;
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        TableInfo info = (TableInfo) node.getUserObject();
        return info;
    }
    /**
     * 指定されたノードが何列目かを返す。
     * 見つからなかった場合、(-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;	// 見つからなかった
    }
    /**
     * 指定されたノード配下のノードをリストにして返します.
     * 展開されていないノードもすべて返します.
     * @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;	// 見つからなかった
    }
    /**
     * ツリーのセルを描画する
     */
    static class CellRednerer extends DefaultTreeCellRenderer {
       /** 現在のツリーセルの値を value に設定します。 */
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
            Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
            JLabel label = (JLabel) c;
            if (value instanceof DefaultMutableTreeNode){
            	DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            	Object obj = node.getUserObject();
            	if (obj instanceof String) {
            		if (obj.equals("るーと")) {
            			label.setIcon(Global.rootIcon);
            		} else if (obj.equals("お気に入り")){
            			label.setIcon(Global.favoriteIcon);
            		}
            	}
                else if (obj instanceof VvDatabase) {
                	VvDatabase db = (VvDatabase) obj;
                	if (db.getConnection() != null) {
                		label.setIcon(Global.databaseIcon);
                	} else {
                		label.setIcon(Global.databaseOffIcon);
                	}
                }
                else if (obj instanceof SchemaInfo) {
                	label.setIcon(Global.schemaIcon);
                }
                else if (obj instanceof TableTypeInfo) {
                	TableTypeInfo type = (TableTypeInfo) obj;
                	label.setText(type.type);
                }
                else if (obj instanceof TableInfo) {
                	TableInfo info = (TableInfo) obj;
                	String text = value.toString();
                	if (true) {
                		if (info.getRemarks() != null && info.getRemarks().length() > 0) {
                			text += "(" + info.getRemarks() + ")";
                		}
                	}
                	label.setText(text);
                	String type = info.getTableType();
                    if ( type.equals("TABLE") ) {
                        label.setIcon(Global.tableIcon);
                    }
                    else if (type.equals("VIEW")) {
                        label.setIcon(Global.viewIcon);
                    }
                    else if (type.equals("SYSTEM TABLE")) {
                        label.setIcon(Global.systemTableIcon);
                    }
                    else if (type.equals("GLOBAL TEMPORARY")) {
                        label.setIcon(Global.globalTemporaryIcon);
                    }
                    else if (type.equals("LOCAL TEMPORARY")) {
                        label.setIcon(Global.localTemporaryIcon);
                    }
                    else if (type.equals("ALIAS")) {
                        label.setIcon(Global.aliasIcon);
                    }
                    else if (type.equals("SYNONYM")) {
                        label.setIcon(Global.synonymIcon);
                    }
                    else {
                        label.setIcon(Global.unknownIcon);
                    }
                }
                else if (obj instanceof FavoriteTable) {
                	FavoriteTable fav = (FavoriteTable) obj;
                	label.setText(fav.name);
                	String type = fav.tableType;
                    if ( type.equals("TABLE") ) {
                        label.setIcon(Global.favTableIcon);
                    }
                    else if (type.equals("VIEW")) {
                        label.setIcon(Global.viewIcon);
                    }
                    else if (type.equals("SYSTEM TABLE")) {
                        label.setIcon(Global.systemTableIcon);
                    }
                    else if (type.equals("GLOBAL TEMPORARY")) {
                        label.setIcon(Global.globalTemporaryIcon);
                    }
                    else if (type.equals("LOCAL TEMPORARY")) {
                        label.setIcon(Global.localTemporaryIcon);
                    }
                    else if (type.equals("ALIAS")) {
                        label.setIcon(Global.aliasIcon);
                    }
                    else if (type.equals("SYNONYM")) {
                        label.setIcon(Global.synonymIcon);
                    }
                    else {
                        label.setIcon(Global.unknownIcon);
                    }
                }
                else if (obj instanceof FavoriteSQL) {
                	FavoriteSQL favSql = (FavoriteSQL) obj;
                	label.setText(favSql.name);
                    label.setIcon(Global.favSqlIcon);
                }
                else if (obj instanceof FavoriteGroup){
                	FavoriteGroup group = (FavoriteGroup) obj;
                	label.setText(group.name);
                	label.setIcon(Global.groupIcon);
                } else {
                	System.out.println("value="+value+" class="+value.getClass());
                }
            }
            return c;
        }
    }
    /**
     * テーブルをダブルクリックしたら、テーブルフレームを開く。
     */
    class TreeMouseListener extends MouseAdapter {
    	DbTreeView tree;
        TreeMouseListener(DbTreeView tree) {
            this.tree = tree;
        }
        public void mouseClicked(MouseEvent e) {
            //System.out.println(e.getButton());
            // 左ボタンがダブルクリックされたら
            if (e.getClickCount()==2 && e.getButton() == MouseEvent.BUTTON1) {
                TreePath selPath = getPathForLocation(e.getX(), e.getY());
                if (selPath==null) return;
                // テーブルが選択されたら開く
                if (selPath.getPathCount() != NODE_DEPTH_TABLE) return;
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent();
                TableInfo table = (TableInfo) node.getUserObject();
                //System.out.println(table);
                try {
                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                    dbTools.openTableFrame(table);
                } catch (DbException ex) {
                    AsanDatabaseBrowser.theApp.showMessage(DbTreeView.this, "データベースの照会に失敗しました",
                    		ex, SxOptionPane.ERROR_MESSAGE);
                } finally {
                    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 
                }
                dbTools.getTreeFrame().updateMainFrame();
            }
        }
        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;
            if (selPaths != null) {
	            for (int i=0; i<selPaths.length; i++) {
	            	if (selPaths[i] == selPath) {
	            		contain = true;
	            		break;
	            	}
	            }
            }
            // 含まれていないときには、選択しなおす。
            if (! contain) {
                setSelectionPath(selPath);
            }
            
            DbTreeFrame frame = dbTools.getTreeFrame();
            JPopupMenu popup;
            switch (selPath.getPathCount()) {
            case NODE_DEPTH_ROOT:	// るーと
                // ポップアップ・メニューを表示する。
                popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(frame.databaseAddAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
                break;
            case NODE_DEPTH_DB:	// データベース
                // ポップアップ・メニューを表示する。
                popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(frame.databaseConnectAction);
                popup.add(frame.databaseDisconnectAction);
                popup.addSeparator();
                popup.add(frame.databaseMetadataAction);
                popup.add(frame.openDatabaseDefinitionAction);
                popup.addSeparator();
                popup.add(frame.databaseRefreshAction);
                popup.addSeparator();
                popup.add(frame.databaseDeleteAction);
                popup.add(frame.databasePropertyAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
                break;
            case NODE_DEPTH_SCHEMA:	// スキーマorカタログ
                // ポップアップ・メニューを表示する。
                popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(frame.countListAction);
                popup.addSeparator();
                popup.add(frame.createDdlAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
                break;
            case NODE_DEPTH_FOLDER:	// フォルダ
                // ポップアップ・メニューを表示する。
                popup = new JPopupMenu("ぽっぷあっぷ");
                popup.add(frame.countListAction);
                popup.addSeparator();
                popup.add(frame.createDdlAction);
                popup.show(e.getComponent(), e.getX(), e.getY());
                break;
            case NODE_DEPTH_TABLE:	// テーブル、ビュー、などなど
                // ポップアップ・メニューを表示する。
                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);
                if (dbTools.doc.favList.size() > 0) {
	                for (int i=0; i<dbTools.doc.favList.size(); i++) {
	                    FavoriteGroup g = (FavoriteGroup) dbTools.doc.favList.get(i);
	                    sendFav.add(new JMenuItem(new SendFavTreeAction(g)));
	                }
                } else {
                	JMenuItem menu = new JMenuItem("お気に入りにグループがありません。まずはお気に入りタブでグループを作成してください");
                	menu.setEnabled(false);
                	sendFav.add(menu);
                }
                popup.add(sendFav);
                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());
                break;
            default:
            	System.out.println("getPathCount()="+selPath.getPathCount());
            	assert false: selPath.getPathCount();
            }
        }
    }
    /** 選択されているテーブルをお気に入りに送る。 */
    class SendFavTreeAction extends AbstractAction {
        FavoriteGroup group;
        SendFavTreeAction(FavoriteGroup g) {
        	super(g.name, Global.groupIcon);
        	this.group = g;
        }
        public void actionPerformed(ActionEvent e) {
              // 選択されているテーブルについて
              TreePath[] paths = getSelectionPaths();
              for (int i=0; i<paths.length; i++) {
            	  // 選択されているのがテーブルなら
            	  if (paths[i].getPathCount() == 5) {
            		  // テーブル情報を取得
            		  DefaultMutableTreeNode node = (DefaultMutableTreeNode) paths[i].getLastPathComponent();
            		  TableInfo tableInfo = (TableInfo) node.getUserObject();
                      // お気に入りを作成。
            		  FavoriteTable favtable = new FavoriteTable(
            				  //tableInfo.getTitleName(),
            				  tableInfo.getSqlTableName(),
                              tableInfo.db.name, tableInfo.getCatalogName(),
                              tableInfo.getSchemaName(), tableInfo.getTableName(), tableInfo.getTableType());
                      // グループに登録
            		  group.add(favtable);
            	  }
              }
              // お気に入りのツリーを更新
              dbTools.getTreeFrame().favTree.buildTree();
              dbTools.doc.setModified(true);
              dbTools.getTreeFrame().updateMainFrame();
        }

    }
}
