/* *******************************************************************
 * Copyright (c) 1999-2001 Xerox Corporation, 
 *               2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Common Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/cpl-v10.html 
 *  
 * Contributors: 
 *     Xerox/PARC     initial implementation 
 * ******************************************************************/

     
package org.aspectj.ajde.ui.swing;

import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;

import org.aspectj.ajde.Ajde;
import org.aspectj.ajde.ui.*;
import org.aspectj.asm.*;

/**
 * @author  Mik Kersten
 */
class StructureTreeManager {

    private StructureTree structureTree;
    private SwingTreeViewNodeRenderer cellRenderer = null;
    private TreeSelectionListener treeListener = null;

    private final StructureTreeModel NO_STRUCTURE_MODEL
        = new StructureTreeModel(new SwingTreeViewNode(IHierarchy.NO_STRUCTURE, new AbstractIcon(null), new ArrayList()));
        
    /**
     * @todo	should probably avoid that MouseListener cast
     */
    public StructureTreeManager() { 
        structureTree = new StructureTree();
        structureTree.setModel(NO_STRUCTURE_MODEL);
        cellRenderer = new SwingTreeViewNodeRenderer();
        structureTree.setCellRenderer(cellRenderer);
        //if (fileView) {
        	treeListener = new StructureViewTreeListener(structureTree);
        //} else {
        //	treeListener = new BrowserViewTreeListener(structureTree);
        //}
        structureTree.addTreeSelectionListener(treeListener);
        structureTree.addMouseListener((MouseListener)treeListener);
    }

	public void highlightNode(IProgramElement node) {
		highlightNode((SwingTreeViewNode)structureTree.getModel().getRoot(), node);
	}

    public IProgramElement getSelectedIProgramElement() {
        return (IProgramElement)((SwingTreeViewNode)structureTree.getLastSelectedPathComponent()).getUserObject();
    }

    public void scrollToHighlightedNode() {
        structureTree.scrollPathToVisible(structureTree.getSelectionPath());
    }

    private void highlightNode(SwingTreeViewNode parent, IProgramElement node) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            SwingTreeViewNode currNode = (SwingTreeViewNode)parent.getChildAt(i);
            IProgramElement sNode = (IProgramElement)currNode.getStructureNode();
            if (sNode != null && sNode.equals(node) && currNode.getKind() != IStructureViewNode.Kind.LINK) {
                TreePath path = new TreePath(currNode.getPath());
                structureTree.setSelectionPath(path);
                int currRow = structureTree.getRowForPath(path);
                structureTree.expandRow(currRow);
                structureTree.scrollRowToVisible(currRow);
            } else {
                highlightNode(currNode, node);
            }
        }
    }

//	public void updateTree(StructureView structureView) {	
//    	displayTree(structureView, 10);
//	}
//
//	public void updateTree(GlobalStructureView structureView) {
//		displayTree(structureView, depth);		
//	}
  
	public void updateTree(final StructureView structureView) {	
		if (structureView == null) return;
		Runnable update = new Runnable() {
            public void run() {
                structureTree.removeAll();
//                SwingTreeViewNode currNode;
                if (structureView.getRootNode() == null) {
                	structureTree.setModel(NO_STRUCTURE_MODEL);	
                } else {
                	structureTree.setModel(new StructureTreeModel((SwingTreeViewNode)structureView.getRootNode()));
                }

                if (structureView instanceof GlobalStructureView) {
                	GlobalStructureView view = (GlobalStructureView)structureView;
                	if (view.getGlobalViewProperties().getHierarchy() == StructureViewProperties.Hierarchy.DECLARATION) {
	                	expandTreeToFiles(); 
                	} else {
                		expandTree(15);	
                	}
                } else {
	                expandTree(10);	
                }
        		
           }
        };
  
        if (SwingUtilities.isEventDispatchThread()) {
            update.run();
        } else {
            try {
                SwingUtilities.invokeAndWait(update);
            } catch (Exception e) {
                Ajde.getDefault().getErrorHandler().handleError("Could not update tree.", e);
            }
        }
	}

    StructureTree getStructureTree() {
        return structureTree;
    }

    private void expandTreeToFiles() {
        for (int i = 0; i < structureTree.getRowCount(); i++) {
            TreePath path = structureTree.getPathForRow(i);
            SwingTreeViewNode node = (SwingTreeViewNode)path.getLastPathComponent();
            if (node.getUserObject() instanceof IProgramElement) {
	            IProgramElement pNode = (IProgramElement)node.getUserObject();
	            IProgramElement.Kind kind = pNode.getKind();
	            if (kind == IProgramElement.Kind.PROJECT
					|| kind == IProgramElement.Kind.PACKAGE) {
	                structureTree.expandPath(path);
	            } else {
	                structureTree.collapsePath(path);
	            }
            } else {
            	structureTree.collapsePath(path);
            }
        }
        structureTree.expandPath(structureTree.getPathForRow(0));
    }

    private void expandTree(int depth) {
        for (int i = 0; i < structureTree.getRowCount(); i++) {
            TreePath path = structureTree.getPathForRow(i);
            SwingTreeViewNode node = (SwingTreeViewNode)path.getLastPathComponent();
            if (path.getPath().length-1 > depth || node.getKind() == IStructureViewNode.Kind.RELATIONSHIP) {
                structureTree.collapsePath(path);
            } else {
                structureTree.expandPath(path);
            }
        }
        structureTree.expandPath(structureTree.getPathForRow(0));
    }

    private class StructureTreeModel extends DefaultTreeModel implements TreeModel {
        public StructureTreeModel(TreeNode newRoot) {
            super(newRoot);
        }

        public void valueForPathChanged(TreePath path, Object newValue) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
            nodeChanged(node);
        }
    }
}

//    /**
//     * @param   node    if null assume root
//     */
//    public void navigationAction(ProgramElementNode node, boolean followedLink, boolean forward) {
//        if (node == null) {
//            structureTree.setSelectionRow(0);
//            structureTree.scrollRowToVisible(0);
//        } else if (node.getSourceLocation().getSourceFilePath() != null) {
//            final String fileName = node.getSourceLocation().getSourceFilePath();
//            final int lineNumber = node.getSourceLocation().getLineNumber();
//            if (fileName != null && lineNumber > 0) {
//                Runnable update = new Runnable() {
//                    public void run() {
//                        Ajde.getDefault().getEditorManager().showSourceLine(fileName, lineNumber, true);
//                   }
//                };
//
//                if (SwingUtilities.isEventDispatchThread()) {
//                    update.run();
//                } else {
//                    try {
//                        SwingUtilities.invokeAndWait(update);
//                    } catch (Exception ee) {
//                        
//                    }
//                }
//            }
//            if (followedLink) {
//                highlightNode((SwingTreeViewNode)structureTree.getModel().getRoot(), node);
//            }
//        }
//    }


//            if (node instanceof ProgramElementNode) {
//                ProgramElementNode lexicalNode = (ProgramElementNode)node;
//                setIcon(getProgramElementNodeIcon(lexicalNode));
//            } else if (node instanceof RelationNode) {
//                RelationNode relationNode = (RelationNode)node;
//                
//                setIcon(icons.getAssociationSwingIcon(relationNode.getRelation()));
//		        this.setFont(new Font(this.getFont().getName(), Font.ITALIC, this.getFont().getSize()));
//
//            } else if (node instanceof LinkNode) {
//                LinkNode link = (LinkNode)node;
//                setIcon(getProgramElementNodeIcon(link.getProgramElementNode()));
//            } else {
//                if (node != null && ProgramElementNode.Kind.PACKAGE.equals(node.getKind())) {
//                    setIcon(icons.getStructureSwingIcon(ProgramElementNode.Kind.PACKAGE));
//                } else if (node != null && ProgramElementNode.Kind.PROJECT.equals(node.getKind())) {
//                    setIcon(icons.getStructureSwingIcon(ProgramElementNode.Kind.PROJECT));
//                } else if (node != null && ProgramElementNode.Kind.FILE.equals(node.getKind())) {
//                    setIcon(icons.getStructureSwingIcon(ProgramElementNode.Kind.CLASS));
//                } else {
//                    setIcon(null);
//                }
//            }

//    void updateTree(int depth, GlobalViewProperties properties) {
//    	this.hierarchy = properties.getHierarchy();
//        displayTree(depth, null);
//    }
//
//    void updateTree(String filePath, int depth, GlobalViewProperties properties) {
//    	this.hierarchy = properties.getHierarchy();
//        if (filePath == null || filePath.equals("")) {
//            structureTree.setModel(NO_FILE_SELECTED_MODEL);
//        } else {
//            structureTree.setRootFilePath(filePath);
//            displayTree(depth, filePath);
//        }
//    }

//            int accessibility = 0;
//            if (pNode.getAccessibility().contains(ProgramElementNode.Accessibility.PUBLIC)) {
//                accessibility = 1;
//            } else if (pNode.getAccessibility().contains(ProgramElementNode.Accessibility.PROTECTED)) {
//                accessibility = 2;
//            } else if (pNode.getAccessibility().contains(ProgramElementNode.Accessibility.PRIVATE)) {
//                accessibility = 3;
//            } else if (pNode.getAccessibility().contains(ProgramElementNode.Accessibility.PRIVILEGED)) {
//                accessibility = 3;
//            }
//
//            if (pNode == null || pNode.getKind() == null) {
//                return null;
//            } else if (ProgramElementNode.Kind.PROJECT.equals(pNode.getKind())) {
//                return icons.getStructureSwingIcon(ProgramElementNode.Kind.PROJECT);
//            } else if (ProgramElementNode.Kind.PACKAGE.equals(pNode.getKind())) {
//                return icons.getStructureSwingIcon(ProgramElementNode.Kind.PACKAGE);
//            } else if (ProgramElementNode.Kind.FILE.equals(pNode.getKind())) {
//                return icons.getStructureSwingIcon(ProgramElementNode.Kind.CLASS);
//            } else if (ProgramElementNode.Kind.CLASS.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getClassPublicIcon();
//                    case 2: return icons.getClassProtectedIcon();
//                    case 3: return icons.getClassPrivateIcon();
//                    default: return icons.getClassPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.INTERFACE.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getInterfacePublicIcon();
//                    case 2: return icons.getInterfaceProtectedIcon();
//                    case 3: return icons.getInterfacePrivateIcon();
//                    default: return icons.getInterfacePackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.ASPECT.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getAspectPublicIcon();
//                    case 2: return icons.getAspectProtectedIcon();
//                    case 3: return icons.getAspectPrivateIcon();
//                    case 4: return icons.getAspectPrivilegedIcon();
//                    default: return icons.getAspectPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.METHOD.equals(pNode.getKind())
//                || ProgramElementNode.Kind.INITIALIZER.equals(pNode.getKind())
//                || ProgramElementNode.Kind.CONSTRUCTOR.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getMethodPublicIcon();
//                    case 2: return icons.getMethodProtectedIcon();
//                    case 3: return icons.getMethodPrivateIcon();
//                    default: return icons.getMethodPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.FIELD.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getFieldPublicIcon();
//                    case 2: return icons.getFieldProtectedIcon();
//                    case 3: return icons.getFieldPrivateIcon();
//                    default: return icons.getFieldPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.INTRODUCTION.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getIntroductionPublicIcon();
//                    case 2: return icons.getIntroductionProtectedIcon();
//                    case 3: return icons.getIntroductionPrivateIcon();
//                    default: return icons.getIntroductionPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.POINTCUT.equals(pNode.getKind())) {
//                switch (accessibility) {
//                    case 1: return icons.getJoinpointPublicIcon();
//                    case 2: return icons.getJoinpointProtectedIcon();
//                    case 3: return icons.getJoinpointPrivateIcon();
//                    default: return icons.getJoinpointPackageIcon();
//                }
//            } else if (ProgramElementNode.Kind.ADVICE.equals(pNode.getKind())) {
//               return icons.getAdviceIcon();
//            } else if (ProgramElementNode.Kind.DECLARE_PARENTS.equals(pNode.getKind())) {
//               return icons.getDeclareParentsIcon();
//            } else if (ProgramElementNode.Kind.DECLARE_ERROR.equals(pNode.getKind())) {
//               return icons.getDeclareErrorIcon();
//            } else if (ProgramElementNode.Kind.DECLARE_WARNING.equals(pNode.getKind())) {
//               return icons.getDeclareWarningIcon();
//            } else if (ProgramElementNode.Kind.DECLARE_SOFT.equals(pNode.getKind())) {
//               return icons.getDeclareSoftIcon();
//            } else if (ProgramElementNode.Kind.CODE.equals(pNode.getKind())) {
//               return icons.getCodeIcon();
//            } else {
//                return null;
//            }

//                
//                if (relationNode.getKind().equals(org.aspectj.asm.associations.Advice.NAME) ||
//                    relationNode.getKind().equals(org.aspectj.asm.associations.Introduction.NAME)) {
//                    if (relationNode.getRelation().getBackNavigationName().equals(relationNode.getName()) ){
//                        setIcon(icons.getRelationAdviceBackIcon());
//                    } else {
//                    	setIcon(icons.getAssociationSwingIcon(relationNode.getRelation()));
//                        setIcon(icons.getRelationAdviceForwardIcon());
//                    }
//                } else if (relationNode.getKind().equals(org.aspectj.asm.associations.Inheritance.NAME)) {
//                    if (relationNode.getRelation().getBackNavigationName().equals(relationNode.getName()) ){
//                        setIcon(icons.getRelationInheritanceBackIcon());
//                    } else {
//                        setIcon(icons.getRelationInheritanceForwardIcon());
//                    }
//                } else {
//                    if (relationNode.getRelation().getBackNavigationName().equals(relationNode.getName()) ){
//                        setIcon(icons.getRelationReferenceBackIcon());
//                    } else {
//                        setIcon(icons.getRelationReferenceForwardIcon());
//                    }
//                }

//    public ProgramElementNode getRootProgramElementNode() {
//        IProgramElement node = (IProgramElement)((SwingTreeViewNode)structureTree.getModel().getRoot()).getUserObject();
//        if (node instanceof ProgramElementNode) {
//            return (ProgramElementNode)node;
//        } else {
//            return null;
//        }
//    }

//    /**
//     * @todo    HACK: this is a workaround and can break
//     */
//    private static ProgramElementNode mapResult = null;
//    private ProgramElementNode getNodeForLink(LinkNode node, IProgramElement rootNode) {
//        ProgramElementNode result = null;
//        if (rootNode instanceof ProgramElementNode &&
//            ((ProgramElementNode)rootNode).getName().equals(node.getProgramElementNode().getName())) {
//            mapResult = (ProgramElementNode)rootNode;
//        } else {
//            ProgramElementNode linkedNode = node.getProgramElementNode();
//            for (Iterator it = rootNode.getChildren().iterator(); it.hasNext(); ) {
//                IProgramElement child = (IProgramElement)it.next();
//                getNodeForLink(node, child);
//            }
//        }
//        return mapResult;
//    }

//    private void sortNodes(List nodes) {
//        if (sortNodes) {
//            Collections.sort(nodes, IProgramElementComparator);
//        }
//    }
    
//	private class IProgramElementComparator implements Comparator {
//		public int compare(Object o1, Object o2) {
//			IProgramElement t1 = (IProgramElement) ((SwingTreeViewNode) o1).getUserObject();
//			IProgramElement t2 = (IProgramElement) ((SwingTreeViewNode) o2).getUserObject();
//			if (t1 instanceof ProgramElementNode && t2 instanceof ProgramElementNode) {
//				ProgramElementNode p1 = (ProgramElementNode) t1;
//				ProgramElementNode p2 = (ProgramElementNode) t2;
//				return p1.getName().compareTo(p2.getName());
//			} else {
//				return 0;
//			}
//		}
//	}

//    private class StructureViewNodeAdapter extends DefaultMutableTreeNode {
// 
//        private StructureViewNode nodeInfo = null;
//
//        public StructureViewNodeAdapter(StructureViewNode nodeInfo) {
//            super(nodeInfo, true);
//            this.nodeInfo = nodeInfo;
//        }
//
//        public String toString() {
//            if (nodeInfo != null) {
//                return nodeInfo.toString();
//            } else {
//                return "";
//            }
//        }
//    }


