package org.jcon.df.edit.control.tree;

import org.jcon.df.edit.control.Control;
import org.jcon.df.edit.control.element.ElementEvent;
import org.jcon.df.edit.control.element.ElementListener;
import org.jcon.df.edit.control.element.TreeElement;
import org.jcon.df.edit.control.element.ElementTreeModel;
import org.jcon.util.TNode;
import org.jcon.ui.tree.BTree;
import org.jcon.ui.tree.BTreeEvent;
import org.jcon.ui.tree.BTreeListener;
import org.jcon.param.Param;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPopupMenu;
import java.util.Vector;

/**
 * A Control representing a Tree. It uses TreeElements.
 *
 * @author Jack Harich
 */
public class TreeControl extends Control
    implements BTreeListener {

//---------- Private Fields ------------------------------
// Properties
private TreeElement rootTreeElement; // Must be supplied
  private ElementTreeModel model = new ElementTreeModel();

// Internal
private TNode      rootNode; // *** = new TNode();
public BTree      tree = new BTree();
private Vector     listeners = new Vector();


//---------- Initialization ------------------------------
  public TreeControl() {
    //    this.model = model;
    setModel(model);
    tree.addBTreeListener(this);
  }

//------------ Get and Set the model ---------------------
  public void setModel(ElementTreeModel model) {
    this.model = model;
    tree.setModel(model);
    rootTreeElement = model.getRootElement();
  }

  public ElementTreeModel getModel() {
    return model;
  }

//---------- Abstract Implementation ---------------------
public Component getComponent() {
    return tree.getComponent();
}



//---------- Superclass Overrides ------------------------
public void init() {
    // Could use myParam to set properties
}
//---------- BTreeListener Implementation ----------------
public void processBTreeEvent(BTreeEvent evt) {
  //  print(".processBTreeEvent() - " + evt);
    int eventType = evt.getEventType();
    
    if (eventType == BTreeEvent.NODE_SELECTED) {
        fireEvent(ElementEvent.SELECTION_CHANGED);
    
    } else if (eventType == BTreeEvent.NODE_ACTIVATED) {
        fireEvent(ElementEvent.ELEMENT_ACTIVATED);
    }
}
//---------- Properties ----------------------------------
//----- RootElement
/**
* Sets the all important root of the tree. This MUST be
* done first, before inserting more nodes, or the root can
* contain the other nodes. Please note there is no default
* root.
*/
public void setRootElement(TreeElement element) {
    rootTreeElement = element;
    rootNode = element.getNode();
    //rootTreeElement.setNode(rootNode);
    //rootNode.setUserObject(rootTreeElement);
    //rootNode.setAllowsChildren(true);
    tree.setRootNode(rootNode);
    
    // Note - Cannot set renderer in constructor, causes Swing bug
    // This renderer uses TreeElements in user object
    tree.setCellRenderer(new TreeControlCellRenderer()); 
    
    tree.nodeStructureChanged(rootNode);
    //print(".setRootElement() - tree is: \n" + rootNode.listTree());
     
}
public TreeElement getRootElement() {
    return rootTreeElement;
}
//----- RootVisible
public void setRootVisible(boolean visible) {
    tree.setRootVisible(visible);
}
public boolean isRootVisible() {
    return tree.isRootVisible();
}
//----- RowHeight
/**
* Set this to zero to have each row height calculated,
* based on icon and text height.
*/
public void setRowHeight(int height) {
    tree.setRowHeight(height);
}
public int getRowHeight() {
    return tree.getRowHeight();
}  
//---------- Events --------------------------------------
public void addElementListener(ElementListener listener) {
    listeners.addElement(listener);
}
public void removeElementListener(ElementListener listener) {
    listeners.removeElement(listener);
}
//---------- Public Methods ------------------------------
//----- Simple node manipulation - UserObject is a String
//
/**
* Inserts the childElement into the parentElement.
*
* @param parentElement  the container of the child.
* @praam childElement   the child of the container.
* @param alphabetically if true then insert in alphabetical
*    order. If false insert as the last child.
*/
public void insertElement(TreeElement parentElement,
                          TreeElement childElement,
                          boolean    alphabetically) {


    TNode parentNode = parentElement.getNode();
    TNode childNode  = new TNode(childElement);
    childElement.setNode(childNode);
    childNode.setAllowsChildren(childElement.isAllowsChildren());
    
    if (alphabetically) {
        parentNode.insertAlphabetically(childNode);
    } else {
        parentNode.insertAtEnd(childNode);
    }
    // This may slow down a lot of adds ***
    tree.nodeStructureChanged(parentNode);
}
public void removeElement(TreeElement element) {
  TNode node = (TNode)element.getNode();
  
  TNode parent = (TNode)node.getParent();
  node.removeFromTree();
  tree.nodeStructureChanged(parent);
  System.out.println("we deleted the element: " + element);
}
public void removeAllChildren(TreeElement element) {
    element.getNode().removeAllChildren();
}
/**
* Returns the selected element or null if none selected.
*/
public TreeElement getSelectedElement() {
    return (TreeElement)tree.getSelectedUserObject();
}
/**
* Deselects all and selects the element.
*/
public void selectSingleElement(TreeElement element) {
    tree.selectSingleNode(element.getNode());
}
/**
* "Opens" the element by expanding it and all its children.
* If the element is null then the root is used.
*/
public void openElement(TreeElement element) {
    if (element == null) element = rootTreeElement;
    tree.openNode(element.getNode());
}
public void refresh() {
    tree.refresh();
}
/**
* Useful for when the appearance of an element has changed,
* such as due to an update.
*/
public void elementChanged(TreeElement element) {
    tree.nodeChanged(element.getNode());
}
/**
* Useful for when the children of an element have changed,
* such as due to an add or remove.
*/
public void elementChildrenChanged(TreeElement element) {
    tree.nodeStructureChanged(element.getNode());
}
/* public void resortChildren(TreeElement parent) {
    tree.resortChildren(parent.getNode());
} */


  /* joshy  i got rid of these because the poke too many hole into 
   * the existing class.  they should be considered when
   * doing the redesign though.
  // joshy  these are additions to make let us use popup menus
  // joshy addPopup
  public void addPopup(JPopupMenu popupMenu) {
    tree.addPopup(popupMenu);
  }

  // joshy checkPopup
  public void checkPopup(JPopupMenu popupMenu, MouseEvent evt) {
    if (! tree.rowIsBeneathMouse(evt)) return;
    
    // The following is required for correct behavior on Linux and Windows
    //    if (VisualLib.isRightClick(evt)) {
    //      Item item = getSelectedItem();
    //        controller.configItemPopup(popupHelper, item, this);
        tree.showPopup(popupMenu, evt);
	//    }
  }

  // joshy addMouseListener
  public void addMouseListener(MouseListener ml) {
    tree.addMouseListener(ml);
  }
  */

//---------- Private Methods -----------------------------
private void fireEvent(int eventType) {
    ElementEvent evt = new ElementEvent(eventType, this.getName());
    
    Vector list;
    synchronized(this) {
        list = (Vector)listeners.clone();
    }
    for (int i = 0; i < list.size(); i++) {
        ElementListener listener = (ElementListener)list.elementAt(i);
        listener.processElementEvent(evt);
    }
}    
//--- Std
private static void print(String text) {
    System.out.println("TreeControl" + text);
}

} // End class
