package aspectEditor.aspectEditorUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.jar.JarFile;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import aspectEditor.editors.AspectEditor;
import edu.neu.ccs.demeter.aplib.TraversalException;
import edu.neu.ccs.demeter.aplib.TraversalGraph.EdgeSet;
import edu.neu.ccs.demeter.aplib.TraversalGraph.NodeSet;
import edu.neu.ccs.demeter.aplib.cd.ClassGraph;
public class Main {
	public static HashSet nodesTraversed = new HashSet();
	public static HashSet edgesTraversed = new HashSet();
	
	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 void main(String[] args) {
		
		//XAspectLanguage2 s = XAspectLanguage2.parse("");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			XAspectLanguage2 s = XAspectLanguage2.parse(in);
			checkSemantics(s); //Check semantics of s
		} catch (ParseException e) {
			System.err.println(e.getMessage());
		}
	}
	
	public static void highlight(String strategy){
		
		//get the contents out of the editor
		String docToParse = AspectEditor.myDoc.get();
		XAspectLanguage2 s =  new XAspectLanguage2();
		s =  XAspectLanguage2.parse(docToParse);
		HashSet myCDNodeHash = new HashSet();
		
		//setup iterator for list of XAspectLanguage2s in the editor window
		Enumeration aspectEnumeration = s.get_aspectlanguage_list().elements();
		String myClass = "";
		String cdType = "aspectEditor.aspectEditorUtils.ClassDictionaryL";
		String traversalType= "aspectEditor.aspectEditorUtils.SelectorL";
		edu.neu.ccs.demeter.aplib.cd.ClassGraph genericCG = new ClassGraph();
		SelectorL mySelector = new SelectorL();
		
		//loops through aspect cd (will contain ClassDictionaries and StrategyLs)
		while(aspectEnumeration.hasMoreElements())
		{
			Object currObj = aspectEnumeration.nextElement();
			//get cd out of editor////////////////////////////////////////////
			//check if it's a class dictionary then check semantics
			if(cdType.equals(currObj.getClass().getName()))
			{
				ClassDictionaryL currDict = (ClassDictionaryL)currObj;
				myClass = currDict.get_classdict().get_text().toString();
				//aspectEditor.traversalViewer.setText(myClass);
				genericCG = edu.neu.ccs.demeter.aplib.cd.ClassGraph.parse(myClass);
				myCDNodeHash.addAll(genericCG.getNodes());
			}
			/////////////////////end get cd out of editor/////////////////////////////////
			
			//////////get SelectorL out of editor////////////////////////
			if(traversalType.equals(currObj.getClass().getName()))
			{
				mySelector = (SelectorL)currObj;
			}
			///////////end get SelectorL out of editor
		}
		
		//highlight based on trying whether the line line the cursor is over is a strategy def or a nodesubset def
		//using the following try clauses
		
		StrategyDef sd;
		//try parsing as a StrategyDef
		try{
			//construct a StrategyDef from this function's strategy argument (which is a strategy or node set)
			sd= StrategyDef.parse(strategy);
			//parse arguments as ClassGraphs and StrategyGraphs
			edu.neu.ccs.demeter.aplib.cd.ClassGraph cg= edu.neu.ccs.demeter.aplib.cd.ClassGraph.parse(myClass);
			
			//System.out.println (cg);
			
			//get the actual strategy out of the StrategyDef parsed
			edu.neu.ccs.demeter.dj.Strategy sg = new edu.neu.ccs.demeter.dj.Strategy(sd.get_strategyexpression().get_strategy().toString());
			
			//System.out.println (sg.toSimpleStrategy());
			
			//highlight the nodes and edges that pertain to the cg and strategy 
			highlightStrategies(cg,sg);
		}
		catch(RuntimeException re){
			
			//if it couldn't be parsed as a strategydef then try to parse as a NodeSubsetDef
			try{
				NodeSubsetDef ns = NodeSubsetDef.parse(strategy);
				edu.neu.ccs.demeter.aplib.cd.ClassGraph cg= edu.neu.ccs.demeter.aplib.cd.ClassGraph.parse(myClass);
				
				//highlight function specially for NodeSets
				highlightNodeSets(ns,cg,mySelector);
			}
			catch(RuntimeException re2){
				re2.printStackTrace();
			}
		}
		
		
	}
	public static void highlightNodeSets(NodeSubsetDef ns,ClassGraph c, SelectorL s){
		//clean out the nodes and edges, so old traversals aren't shown in the viewer window
		Main.nodesTraversed = new HashSet();
		Main.edgesTraversed =  new HashSet();
		
		NodeSubsetExpression nse = ns.get_nodesubsetexpression();
		
		//in an abstract manner, interpret the the nodesubsetexpression
		//interpretExpression gets evaluated according to the type of NodeSubsetExpression
		nse.interpretExpression(s,c);
		
		//highlight the viewer window according to the nodesTraversed and edgesTraversed
		//results from interpretExpression
		Main.highlightStrategiesBinary(c,nodesTraversed,edgesTraversed); 
	}
	
	public static void highlightStrategies(edu.neu.ccs.demeter.aplib.cd.ClassGraph cg, edu.neu.ccs.demeter.dj.Strategy sg) {
		HashSet nodesTraversed;
		HashSet edgesTraversed;
		cg = cg.normalize();
		AspectEditor.traversalViewer.setText(cg.normalize().toString());
		//String nodes="";
		//highlight Nodes
		try {
			edu.neu.ccs.demeter.aplib.TraversalGraph trav = new edu.neu.ccs.demeter.aplib.TraversalGraph(sg.toSimpleStrategy(),cg);
			nodesTraversed= new HashSet(trav.getNodeSets());
			Iterator i = nodesTraversed.iterator();
			//highlight the nodes traversed in the viewer window
			while(i.hasNext()){
				NodeSet ns = (NodeSet)i.next();
				//use style range to determine where to change the color in the viewer window
				StyleRange styleRange = new StyleRange();
				StyleRange styleRange2 = new StyleRange();
				
				//Find the node name in the viewer window
				styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString());
				
				styleRange.length = ns.getNode().toString().length();
				
				Shell shell = new Shell();
				Display display = shell.getDisplay();
				styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
				
				//StyleRange[] styleRanges={styleRange,styleRange2};
				
				AspectEditor.traversalViewer.setStyleRange(styleRange);
				//also highlight the definition of the node traversed
				styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString()+" =");
				styleRange.length = ns.getNode().toString().length();
				styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
				AspectEditor.traversalViewer.setStyleRange(styleRange);
				
				/*
				//and another case if there isn't a space between the '=' sign
				styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString()+"=");
				styleRange.length = ns.getNode().toString().length();
				styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
				AspectEditor.traversalViewer.setStyleRange(styleRange);
				*/
				
				//nodes+="\n"+ ns.getNode().toString();
			}
			//AspectEditor.traversalViewer.append(nodes);
		} catch (TraversalException e) {
			AspectEditor.traversalViewer.setText(e.getMessage());
		}
		
		//Highlight edges
		//do the same thing we did for nodes, and do it for edges
		try {
			edu.neu.ccs.demeter.aplib.TraversalGraph trav = new edu.neu.ccs.demeter.aplib.TraversalGraph(sg.toSimpleStrategy(),cg);
			edgesTraversed= new HashSet(trav.getEdgeSets());
			Iterator i2 = edgesTraversed.iterator();
			//highlight edges
			while(i2.hasNext()){
				EdgeSet es = (EdgeSet)i2.next();
				StyleRange styleRange = new StyleRange();
				styleRange.start = AspectEditor.traversalViewer.getText().indexOf(es.getEdge().toString());
				styleRange.length = es.getEdge().toString().length();
				Shell shell = new Shell();
				Display display = shell.getDisplay();
				styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
				AspectEditor.traversalViewer.setStyleRange(styleRange);
				//nodes+="\n"+ es.getEdge().toString();
			}
			//AspectEditor.traversalViewer.append(nodes);
		} catch (TraversalException e) {
			AspectEditor.traversalViewer.setText(e.getMessage());
		}
	}
	
	public static void highlightStrategiesBinary(edu.neu.ccs.demeter.aplib.cd.ClassGraph cg,HashSet nodesTraversed,HashSet edgesTraversed) {
		//add the classgraph to the viewer window (unstyled)
		AspectEditor.traversalViewer.setText(cg.normalize().toString());
		String nodes="";
		
		//highlight Nodes
		Iterator i = nodesTraversed.iterator();
		
		while(i.hasNext()){
			NodeSet ns = (NodeSet)i.next();
			StyleRange styleRange = new StyleRange();
			styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString());
			styleRange.length = ns.getNode().toString().length();
			Shell shell = new Shell();
			Display display = shell.getDisplay();
			styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
			AspectEditor.traversalViewer.setStyleRange(styleRange);
			nodes+="\n"+ ns.getNode().toString();
			
			styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString()+" =");
			styleRange.length = ns.getNode().toString().length();
			styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
			AspectEditor.traversalViewer.setStyleRange(styleRange);
			
			/*
			//and another case if there isn't a space between the '=' sign
			styleRange.start = AspectEditor.traversalViewer.getText().indexOf(ns.getNode().toString()+"=");
			styleRange.length = ns.getNode().toString().length();
			styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
			AspectEditor.traversalViewer.setStyleRange(styleRange);
			*/
		}
		//AspectEditor.traversalViewer.append(nodes);
		
		//Highlight edges

			Iterator i2 = edgesTraversed.iterator();
		
			while(i2.hasNext()){
				EdgeSet es = (EdgeSet)i2.next();
				StyleRange styleRange = new StyleRange();
				styleRange = new StyleRange();
				styleRange.start = AspectEditor.traversalViewer.getText().indexOf(es.getEdge().toString());
				styleRange.length = es.getEdge().toString().length();
				Shell shell = new Shell();
				Display display = shell.getDisplay();
				styleRange.foreground = display.getSystemColor(SWT.COLOR_RED);
				AspectEditor.traversalViewer.setStyleRange(styleRange);
				nodes+="\n"+ es.getEdge().toString();
			}
			
	}
	
	//checks the semantics of the content in the editor window
	public static void checkSemantics(XAspectLanguage2 s) {
		String stringOfErrors = "Starting Semantic Checker...\n";
		boolean semanticError = false;
		edu.neu.ccs.demeter.dj.ClassGraph cg = new
		edu.neu.ccs.demeter.dj.ClassGraph(true,false);
		
		try {
			String classpath = System.getProperty("java.class.path");
			String[] classpaths = classpath.split(File.pathSeparator);
			int last_slash = classpaths[0].lastIndexOf("\\");
			String sg_path =
				classpaths[0].substring(0, last_slash)
				+ "\\plugins\\aspectEditor_1.0.0\\aspectElements.jar";
			
			JarFile jf = new JarFile(new File(sg_path));
			Enumeration enu = jf.entries();
			while(enu.hasMoreElements()){
				String entry = enu.nextElement().toString();
				if(entry.indexOf("class")!=-1){
					entry = entry.replace('/', '.');
					int last_dot = entry.lastIndexOf(".class");
					entry = entry.substring(0, last_dot);
					
					Class cl = Class.forName(entry);
					cg.addClass(cl);
				}
			}
		}
		catch(Exception e1){
			System.out.println("Error opening file aspectElements.jar using the Windows method:");
			System.out.println("\t" + e1 + "\n");
			System.out.println("Will try the MAC/Solaris/LINUX method");
			
			try {
				String classpath = System.getProperty("java.class.path");
				String[] classpaths = classpath.split(File.pathSeparator);
				int last_slash = classpaths[0].lastIndexOf("/");
				String sg_path =
					classpaths[0].substring(0, last_slash)
					+ "/plugins/aspectEditor_1.0.0/aspectElements.jar";
				
				JarFile jf = new JarFile(new File(sg_path));
				Enumeration enu = jf.entries();
				while(enu.hasMoreElements()){
					String entry = enu.nextElement().toString();
					if(entry.indexOf("class")!=-1){
						entry = entry.replace('/', '.');
						int last_dot = entry.lastIndexOf(".class");
						entry = entry.substring(0, last_dot);
						
						Class cl = Class.forName(entry);
						cg.addClass(cl);
					}
				}
			}
			catch(Exception e2) {
				System.out.println("Could not open aspectElements.jar using the Windows or MAC/Solaris/LINUX" +
				"method:");
				System.out.println("\t" + e2 + "\n");
			}
		}
		
		//Check traversals
		//HashSet declaredStrategies = new HashSet(cg.gather(s, "from XAspectLanguage2 to StrategyName"));
		
		//Check class dictionary(s)
		
		//setup iterator for list of aspects in the cd
		Enumeration aspectEnumeration = s.get_aspectlanguage_list().elements();
		HashSet cdNames = new HashSet();//has set that collects all cd names for semantic checking of traversal declaratons
		
		//loops through aspect cd (will contain ClassDictionaries and Traversals)
		while(aspectEnumeration.hasMoreElements()){
			
			Object currObj = aspectEnumeration.nextElement();
			String cdType = "aspectEditor.aspectEditorUtils.ClassDictionaryL";
			String traversalType= "aspectEditor.aspectEditorUtils.SelectorL";
			//check if it's a class dictionary then check semantics
			if(cdType.equals(currObj.getClass().getName())){
				
				//get name of the current cd and add it to cdNames, get's used in semantic check for selector langs
				edu.neu.ccs.demeter.dj.Visitor cdnamev = new edu.neu.ccs.demeter.dj.Visitor(){
					Object cdname = new Object();
					void before(ClassDictionaryName cdName){
						cdname= cdName.get_ident();
					}
					public Object getReturnValue(){
						return cdname;
					}
				};
				
				
				//use cdnamev visitor to get cd names
				cg.traverse(s, "from * via aspectEditor.aspectEditorUtils.ClassDictionaryL to *", cdnamev);
				Object cdNameHash = cdnamev.getReturnValue();
				cdNames.add(cdNameHash);
				
				
				/////////CHECK TO SEE IF ALL CLASSES USED ARE DEFINED (our first handin)////
				ClassDictionaryL currDict = (ClassDictionaryL)currObj;
				edu.neu.ccs.demeter.aplib.cd.ClassGraph inputcg= edu.neu.ccs.demeter.aplib.cd.ClassGraph.parse(currDict.get_classdict().get_text().toString());
				inputcg = inputcg.normalize();
				//get container of used classes as a HashSet
				HashSet usedClasses = new HashSet(inputcg.getNodes());
				
				//setup iterator to go through nodes and see if they're defined
				Iterator usedIterator = usedClasses.iterator();
				while(usedIterator.hasNext()){
					Object currClass = usedIterator.next();
					if(!inputcg.definesClass(currClass.toString())){
						System.out.println();
						System.out.println("Semantic Error: The class " + currClass.toString() + " is used, but not defined.");
						stringOfErrors += "\n" + "Semantic Error: The class " + currClass.toString() + " is used, but not defined.";
						semanticError = true;
					}
				}
				//////////////////////END CHECK FOR USED CLASSES///////////////////////////
				
				
			}
			
			//check if current aspect definition is a set of traversals and check semantics
			if(traversalType.equals(currObj.getClass().getName())){
				SelectorL currSelector = (SelectorL)currObj;
				////////////////////CHECK SEMANTICS FOR ASPECT TRAVERSALS///////////////////////
				//visitor for gathering selector names
				
				edu.neu.ccs.demeter.dj.Visitor selectorv = new edu.neu.ccs.demeter.dj.Visitor(){
					final HashSet selnamehash = new HashSet();
					void before(SelectorName selectorName){
						selnamehash.add(selectorName.get_ident());
					}
					public Object getReturnValue(){
						return (HashSet)selnamehash;
					}
				};
				
				//visitor for gathering strategy names
				
				edu.neu.ccs.demeter.dj.Visitor strategyv = new edu.neu.ccs.demeter.dj.Visitor(){
					final HashSet stratnamehash = new HashSet();
					void before(StrategyName strategyName){
						stratnamehash.add(strategyName.get_name());
					}
					public Object getReturnValue(){
						return (HashSet)stratnamehash;
					}
				};
				
				//visitor for gathering node subset names declared
				
				edu.neu.ccs.demeter.dj.Visitor subsetnamev = new edu.neu.ccs.demeter.dj.Visitor(){
					final HashSet subsetnamehash = new HashSet();
					void before(NodeSubsetName subsetName){
						subsetnamehash.add(subsetName.get_name());
					}
					public Object getReturnValue(){
						return (HashSet)subsetnamehash;
					}
				};
				
				//visitor for gathering node subset names used
				
				edu.neu.ccs.demeter.dj.Visitor subsetnamev2 = new edu.neu.ccs.demeter.dj.Visitor(){
					final HashSet subsetnamehash = new HashSet();
					void before(NodeSubsetName subsetName){
						subsetnamehash.add(subsetName.get_name());
					}
					public Object getReturnValue(){
						return (HashSet)subsetnamehash;
					}
				};
				
				//visitor for gathering node strategy names in the node set declarations
				
				edu.neu.ccs.demeter.dj.Visitor strategynamev = new edu.neu.ccs.demeter.dj.Visitor(){
					final HashSet strategynamehash = new HashSet();
					void before(StrategyName strategyName){
						strategynamehash.add(strategyName.get_name());
					}
					public Object getReturnValue(){
						return (HashSet)strategynamehash;
					}
				};
				
				
				//use selectorv visitor to get selector names
				cg.traverse(s, "from * to *", selectorv);
				HashSet selNameHash = (HashSet)selectorv.getReturnValue();
				
				//use strategyv visitor to get strategy names use in strategy declarations
				cg.traverse(s, "from * through aspectEditor.aspectEditorUtils.StrategyDef to *", strategyv);
				HashSet stratNameHash = (HashSet)strategyv.getReturnValue();
				//System.out.println(stratNameHash.size()+"size here");  
				
				//use nodesubsetv visitor to get node subset names declared
				cg.traverse(s, "from aspectEditor.aspectEditorUtils.XAspectLanguage2 bypassing aspectEditor.aspectEditorUtils.NodeSubsetExpression to-stop aspectEditor.aspectEditorUtils.NodeSubsetName", subsetnamev);
				HashSet subsetNameHashDecl = (HashSet)subsetnamev.getReturnValue();
				//System.out.println(subsetNameHashDecl.size()+"size here1");         
				
				//use nodesubsetv visitor to get node subset names used
				cg.traverse(s, "from * via aspectEditor.aspectEditorUtils.NodeSubsetExpression to *", subsetnamev2);
				HashSet subsetNameHashUsed = (HashSet)subsetnamev2.getReturnValue();
				//System.out.println(subsetNameHashUsed.size()+"size here2");         
				
				//use strategynamev visitor to get strategy names in node set declarations
				cg.traverse(s, "from * through aspectEditor.aspectEditorUtils.GraphNodes to *", strategynamev);
				HashSet strategyNameHash = (HashSet)strategynamev.getReturnValue();
				//System.out.println(subsetNameHash.size()+"size here");
				
				//create iterator for used selector names
				Iterator selectorNameI = selNameHash.iterator();
				
				//iterate through selector names to make sure they are defined
				while(selectorNameI.hasNext()){
					Object currSelectorN = selectorNameI.next();
					//System.out.println(currSelectorN.toString());
					if(!stratNameHash.contains(currSelectorN)){
						System.out.println("Semantic Error: There is not a strategy named (Selector Name Error): "+ currSelectorN.toString() );
						semanticError = true;
						stringOfErrors += "\n" + "Semantic Error: There is not a strategy named (Selector Name Error): "+ currSelectorN.toString(); 
					}
				}
				
				//create iterator for used strategy names
				Iterator strategyNameI = strategyNameHash.iterator();
				
				//iterate through strategy names in node set declarations to make sure they are defined
				while(strategyNameI.hasNext()){
					Object currStrategyN = strategyNameI.next();
					//System.out.println(currStrategyN.toString() +"(HERE)");
					if(!stratNameHash.contains(currStrategyN)){
						System.out.println("Semantic Error: There is not a strategy named (Node Set Error): "+ currStrategyN.toString() );
						semanticError = true;
						stringOfErrors += "\n" + "Semantic Error: There is not a strategy named (Node Set Error): "+ currStrategyN.toString();
					}
				}
				
				//create iterator for used subset names
				Iterator subsetNameI = subsetNameHashUsed.iterator();         
				//System.out.println(subsetNameHashDecl.size() + "size here3");
				
				//iterate through node names in node set with node subsets to make sure they are defined
				while(subsetNameI.hasNext()){
					Object currSubsetN = subsetNameI.next();
					//System.out.println(currSubsetN.toString() +"(HERE2)");
					if(!subsetNameHashDecl.contains(currSubsetN)){
						System.out.println("Semantic Error: There is not a strategy named (Node Subset Error): "+ currSubsetN.toString() );
						semanticError = true;
						stringOfErrors += "\n" + "Semantic Error: There is not a strategy named (Node Subset Error): "+ currSubsetN.toString();
					}
				}
				
				
				//get all cd names used in chosen class dictionary declarations
				edu.neu.ccs.demeter.dj.Visitor cdnamev2 = new edu.neu.ccs.demeter.dj.Visitor(){
					HashSet cdname = new HashSet();
					void before(ClassDictionaryName cdName){
						cdname.add(cdName.get_ident());
					}
					public Object getReturnValue(){
						return cdname;
					}
				};
				
				
				//use cdnamev visitor to get cd names
				cg.traverse(s, "from * bypassing aspectEditor.aspectEditorUtils.ClassDictionaryL to *", cdnamev2);
				HashSet cdNameHash2 = (HashSet)cdnamev2.getReturnValue();
				
				
				//create iterator for class dictionary names used in strategy declarations
				Iterator cdNameI = cdNameHash2.iterator();
				
				//iterate through node names in node set with node subsets to make sure they are defined
				while(cdNameI.hasNext()){
					Object currcdN = cdNameI.next();
					if(!cdNames.contains(currcdN)){
						System.out.println("Semantic Error: There is no class dictionary named (chosen class dictionary Error): "+ currcdN.toString() );
						semanticError = true;
						stringOfErrors += "\n" + "Semantic Error: There is no class dictionary named (chosen class dictionary Error): "+ currcdN.toString();
					}
				}
				
				///////////////////////////END SEMANTIC CHECK FOR ASPECT TRAVERSALS///////////////////////
				stringOfErrors = stringOfErrors + "\n\nSemantic Checking Complete.";
				stringOfErrors = stringOfErrors + "\n \n \n Please click a traversal aspect line to peform a traversal";
				AspectEditor.traversalViewer.setText(stringOfErrors);
			}
		}
	}
	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_);
	}
	
}
