package CSU670_Plugin.gen;
import edu.neu.ccs.demeter.dj.*;  
import edu.neu.ccs.demeter.aplib.EdgeI;
import edu.neu.ccs.demeter.aplib.Traversal;
import edu.neu.ccs.demeter.aplib.TraversalException;
import edu.neu.ccs.demeter.aplib.cd.ClassDefVisitor;
import edu.neu.ccs.demeter.aplib.cd.ClassDef;
import edu.neu.ccs.demeter.aplib.sg.Strategy;

import java.io.*;
import java.util.*;

import CSU670_Plugin.TraversalHighlight;
import CSU670_Plugin.editors.AspectXEditor;
import edu.neu.ccs.demeter.*;

public class Main {
  public Main() {
    super();
  }
  public static Main parse(java.io.Reader in) throws ParseException { return new Parser(in)._Main(); }
  public static Main parse(java.io.InputStream in) throws ParseException { return new Parser(in)._Main(); }
  public static Main parse(String s) {
    try { return parse(new java.io.StringReader(s)); }
    catch (ParseException e) {
      throw new RuntimeException(e.toString());
    }
  }

  public static String log="";
  public static boolean hadERROR=true;
  static void ERROR(String str, Exception e){
        if(e!=null){
            log+="\n";
            log+=e+"\n";
        }
    log+="\n";
    log+="ERROR IN USER CODE:\n";
    log+=str;
    log+="\n\n";
    hadERROR=true;
  }
  static java.util.HashSet definedNodes;
  static boolean isDefinedNode(String s){return definedNodes.contains(s);}
  static ClassGraph cg;
  //static ClassGraph cdCg;
  static String userCgStr;
  public static edu.neu.ccs.demeter.aplib.cd.ClassGraph userCg=null;
  static AspectFile input;
  public static Map traversals;
  public static void check(String text){
    log="";
    hadERROR=false;
    traversals=new HashMap();
    //this doesnt work for some reason
    //cdCg=new ClassGraph("edu.neu.ccs.demeter.aplib.cd.ClassGraph",true,false);

    //  initialize cg
    cg = new ClassGraph(true,false);

    //list of all classes
    Class[] classes={AnyClass.class, AnyPart.class, AspectBody.class, AspectCD.class, AspectFile.class, Aspect.class, Aspect_List.class, AspectName.class, Aspects.class, AspectTV.class, BCompound.class, BinaryOp.class, Bypassing.class, CDBody.class, ChosenCD.class, ChosenSelector.class, ClassGlob_Commalist.class, ClassGlob.class, ClassGlobSet.class, ClassGlobSpec.class, ClassName_BCList.class, ClassNameExact.class, ClassNameGlob.class, ClassName.class, ClassNamePrefix.class, ClassNameSet.class, Complement.class, Constraint.class, CopyVisitor.class, DecChosenBody.class, DecChosen.class, DecName.class, DecNodeSetBody.class, DecNodeSetBody_PCList.class, DecNodeSet.class, DecStrategy.class, DisplayVisitor.class, EdgeGlob.class, EqualVisitor.class, From.class, Glob_Commalist.class, Glob.class, GlobSet.class, GlobSpec.class, GlobVisitor.class, GraphNodeRegExp.class, GraphNodes.class, Intersection.class, Intersect.class, JavaCharStream.class, Join.class, Main.class, Merge.class, NameBinding_Commalist.class, NameBinding.class, Name.class, NameMap.class, NegativeConstraint.class, NodeSubsetName.class, Nonempty_Aspect_List.class, Nonempty_ClassGlob_Commalist.class, Nonempty_ClassName_BCList.class, Nonempty_DecNodeSetBody_PCList.class, Nonempty_Glob_Commalist.class, Nonempty_NameBinding_Commalist.class, Nonempty_Name.class, Nonempty_PathSegment_List.class, Nonempty_SGEdge_SList.class, Nonempty_Strategy_Commalist.class, Nonempty_TVDeclaration_List.class, OneClassGlob.class, OneGlob.class, OnlyThrough.class, Operator.class, ParseException.class, ParserConstants.class, Parser.class, ParserTokenManager.class, PartGlob.class, PartNameExact.class, PartNameGlob.class, PartName.class, PathDirective.class, PathSegment.class, PathSegment_List.class, PositiveConstraint.class, PrintVisitor.class, Regexp.class, SGEdge.class, SGEdge_SList.class, Simple.class, SimpleStrategy.class, SourceDirective.class, SourceGlob.class, SourceMarker.class, StrategyCombination.class, Strategy_Commalist.class, StrategyExpression.class, StrategyGraph.class, Strategy.class, StrategyReference.class, SubclassGlob.class, SubgraphVisitor.class, __Subtraversal.class, SuperclassGlob.class, SymbolicNameMap.class, TargetDirective.class, TargetGlob.class, TargetMarker.class, Through.class, To.class, Token.class, TokenMgrError.class, ToStop.class, TraceVisitor.class, TVBody.class, TVDeclarationBody.class, TVDeclaration.class, TVDeclaration_List.class, TVDeclarations.class, UCompound.class, UnaryOp.class, Union.class, UniversalVisitor.class, Via.class, WildCard.class};

    //manually add all classes
    for(int i=0; i<classes.length; ++i)
        cg.addClass(classes[i]);


    //see if input can parse
    try{
        input = AspectFile.parse(new StringBufferInputStream(text));
    } catch (Exception e) {
        ERROR("Parse Error In user Input",e);
        return;
    }


    //fetch user ClassGraph
    userCgStr=null;
    cg.traverse(input,"from CSU670_Plugin.gen.AspectFile to CSU670_Plugin.gen.CDBody",
                    new Visitor(){
                        void before(CDBody t){
                            //check for multiple CGs
                            if(Main.userCgStr!=null) ERROR("Multiple user ClassGraphs",null);
                            else Main.userCgStr=t.toString(); }
                    }
                );
    //check for 0 CGs
    if(userCgStr==null)
        ERROR("No user ClassGraph found",null);

    if(hadERROR) return;


    //System.out.println(userCgStr);

    //check if user CG can parse
    try{
        userCg = ClassGraph.parse(new StringBufferInputStream(userCgStr));
    } catch (Exception e) {
        ERROR("User ClassGraph failed to parse",e);
        return;
    }

    //normalize it
    userCg = userCg.normalize();


    //generate list of defined nodes
    definedNodes=new HashSet();
    userCg.allClassDefs(new ClassDefVisitor(){
        public void before(ClassDef host){
            Main.definedNodes.add(host.toString());
        }
    });

    //make sure all nodes in CG are defined
    for(Iterator i = userCg.getNodes().iterator(); i.hasNext();){
        String cur = i.next().toString();
        if(!isDefinedNode(cur)){
            ERROR("Undefined class in ClassGraph: "+ cur,null);
            return;
        }
    }

    //make sure all other nodes are defined
    cg.traverse(input,"from CSU670_Plugin.gen.AspectFile to CSU670_Plugin.gen.ClassName",new Visitor(){
        void before(ClassName n){
            if(!isDefinedNode(n.toString())){
                ERROR("Undefined class in Traversal: "+ n,null);
            }
        }
    });
    if(hadERROR) return;
    
    //pre-compute traversals
    cg.traverse(input,"from CSU670_Plugin.gen.AspectFile to CSU670_Plugin.gen.DecStrategy",new Visitor(){
    	void before(DecStrategy s){
    		String key = s.decname.ident.toString();
    		TraversalHighlight val = new TraversalHighlight();
    		
    		StringWriter out = new StringWriter();
    		PrintVisitor v = new PrintVisitor();
    		v.out=new PrintWriter(out);
    		s.strategyexpression.universal_trv0(v);
    		
    		Traversal t;
			try {
				t = Traversal.compute(Strategy.fromString(out.toString()), Main.userCg);
			} catch (TraversalException e) {
				ERROR("Error in user traversal",e);
				return;
			}
    	
			for(Iterator i=t.getNodeSets().iterator(); i.hasNext();)
				val.addNode(((Traversal.NodeSet)i.next()).getNode().toString());
			
			for(Iterator i=t.getEdgeSets().iterator(); i.hasNext();)
				val.addEdge(((Traversal.EdgeSet)i.next()).getEdge());
			
			Main.traversals.put(key,val);
    	}
    });
    if(hadERROR) return;
    
    //pre-compute nodesets
    cg.traverse(input,"from CSU670_Plugin.gen.AspectFile to CSU670_Plugin.gen.DecNodeSet",new Visitor(){
    	void before(DecNodeSet s){
    		String key = s.nodesubsetname.ident.toString();
    		TraversalHighlight val = new TraversalHighlight();
    		
    		StringWriter out = new StringWriter();
    		PrintVisitor v = new PrintVisitor();
    		v.out=new PrintWriter(out);
    		
    		val.addNodes(s.decnodesetbody.eval());

    		Main.traversals.put(key,val);
    	}
    });
    if(hadERROR) return;
    
    //print out success
    log+="USER CODE PARSED SUCCESSFULLY";
    //*/
  }

  void universal_trv0_bef(UniversalVisitor _v_) {
    ((UniversalVisitor) _v_).before(this);
  }

  void universal_trv0_aft(UniversalVisitor _v_) {
    ((UniversalVisitor) _v_).after(this);
  }

  void universal_trv0(UniversalVisitor _v_) {
    universal_trv0_bef(_v_);
    universal_trv0_aft(_v_);
  }

}
