package com.dbxml.db.admin.components;

/*
 * dbXML - Native XML Database
 * Copyright (C) 1999-2004  The dbXML Group, L.L.C.
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: DBBrowser.java,v 1.8 2004/07/29 18:41:15 bradford Exp $
 */

import com.dbxml.db.admin.nodes.*;
import javax.swing.*;
import javax.swing.tree.*;

import com.dbxml.db.admin.Admin;
import com.dbxml.db.client.dbXMLClient;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.swing.border.Border;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import com.dbxml.util.Stopwatch;
import com.dbxml.db.admin.DocWrapper;
import java.awt.PopupMenu;

/**
 * DBBrowser
 */

public class DBBrowser extends JTree {
   private static final Border brdMargin = BorderFactory.createEmptyBorder(1, 1, 1, 1);

   private Admin admin;
   private RootNode adminRoot = new RootNode();
   private DefaultMutableTreeNode nodeRoot = new DefaultMutableTreeNode();
   private DefaultTreeModel treeModel;
   private JMenu menu;

   public DBBrowser(Admin admin) {
      this.admin = admin;

      ToolTipManager.sharedInstance().registerComponent(this);

      nodeRoot.setUserObject(adminRoot);

      setCellRenderer(new AdminNodeRenderer());

      getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

      treeModel = new DefaultTreeModel(nodeRoot, true);
      setModel(treeModel);

      TreePath rootPath = new TreePath(nodeRoot.getPath());
      setSelectionPath(rootPath);

      setEditable(false);
      setAutoscrolls(true);
      setRootVisible(true);
      setShowsRootHandles(false);
      setScrollsOnExpand(true);
      addTreeExpansionListener(new TreeExpansionListener() {
         public void treeExpanded(TreeExpansionEvent e) {
            browser_treeExpanded(e);
         }

         public void treeCollapsed(TreeExpansionEvent e) {
            browser_treeCollapsed(e);
         }
      });
      addMouseListener(new MouseAdapter() {
         public void mousePressed(MouseEvent e) {
            browser_mousePressed(e);
         }

         public void mouseClicked(MouseEvent e) {
            browser_mouseClicked(e);
         }
      });
   }

   public void addClient(dbXMLClient client, String label) {
      AdminNode info = new CollectionNode(client, label);
      DefaultMutableTreeNode sn = new DefaultMutableTreeNode(info, true);
      nodeRoot.add(sn);
      treeModel.reload(nodeRoot);
   }

   public void addFileSystem(String filename, String label) {
      AdminNode info = new DirectoryNode(adminRoot, filename, label);
      DefaultMutableTreeNode sn = new DefaultMutableTreeNode(info, true);
      nodeRoot.add(sn);
      treeModel.reload(nodeRoot);
   }

   public void addFileSystem(String filename) {
      addFileSystem(filename, null);
   }

   public void setMenu(JMenu menu) {
      this.menu = menu;
      generateMenu(menu, adminRoot, getSelectionPath(), false);
   }

   private JPopupMenu generateMenu(JMenu menu, final AdminNode info, final TreePath path, boolean useParentMenu) {
      JPopupMenu pmenu = null;
      if ( menu != null )
         menu.setVisible(false);
      else
         pmenu = new JPopupMenu();

      com.dbxml.db.admin.nodes.MenuItem[] items = null;

      if ( info instanceof HasMenu )
         items = ((HasMenu)info).getMenu();

      if ( items != null && items.length > 0 ) {
         HasMenu hm = (HasMenu)info;
         ActionListener listener = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
               JMenuItem item = (JMenuItem)ae.getSource();
               int result = ((HasMenu)info).menuAction(item.getMnemonic());
               TreePath p = null;
               switch ( result ) {
                  case HasMenu.REFRESH_PARENT:
                     p = path.getParentPath();
                     break;

                  case HasMenu.REFRESH_SELF:
                  case HasMenu.REFRESH_CHILDREN:
                     p = path;
                     break;

                  case HasMenu.REMOVE_SELF:
                     DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
                     TreeNode parent = node.getParent();
                     node.removeFromParent();
                     treeModel.nodeStructureChanged(parent);
                     break;
               }

               if ( p != null ) {
                  collapsePath(p);
                  expandPath(p);
               }
            }
         };

         if ( pmenu != null )
            pmenu.setLabel(hm.getMenuTitle());
         else {
            menu.removeAll();
            menu.setText(hm.getMenuTitle());
            menu.setVisible(true);
         }

         for ( int i = 0; i < items.length; i++ ) {
            if ( items[i].getAction() != com.dbxml.db.admin.nodes.MenuItem.ACTION_SEPARATOR ) {
               JMenuItem item = new JMenuItem(items[i].getLabel(), items[i].getAction());
               item.addActionListener(listener);
               if ( pmenu != null )
                  pmenu.add(item);
               else
                  menu.add(item);
            }
            else {
               if ( pmenu != null )
                  pmenu.addSeparator();
               else
                  menu.addSeparator();
            }
         }

         if ( useParentMenu && info.isParentMenuInherited() ) {
            TreePath parentPath = path.getParentPath();
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)parentPath.getLastPathComponent();
            AdminNode parentInfo = (AdminNode)node.getUserObject();
            if ( parentInfo instanceof HasMenu ) {
               HasMenu parentHM = (HasMenu)parentInfo;
               MenuItem[] parentItems = parentHM.getMenu();
               if ( parentItems != null && parentItems.length > 0 ) {
                  JPopupMenu parentMenu = generateMenu(null, parentInfo, parentPath, false);
                  JMenu pm = new JMenu(parentHM.getMenuTitle());
                  Component[] menuItems = parentMenu.getComponents();
                  for ( int i = 0; i < menuItems.length; i++ )
                     pm.add(menuItems[i]);

                  if ( pmenu != null ) {
                     pmenu.addSeparator();
                     pmenu.add(pm);
                  }
                  else {
                     menu.addSeparator();
                     menu.add(pm);
                  }
               }
            }
         }
      }
      else if ( useParentMenu && info.isParentMenuInherited() ) {
         TreePath parentPath = path.getParentPath();
         DefaultMutableTreeNode node = (DefaultMutableTreeNode)parentPath.getLastPathComponent();
         AdminNode parentInfo = (AdminNode)node.getUserObject();
         if ( parentInfo instanceof HasMenu )
            pmenu = generateMenu(null, parentInfo, parentPath, false);
      }

      return pmenu;
   }

   public boolean checkForPopup(MouseEvent e) {
      final TreePath path = getPathForLocation(e.getX(), e.getY());
      if ( path == null )
         return false;

      DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
      final AdminNode info = (AdminNode)node.getUserObject();

      ActionListener listener = new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            TreeExpansionEvent ex = new TreeExpansionEvent(this, path);
            browser_treeCollapsed(ex);
            browser_treeExpanded(ex);
         }
      };

      if ( e.isPopupTrigger() ) {
         JPopupMenu mnu = generateMenu(null, info, path, true);
         if ( mnu != null ) {
            if ( info instanceof HasChildren && isExpanded(path) ) {
               if ( info instanceof HasMenu ) {
                  com.dbxml.db.admin.nodes.MenuItem[] items = ((HasMenu)info).getMenu();
                  if ( items != null && items.length > 0 )
                     mnu.addSeparator();
               }
               JMenuItem item = new JMenuItem("Refresh");
               item.addActionListener(listener);
               mnu.add(item);
            }

            if ( mnu.getComponentCount() > 0 )
               mnu.show(this, e.getX(), e.getY());
         }
         return true;
      }
      else
         return false;
   }

   public void browser_mousePressed(MouseEvent e) {
      if ( checkForPopup(e) )
         return;

      final TreePath path = getPathForLocation(e.getX(), e.getY());
      if ( path == null )
         return;

      try {
         admin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
         DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
         final AdminNode info = (AdminNode)node.getUserObject();

         switch ( e.getClickCount() ) {
            case 1:
               admin.setEnableQueries(info instanceof HasCollection);
               if ( menu != null )
                  generateMenu(menu, info, path, true);
               break;

            case 2:
               if ( info instanceof HasDocWrapper ) {
                  e.consume();
                  setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                  Stopwatch sw = new Stopwatch("Document Retrieval", true);
                  DocWrapper dw = ((HasDocWrapper)info).getDocWrapper();
                  sw.stop();
                  admin.setStatus(sw.toString());
                  admin.editDocument(dw);
                  setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
               }
               break;
         }
         admin.refreshMenuBar();
      }
      catch ( Exception ex ) {
         admin.addMessage(ex.toString());
      }
      finally {
         admin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
   }

   public void browser_mouseClicked(MouseEvent e) {
      checkForPopup(e);
   }

   public AdminNode getAdminRoot() {
      return adminRoot;
   }

   public void browser_treeExpanded(TreeExpansionEvent e) {
      TreePath path = e.getPath();
      DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();

      if ( node == nodeRoot )
         return;

      admin.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

      node.removeAllChildren();
      AdminNode info = (AdminNode)node.getUserObject();
      if ( info instanceof HasChildren ) {
         HasChildren hs = (HasChildren)info;
         AdminNode[] nodes = hs.getChildren();
         for ( int i = 0; i < nodes.length; i++ )
            node.add(new DefaultMutableTreeNode(nodes[i], nodes[i] instanceof HasChildren));

         if ( nodes.length == 0 )
            collapsePath(path);
      }
      treeModel.nodeStructureChanged(node);
      admin.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
   }

   public void browser_treeCollapsed(TreeExpansionEvent e) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.getPath().getLastPathComponent();

      if ( node == nodeRoot )
         return;

      closeNode(node);
      node.add(new DefaultMutableTreeNode());
      treeModel.nodeStructureChanged(node);
   }

   private void closeNode(DefaultMutableTreeNode node) {
      Enumeration enum = node.children();
      ArrayList list = new ArrayList();
      while ( enum.hasMoreElements() )
         list.add(enum.nextElement());
      for ( int i = 0; i < list.size(); i++ ) {
         DefaultMutableTreeNode c = (DefaultMutableTreeNode)list.get(i);
         closeNode(c);
         AdminNode info = (AdminNode)c.getUserObject();
         if ( info != null )
            info.close();
         node.remove(c);
      }
   }


   /**
    * AdminNodeRenderer
    */

   private class AdminNodeRenderer extends DefaultTreeCellRenderer {
      public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
         super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
         DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;

         if ( node.getUserObject() instanceof AdminNode ) {
            AdminNode info = (AdminNode)node.getUserObject();
            setFont(info.getFont());
            setForeground(info.getColor());
            setText(info.getLabel());
            setIcon(info.getIcon());
            setToolTipText(info.getTooltip());
            setBorder(brdMargin);
         }

         return this;
      }
   }
}




