import java.util.*;
import java.net.URL;
import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;
import com.sun.java.swing.event.*;

class HTMLfinder implements EngineCallBack, ActionListener, TreeExpansionListener, TreeSelectionListener
{
  Thread moteur;
  JTextArea log;
  DefaultMutableTreeNode dataTreeRoot;
  DefaultMutableTreeNode dirTreeRoot;
  DefaultTreeModel dirTreeModel;
  JTree dirTree;
  JPTable dataTable;
  JComboBox comboBox;

  public HTMLfinder() // initialise les composants le la fenetre
    {
      JLabel label= new JLabel("Location : ");
      JScrollPane treeScroll= new JScrollPane(dirTree= new JTree(dirTreeModel= new DefaultTreeModel(dirTreeRoot= new DefaultMutableTreeNode(new DirElement("HTMLfinder",dataTreeRoot= new DefaultMutableTreeNode(new DataElement("URLs",null,null)))))));
      JSplitPane hSplitPane= new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,treeScroll,JTable.createScrollPaneForTable((dataTable= new JPTable(DataElement.names)).getJTable()));
      JSplitPane vSplitPane= new JSplitPane(JSplitPane.VERTICAL_SPLIT,hSplitPane,new JScrollPane(log= new JTextArea()));
      GridBagLayout gridBag= new GridBagLayout();
      JPanel mainPanel= new JPanel(gridBag);
      JFrame mainFrame= new JFrame("HTMLfinder");
      GridBagConstraints c= new GridBagConstraints();

      dirTree.putClientProperty("JTree.lineStyle","None"); // corrects the swing bug array out of bounds exception !!
      ((DataElement)dataTreeRoot.getUserObject()).relatedObject= dirTreeRoot;
      dirTreeRoot.add(new DefaultMutableTreeNode());
      dirTree.addTreeExpansionListener(this);
      dirTree.addTreeSelectionListener(this);
      comboBox= new JComboBox();
      comboBox.setBackground(Color.white);
      comboBox.setEditable(true);
      comboBox.addActionListener(this);
      log.setEditable(false);
      log.setAutoscrolls(true);
      c.gridwidth= GridBagConstraints.REMAINDER;
      c.fill= GridBagConstraints.BOTH;
      c.weightx= 1;
      c.weighty= 1;
      gridBag.setConstraints(vSplitPane,c);
      c.fill= GridBagConstraints.HORIZONTAL;
      c.weighty= 0;
      gridBag.setConstraints(comboBox,c);
      c.gridwidth= GridBagConstraints.RELATIVE;
      c.fill= GridBagConstraints.NONE;
      c.weightx= 0;
      gridBag.setConstraints(label,c);
      mainPanel.add(label);
      mainPanel.add(comboBox);
      mainPanel.add(vSplitPane);
      mainFrame.getContentPane().add(mainPanel);
      mainFrame.pack();
      mainFrame.setVisible(true);
    }

    DefaultMutableTreeNode findChild(DefaultMutableTreeNode from, String name)
    {
	Enumeration children= from.children();
	DefaultMutableTreeNode child= null;
	boolean found= false;

	while (children.hasMoreElements() && !(found= name.equals((child= (DefaultMutableTreeNode)children.nextElement()).toString())));
	return found?child:null;
    }

    DefaultMutableTreeNode findNode(DefaultMutableTreeNode from, String name)
    {
	Enumeration children= from.children();
	DefaultMutableTreeNode child= null;
	boolean found= false;

	while (children.hasMoreElements() && !(found= !(child= (DefaultMutableTreeNode)children.nextElement()).isLeaf() && name.equals(child.toString())));
	return found?child:null;
    }

  public synchronized void foundURL(URL u) // engine's callback
    {
      StringTokenizer names= new StringTokenizer(u.getHost()+u.getFile(),"/");
      int nbNames= names.countTokens();
      DefaultMutableTreeNode nodeData= dataTreeRoot;
      DefaultMutableTreeNode nodeDir= dirTreeRoot;
      DefaultMutableTreeNode firstChanged= null;
      DefaultMutableTreeNode tmpNode;
      DirElement tmpElement;
      TreePath tmpPath;
      String name;

      for (int i= 1; i<nbNames; ++i)
	  if ((tmpNode= findChild(nodeDir,name= names.nextToken()))!=null)
	      {
		  nodeDir= tmpNode;
		  nodeData= findNode(nodeData,name);
	      }
	  else
	      {
		  if (firstChanged==null) firstChanged= nodeDir;
		  if (nodeDir.getFirstChild().toString()==null) nodeDir.remove(0); // si c'est un noeud vide je le retire
		  nodeData.add(tmpNode= new DefaultMutableTreeNode(tmpElement= new DirElement(name,null)));
		  nodeData= tmpNode;
		  if ((tmpPath= dirTree.getSelectionPath())!=null && nodeDir==(DefaultMutableTreeNode)tmpPath.getLastPathComponent()) // si modifie le noeud affiche
		      dataTable.add(tmpNode);
		  nodeDir.add(tmpNode= new DefaultMutableTreeNode(new DirElement(name,nodeData)));
		  nodeDir= tmpNode;
		  nodeDir.add(new DefaultMutableTreeNode());
		  tmpElement.relatedObject= nodeDir;
	      }
      if (nbNames>0)
	  {
	      nodeData.add(tmpNode= new DefaultMutableTreeNode(new DataElement(names.nextToken(),nodeDir,u)));
	      if ((tmpPath= dirTree.getSelectionPath())!=null && nodeDir==(DefaultMutableTreeNode)tmpPath.getLastPathComponent()) // si modifie le noeud affiche
		  dataTable.add(tmpNode);
	  }
      if (firstChanged!=null) // optimise le rafraichissement
	  dirTreeModel.nodeStructureChanged(firstChanged);
    }

  public void actionPerformed(ActionEvent e) // comboBox's callback
    {
      String item= (String)comboBox.getSelectedItem();

      try
	{
	  Engine engine= new Engine(this,item,log);
	  int i= 0;

	  while (i<comboBox.getItemCount() && item!=(String)comboBox.getItemAt(i))
	    ++i;
	  if (i==comboBox.getItemCount())
	    comboBox.addItem(item);
// 	  if (moteur!=null) // un seul moteur a la fois
// 	    moteur.stop();
	  moteur= new Thread(engine);
	  moteur.start();
	}
      catch (java.net.MalformedURLException x) { log.append("Bad protocol "+item+"\n => Try something like http://www.essi.fr/~clement\n"); }
      catch (java.io.IOException x) { log.append("Bad address "+item+"\n => Try something like http://www.essi.fr/~clement\n"); }
    }

  public void treeCollapsed(TreeExpansionEvent e) {}

  public void treeExpanded(TreeExpansionEvent e)
    {
      TreePath chemin= e.getPath();
      DefaultMutableTreeNode noeud= (DefaultMutableTreeNode)chemin.getLastPathComponent();

      if (noeud.getFirstChild().toString()==null) // si c'est un noeud vide je le referme
	dirTree.collapsePath(chemin);
    }

  public void valueChanged(TreeSelectionEvent e)
    {
      DefaultMutableTreeNode relatedNode= (DefaultMutableTreeNode)((DirElement)((DefaultMutableTreeNode)e.getPath().getLastPathComponent()).getUserObject()).relatedObject;
      Enumeration elements;
      DefaultMutableTreeNode node;

      dataTable.erase();
      elements= relatedNode.children();
      while (elements.hasMoreElements()) // Tous les repertoires dans l'ordre
	  if (!(node= (DefaultMutableTreeNode)elements.nextElement()).isLeaf())
	      dataTable.add(node);
      elements= relatedNode.children();
      while (elements.hasMoreElements()) // puis tous les fichiers dans l'ordre
	  if ((node= (DefaultMutableTreeNode)elements.nextElement()).isLeaf())
	      dataTable.add(node);
    }

  public static void main(String[] args)
    {
      HTMLfinder finder= new HTMLfinder();
    }
}
