package org.prgtest.performer;
import java.awt.Canvas;
import java.util.Enumeration;
import java.util.Stack;
import java.util.Vector;
import org.prganalysis.graph.*;
import org.prganalysis.walker.NonRuntimeFunc;
import org.prgtest.common.AVar;
import org.prgtest.genetic.CoverageData;
import org.prgtest.settings.KernelTG;

import barat.parser.AOperandExpressionImpl;
import barat.parser.AssignmentImpl;
import barat.parser.CompilationUnitImpl;
import barat.parser.ExpressionStatementImpl;
import barat.reflect.VariableAccess;

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

public class ConditionOptimizer {

	final static String G = "%G%";		//>
	final static String S = "%S%";		//<
	final static String GE = "%GE%";	//>=
	final static String SE = "%SE%";	//<=
	final static String E = "%E%";		//==
	final static String NE = "%NE%";	//!=
	
	TGraph conditions;
	String pathName;
	ArrayList conditionList = new ArrayList<ArrayList>();
	ArrayList orList = new ArrayList();
	ArrayList cloneOrList = new ArrayList();
	ArrayList parameters = new ArrayList();
	int prev;
	int next;
	boolean edgeValue = true;
	
	public ConditionOptimizer(TGraph graph,String name)
	{	
		conditions = graph;
		pathName = name;
		prev = 0;
		next = 0;
	}
	
	public String computeNegation(String expression)
	{
		
		if (expression.contains(">="))
			expression = expression.replace(">=", S);
		
		if (expression.contains("<="))
			expression = expression.replace("<=", G);
		
		if (expression.contains("=="))
			expression = expression.replace("==", NE);
		
		if (expression.contains("!="))
			expression = expression.replace("!=", E);
		
		if (expression.contains(">"))
			expression = expression.replace(">", SE);
		
		if (expression.contains("<"))
			expression = expression.replace("<", GE);

		expression = expression.replace(G, ">");
		expression = expression.replace(S, "<");
		expression = expression.replace(GE, ">=");
		expression = expression.replace(SE, "<=");
		expression = expression.replace(E, "==");
		expression = expression.replace(NE, "!=");
		
		return expression;
	}
	
	public int dontOptimizeConditions()
	{
		TVertex tvertex;
		Writer output = null;
		String line;
		File file = new File("C:/LocalD/JavaWorkSpace/TestDataGenerator/src/org/testfiles/optConditions/"+"optConditions"+ pathName + "_"+0+".java");
		barat.parser.ParameterImpl param;
		
		try{
			output = new BufferedWriter(new FileWriter(file));
			
			line = "package org.testfiles;" + "\n" + "public class optConditions" + pathName + "_"+0 +" {" + "\n" + "\tpublic void methodA(";
    		
		     try{
				 for (int x = 0; x < this.conditions.sizeVertices(); x++) {
		             tvertex = (TVertex) this.conditions.indexVertex(x);
		             
	             if ((tvertex.getName().equals("Start"))||(tvertex.getName().equals("End")))
	             {
	            	 continue;
	             }
	             else
	             {
	            	if ( tvertex.getNodeElement() instanceof  barat.parser.ParameterImpl)
	     			{
	     				param = (barat.parser.ParameterImpl)tvertex.getNodeElement();
	     				parameters.add(param.getType().toString() + " " + param.toString());
	     			}
	             }
			 }
			 }catch(Exception e){
				 e.printStackTrace();
			 }
			for (int z=0;z<parameters.size();z++)
    		{
    			if (z==parameters.size()-1)
    			{
    				line = line + parameters.get(z) + ") \n { \n";
    			}
    			else
    			{
    				line = line + parameters.get(z) + ", ";
    			}
    		}
    		output.write(line);
    		line = "";
			 for (int x = 0; x < this.conditions.sizeVertices(); x++) {
	             tvertex = (TVertex) this.conditions.indexVertex(x);
	             
            if ((tvertex.getName().equals("Start"))||(tvertex.getName().equals("End"))||(tvertex.getName().contains("READ")))
            {
           	 continue;
            }
            else
            {
    		try{
    			
    			line = "\t"+tvertex.getName() + "\n";
    			
    			output.write(line);
    			
				}catch(Exception e){};	
            }
		 }
			 
	     output.write("} \n }");	 
		 output.close();
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		
		 return 1;
	}
	
	public int optimizeConditions()
	{
		TVertex tvertex;
		TVertex previous_tvertex;
		ArrayList orCondPos = new ArrayList<Integer>();
		ArrayList condition = new ArrayList();
		ArrayList optConditions = new ArrayList();
		ArrayList tempOptConditions = new ArrayList();
		int pos = 0;
		int position = 0;
		int cursor = -1;
		int current = -1;
		int progNumber = 0;
		int complex = 1;
		Writer output = null;
		String temp;
		String SymbolicProgram = "";
		String preProgram = "";
		String tempPreProgram = "";
		int flag = 0;
    	int count = 1; 
	     try{
			 for (int x = 0; x < this.conditions.sizeVertices(); x++) {
	             tvertex = (TVertex) this.conditions.indexVertex(x);
	             
             if ((tvertex.getName().equals("Start"))||(tvertex.getName().equals("End")))
             {
            	 continue;
             }
             else
             {
            	 	
            	analyzeCondition(tvertex);
             }
		 }
		 }catch(Exception e){
			 e.printStackTrace();
		 }
		
		 if (conditionList.size()>0)
			{		 	
			 	//System.out.println("ConditionList Size: "+conditionList.size());
				//for (int i=0;i<conditionList.size();i++)
				//	{
				//		condition = (ArrayList)conditionList.get(i);
				//		
				//		for(int j=0;j<condition.size();j++)
				//			System.out.println("condition "+ i + "." + j + " : " + condition.get(j));
				//	}
				
			//	correctList.add(computeNegation("("+(String)tempList.get(k-1)) + ")" + "&&" + "(" + tempList.get(k) + ")");
				 
				for (int j=0; j<conditionList.size();j++)
				{
					complex = complex * ((ArrayList)conditionList.get(j)).size();
					
					if (((ArrayList)conditionList.get(j)).size()>1)
					{
						orCondPos.add(j);
					}
				}
			
				int start;
		    	int end;
		    	start = 0;
		    	int d = 0;
		    	int k= 0;
		    	
		    	
		    	for (int i=0;i<conditionList.size();i++)
		    	{
		    		
		    		condition = (ArrayList)conditionList.get(i);
	    			
    				if ((condition.size()>1)&&optConditions.size()==0)
    				{		
    					do{
    				
    					preProgram = SymbolicProgram;
    					for (int s = condition.size()-1; s>position;s--)
    					{
    						temp = computeNegation((String)condition.get(s).toString());
    						temp = temp.replace("[", "(");
    						temp = temp.replace("]", ")");
    						preProgram = preProgram + "\tboolean O_C_" + pos + " = " + temp + ";\n";
    						pos++;
    					}
					
    					preProgram = preProgram + "\tboolean O_C_" + pos + " = " + condition.get(position).toString() + ";\n";	
    					pos++;	
				
    					optConditions.add(preProgram);
    					position ++;
    					}while(position != condition.size());
    					
    					//preProgram = "";
    					//for (int s=0;s<condition.size();s++)
    					//{
    					//	temp = (String)condition.get(s).toString();
    					//	temp = temp.replace("[", "(");
    					//	temp = temp.replace("]", ")");
    					//	preProgram = preProgram + "\tboolean O_C_" + pos + " = " + temp + ";\n";
    					//	pos++;
    					//}
    					//optConditions.add(preProgram);
    					//position = 0;
    				}
    				else
    				{
    					if ((condition.size()>1))
    					{
    						tempOptConditions = (ArrayList)optConditions.clone();
    						
    						optConditions.clear();
    						
    						int max = tempOptConditions.size();
    							
    						for (int h=0;h<max;h++)
    	    					{
    								position = 0;
    	    						SymbolicProgram = tempOptConditions.get(h).toString();
    	    						SymbolicProgram = SymbolicProgram.replace("[", "(");
    	    						SymbolicProgram = SymbolicProgram.replace("]", ")");
    								
    							do{
    								preProgram = SymbolicProgram;
    								for (int s = condition.size()-1; s>position;s--)
    								{
    									temp = computeNegation((String)condition.get(s).toString());
    									temp = temp.replace("[", "(");
    									temp = temp.replace("]", ")");
    									preProgram = preProgram + "\tboolean O_C_" + pos + " = " + temp + ";\n";
    									pos++;
    								}
    						
   									preProgram = preProgram + "\tboolean O_C_" + pos + " = " + condition.get(position).toString() + ";\n";	
   									pos++;	
   					
   									optConditions.add(preProgram);
   									position ++;
    	    						}while(position != condition.size());		
    								
    								//preProgram = SymbolicProgram;
    							
    								//for (int s=0;s<condition.size();s++)
    								//{
    								//	temp = (String)condition.get(s).toString();
    								//	temp = temp.replace("[", "(");
    								//	temp = temp.replace("]", ")");
    								//	preProgram = preProgram + "\tboolean O_C_" + pos + " = " + temp + ";\n";
    								//	pos++;
    								//}
    								//optConditions.add(preProgram);
    	    					}
    					}
    					else
    					{
    						if (optConditions.size()>0)
    						{
    							for (int j=0;j<optConditions.size();j++)
    							{
    								preProgram = optConditions.get(j).toString();
    								preProgram = preProgram + "\tboolean O_C_" + pos + " = " + condition.toString() + ";\n";
    								preProgram = preProgram.replace("[", "(");
    								preProgram = preProgram.replace("]", ")");
    								optConditions.set(j, preProgram);
    								pos++;
    							}
    						}
    						else
    						{
    							preProgram = "\tboolean O_C_" + pos + " = " + condition.toString() + ";\n";
								preProgram = preProgram.replace("[", "(");
								preProgram = preProgram.replace("]", ")");
    							pos++;
    							optConditions.add(preProgram);
    						}
    					}
    				}
		    	}
    				//for (int y=0; y<optConditions.size();y++)
		    	for (int y=optConditions.size()-1; y<=0;y--)
			    	{
			    		SymbolicProgram = "package org.testfiles;" + "\n" + "public class optConditions" + pathName + "_"+progNumber +" {" + "\n" + "\tpublic void methodA(";
			    		for (int z=0;z<parameters.size();z++)
			    		{
			    			if (z==parameters.size()-1)
			    			{
			    				SymbolicProgram = SymbolicProgram + parameters.get(z) + ") \n { \n";
			    			}
			    			else
			    			{
			    				SymbolicProgram = SymbolicProgram + parameters.get(z) + ", ";
			    			}

			    		}
		            
			    		SymbolicProgram = SymbolicProgram + optConditions.get(y);
			    		SymbolicProgram = SymbolicProgram + "\t}" + "\n" + "}";			
			    		preProgram = "";
			    		pos =0;
			    		File file = new File("C:/LocalD/JavaWorkSpace/TestDataGenerator/src/org/testfiles/optConditions/"+"optConditions"+ pathName + "_"+progNumber +".java");
			    		
					
			    		try{
			    			output = new BufferedWriter(new FileWriter(file));
			    			output.write(SymbolicProgram);
			    			output.close();
							}catch(Exception e){};
							SymbolicProgram="";
							progNumber++;
							tempPreProgram = "";
			    	}
    		
			}
		 return progNumber;
	}
				
	public void analyseOperand(barat.parser.ParenExpressionImpl operand, int flag)
	{
		ArrayList andList = new ArrayList();
		String op;
		op = operand.toString();
		
		if (op.contains("||") || (op.contains("&&")))
		{	
			if (operand.getOperand() instanceof barat.parser.BinaryOperationImpl)
				createHashTable((barat.parser.BinaryOperationImpl)operand.getOperand());
		}
		else
		{
			if (flag == 1)
			{
				orList.add(op);	
			}
			else
			{
				andList.add(op);
				conditionList.add(andList);
			}
		}
	}
	
	public ArrayList createAndList(String op)
	{
		ArrayList tmpList = new ArrayList();
		
		tmpList.add(op);
		
		return tmpList;
	}

	public ArrayList createAndList(ArrayList op)
	{
		ArrayList tmpList = new ArrayList();
		
		for (int i=0; i<op.size();i++)
			tmpList.add((String)op.get(i));
		
		return tmpList;
	}
	
	public void createHashTable(barat.parser.BinaryOperationImpl operand)
	{
		ArrayList andList = new ArrayList();
		String op;
		int flag = 0;
		op = operand.toString();
		
		while (flag==0)
		{
		//if ((op.contains("||") && (op.contains("&&"))) || (op.contains("||")))
			if (op.contains("||") || (op.contains("&&")))
		{
			if ((operand.operator() == "||"))
			{	
				op = operand.getRightOperand().toString();
				if (operand.getRightOperand() instanceof barat.parser.ParenExpressionImpl)
				{
					analyseOperand(( barat.parser.ParenExpressionImpl)operand.getRightOperand(),1);
				}
				else
				{
					orList.add(op);
				}
				
				op = operand.getLeftOperand().toString();
				if (operand.getLeftOperand() instanceof barat.parser.ParenExpressionImpl)
				{
					analyseOperand(( barat.parser.ParenExpressionImpl)operand.getLeftOperand(),1);
					
					if(orList.size()>0)
					{
						cloneOrList = (ArrayList)orList.clone();
						conditionList.add(cloneOrList);
						orList.clear();
					}
					flag = 1;
				}
				else
				{
					operand = (barat.parser.BinaryOperationImpl)operand.getLeftOperand();
				}
				
			}
			else
			{
				op = operand.getRightOperand().toString();
				if (operand.getRightOperand() instanceof barat.parser.ParenExpressionImpl)
				{
					analyseOperand(( barat.parser.ParenExpressionImpl)operand.getRightOperand(),0);
				}
				else
				{
					conditionList.add(createAndList(op));
				}
				
				op = operand.getLeftOperand().toString();
				if (operand.getLeftOperand() instanceof barat.parser.ParenExpressionImpl)
				{
					analyseOperand(( barat.parser.ParenExpressionImpl)operand.getLeftOperand(),0);
					
					//cloneOrList = (ArrayList)orList.clone();
					//conditionList.add(cloneOrList);
					//orList.clear();
					flag = 1;
				}
				else
				{
					operand = (barat.parser.BinaryOperationImpl)operand.getLeftOperand();
				}
			}

		}
		else
		{
			if (orList.size()>0)
			{
				orList.add(op);
				cloneOrList = (ArrayList)orList.clone();
				conditionList.add(cloneOrList);
				orList.clear();
			}
			else
			{
				conditionList.add(createAndList(op));
			}
			flag = 1;
		}
	   }
    }
	
	public void analyzeCondition(TVertex currentCondition)
	{
		//System.out.println("Condition: " +currentCondition.getNodeElement().toString());
		//System.out.println("Condition Class: " +currentCondition.getNodeElement().getClass());
		
		barat.parser.VariableDeclarationImpl condition;
		barat.parser.LocalVariableImpl variable;
		barat.parser.BinaryOperationImpl tempOperand;
		barat.parser.BinaryOperationImpl operand;
		barat.parser.ParenExpressionImpl rightOperand;
		barat.parser.ParenExpressionImpl leftOperand;
		barat.parser.ParameterImpl param;
		String op;
		ArrayList tempList = new ArrayList();
		ArrayList secList = new ArrayList();
		ArrayList correctList = new ArrayList();
		int flag = 0;
		
		if ( currentCondition.getNodeElement() instanceof  barat.parser.VariableDeclarationImpl)
		{
			condition = (barat.parser.VariableDeclarationImpl)currentCondition.getNodeElement();	
			variable = (barat.parser.LocalVariableImpl)condition.getVariable();
			
			operand = (barat.parser.BinaryOperationImpl)condition.getVariable().getInitializer();
			
			op = operand.toString();

        	createHashTable(operand);
		}
		else
		{
			if ( currentCondition.getNodeElement() instanceof  barat.parser.ParameterImpl)
			{
				param = (barat.parser.ParameterImpl)currentCondition.getNodeElement();
				parameters.add(param.getType().toString() + " " + param.toString());
			}
		}
	}
}
