package MONAViewer;

import java.io.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

import MONAViewer.monalanguage.node.*;
import MONAViewer.monalanguage.lexer.*;
import MONAViewer.monalanguage.parser.*;
import MONAViewer.monalanguage.analysis.*;

class ASTDisplay extends DepthFirstAdapter
{
    private Stack parents = new Stack ();
 
    public ASTDisplay()
    {
    }
 
    public void outStart(Start node)
    {
        JFrame frame = new JFrame ("AST Displayer");
        JTree tree = new JTree ((DefaultMutableTreeNode) parents.pop ());
        JScrollPane pane = new JScrollPane (tree);

        expandAll (tree);

        /* window listener so the program will die */
        frame.addWindowListener (new WindowAdapter () {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        frame.setSize (300, 400);
        frame.getContentPane ().add (pane);
        frame.setVisible (true);
    }

    /*
     * As we come across non terminals, push them onto the stack
     */
    public void defaultIn(Node node)
    {
        DefaultMutableTreeNode thisNode = new DefaultMutableTreeNode
            (node.getClass ().getName ().substring 
             (node.getClass().getName().lastIndexOf('.') + 1));

        parents.push (thisNode);
    }

    /*
     * As we leave a non terminal, it's parent is the node before it
     * on the stack, so we pop it off and add it to that node
     */
    public void defaultOut(Node node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    public void inANameExp(ANameExp node) {}
    public void outANameExp(ANameExp node) {}

    public void inAQuantExp(AQuantExp node) {}
    public void outAQuantExp(AQuantExp node) {}

    public void outACallExp(ACallExp node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();
        DefaultMutableTreeNode calleeNode = (DefaultMutableTreeNode) thisNode.getFirstChild();
        String n = "call "+(String)calleeNode.getUserObject();
        thisNode.remove(calleeNode);
        thisNode.setUserObject(n);

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    public void outANameWhere(ANameWhere node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();
        DefaultMutableTreeNode nameNode = (DefaultMutableTreeNode) thisNode.getFirstChild();
        String n = (String)nameNode.getUserObject();
        thisNode.remove(nameNode);
        /*if (thisNode.getChildCount() > 0) -- not quite what we want, doesn't print exp correctly
        {
            DefaultMutableTreeNode whereNode = (DefaultMutableTreeNode) thisNode.getFirstChild();
            n = n + " where " + (String)whereNode.getUserObject();
            thisNode.remove(whereNode);
            }*/
        thisNode.setUserObject(n);

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    public void outAEx2Exp(AEx2Exp node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();
        if (thisNode.getChildCount() == 1)
        {
            DefaultMutableTreeNode nameNode = (DefaultMutableTreeNode) thisNode.getFirstChild();
            if (nameNode.getChildCount() == 0)
            {
                String n = "ex2 "+(String)nameNode.getUserObject();
                thisNode.remove(nameNode);
                thisNode.setUserObject(n);
            }
        }

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    public void outAAll2Exp(AAll2Exp node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();
        if (thisNode.getChildCount() == 1)
        {
            DefaultMutableTreeNode nameNode = (DefaultMutableTreeNode) thisNode.getFirstChild();
            if (nameNode.getChildCount() == 0)
            {
                String n = "all2 "+(String)nameNode.getUserObject();
                thisNode.remove(nameNode);
                thisNode.setUserObject(n);
            }
        }

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    public void outAAndExp(AAndExp node)
    {
        DefaultMutableTreeNode thisNode = (DefaultMutableTreeNode) parents.pop ();
        LinkedList toReparent = new LinkedList();
        Enumeration en = thisNode.children();
        while (en.hasMoreElements()) 
        {
            DefaultMutableTreeNode a = (DefaultMutableTreeNode)en.nextElement();
            if (a.getUserObject().equals("AAndExp"))
            {
                Enumeration en1 = a.children();
                while (en1.hasMoreElements())
                {
                    DefaultMutableTreeNode aa = (DefaultMutableTreeNode)en1.nextElement();
                    toReparent.add(aa);
                }
            }
            else
                toReparent.add(a);
        }
        thisNode.removeAllChildren();
        Iterator it = toReparent.iterator();
        while (it.hasNext()) {
            DefaultMutableTreeNode n = (DefaultMutableTreeNode)it.next();
            thisNode.add(n);
        }

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }

    /*
     * Terminals - our parent is always on the top of the stack, so we
     * add ourselves to it
     */
    public void defaultCase(Node node)
    {
        DefaultMutableTreeNode thisNode = new
            DefaultMutableTreeNode (((Token) node).getText ());

        ((DefaultMutableTreeNode) parents.peek ()).add (thisNode);
    }
    
    public void caseEOF(EOF node)
    {
    }

    /*
     * we want to see the whole tree. These functions expand it for
     * us, they are written by Christian Kaufhold and taken from the
     * comp.lang.jave.help newsgroup
     */
    public static void expandAll(JTree tree)
    {
        Object root = tree.getModel().getRoot();
        
        if (root != null)
            expandAll(tree, new TreePath(root));
    }
    
    
    public static void expandAll(JTree tree, TreePath path)
    {
        for (Iterator i = extremalPaths(tree.getModel(), path,
                                        new HashSet()).iterator();
             i.hasNext(); )
            tree.expandPath((TreePath)i.next());
    }

    /** The "extremal paths" of the tree model's subtree starting at
        path. The extremal paths are those paths that a) are non-leaves
        and b) have only leaf children, if any. It suffices to know
        these to know all non-leaf paths in the subtree, and those are
        the ones that matter for expansion (since the concept of expan-
        sion only applies to non-leaves).
        The extremal paths of a leaves is an empty collection.
        This method uses the usual collection filling idiom, i.e.
        clear and then fill the collection that it is given, and
        for convenience return it. The extremal paths are stored
        in the order in which they appear in pre-order in the
        tree model.
    */
    public static Collection extremalPaths(TreeModel data,
                                           TreePath path, 
                                           Collection result)
    {
        result.clear();
        
        if (data.isLeaf(path.getLastPathComponent()))
        {
            return result; // should really be forbidden (?)
        }
        
        extremalPathsImpl(data, path, result);

        return result;
    }
    
    private static void extremalPathsImpl(TreeModel data, 
                                          TreePath path,
                                          Collection result)
    {
        Object node = path.getLastPathComponent();
        
        boolean hasNonLeafChildren = false;

        int count = data.getChildCount(node);
        
        for (int i = 0; i < count; i++)
            if (!data.isLeaf(data.getChild(node, i)))
                hasNonLeafChildren = true;

        if (!hasNonLeafChildren)
            result.add(path);
        else
        {
            for (int i = 0; i < count; i++)
            {
                Object child = data.getChild(node, i);
                
                if (!data.isLeaf(child))
                    extremalPathsImpl(data,
                                      path.pathByAddingChild(child),
                                      result);
            }
        }
    }
}

public class MONAViewer
{
  public static void main (final String[] argv)
  {
      for (int i = 0; i < argv.length; i++)
      {
          final int j = i;
          new Thread() {
              public void run() {
                  try {
                      Lexer l = new Lexer 
                          (new PushbackReader (new BufferedReader
                                               (new FileReader (argv[j])), 65536));
                      Parser p = new Parser (l);
                      
                      Start start = p.parse ();
                      
                      ASTDisplay ad = new ASTDisplay ();
                      start.apply (ad);
                  } catch (IOException e) {
                      System.err.println("Could not open input file "+argv[j]);
                  } catch (ParserException e) {
                      System.err.println("Parsing error for file "+argv[j]);
                      e.printStackTrace();
                  } catch (LexerException e) {
                      System.err.println("Lexing error for file "+argv[j]);
                      e.printStackTrace();
                  }
              }
          }.start();
      }
  }
}
