package org.prgtest.genetic;

import java.awt.Canvas;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Vector;

import org.jgap.Chromosome;
import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.prganalysis.walker.NonRuntimeFunc;
import org.prgtest.common.AVar;
import org.prgtest.settings.KernelTG;

import barat.parser.BinaryOperationImpl;
import barat.parser.LiteralImpl;
import barat.parser.VariableAccessImpl;

import com.ibm.graph.VertexMissingException;

public class FitnesssFSymbolic extends FitnessFunction {

	//@Override
	final static int TRUE = 1000000;
	final static int FALSE_AND_EQUAL= 1000001;
	final static int FALSE = 1000002;
	final static int EQUALITY_FALSE = 1000003;
	Integer a,b;
	static public double globalResult; 
	protected double evaluate(Chromosome a_subject) {
		// TODO Auto-generated method stub
		Gene[] geneArray = a_subject.getGenes();
		KernelTG kernel = KernelTG.getInstance();

		Object vertexObject;
		barat.parser.VariableDeclarationImpl variable;
		Integer intA = TRUE;
		Integer intB = TRUE;
		org.prgtest.common.AVar var;
		double result;
		
		result = 0;
		globalResult = 0;
		int condNum = kernel.getProgramNonRuntimeResults().getGraph().sizeVertices() - 2;
		double temp;
		int flag = 0;
		int check = -1;
		ArrayList conditionList = new ArrayList();
		ArrayList resultsList = new ArrayList();
		ArrayList operandsList = new ArrayList();
		String c;
		
	    /*
		for(int i=0;i<geneArray.length;i++)
		 {
			 var = (org.prgtest.common.AVar)geneArray[i].getAllele();
			 System.out.println(var.getVariableName() + " : " + var.getCurrentValue());	
		 }
		 */		
		
		for (int y = 0; y < kernel.getProgramNonRuntimeResults().getGraph()
				.sizeVertices(); y++) {
			try {
				vertexObject = kernel.getProgramNonRuntimeResults().getGraph()
						.indexVertex(y);
				

				org.prganalysis.graph.TVertex tvertex = (org.prganalysis.graph.TVertex)vertexObject;
				
				 if ((tvertex.getName().equals("Start"))||(tvertex.getName().equals("End")))
	             {
	            	 continue;
	             }
	             else
	             {
	            	 //System.out.println("--vertex--: "+tvertex.getNodeElement().toString());
	            	 //System.out.println("vertex: "+tvertex.getNodeElement().toString());
	            	 
	            	 if (tvertex.getNodeElement() instanceof barat.parser.VariableDeclarationImpl)
	            	 {       
	            		 
	            		 variable = (barat.parser.VariableDeclarationImpl)tvertex.getNodeElement();
	            		 //System.out.println("var: " + variable);
	            		
	            		 conditionList.clear();
	            		 resultsList.clear();
	            		 operandsList.clear();
	            		 
	            		 if ((variable.toString().contains("||")) || (variable.toString().contains("&&"))){
	            			          			 
	            			 //System.out.println(variable.toString().indexOf("||"));
	            			 
	            			 c = variable.toString();
	            			 
	            			 do{
	            				 check = c.indexOf("||");
	            				 
	            				 if (check != -1)
	            				 {
	            					c = c.substring(check+2);
	            					operandsList.add("||");
	            				 }
	            				 else
	            				 {
	            					 check = c.indexOf("&&");
	            					 if (check != -1)
		            				 {
	            						c = c.substring(check+2);
	            						operandsList.add("&&");
		            				 }
	            				 }
	            			 }while(check != -1);
	            			 
	            			 barat.parser.BinaryOperationImpl cond = (barat.parser.BinaryOperationImpl)variable.getVariable().getInitializer();
	            			 barat.parser.BinaryOperationImpl originalCondition = cond;
	            			 //System.out.println("left: " + cond.getLeftOperand());
	            			 //System.out.println("right " + cond.getRightOperand());
	            			// System.out.println("right C " + cond.getLeftOperand().getClass());
	            			 //System.out.println("left C " + cond.getRightOperand().getClass());
	            			 do{
	            			 if (cond.getRightOperand() instanceof barat.parser.ParenExpressionImpl)
	            			 {
	            				 barat.parser.ParenExpressionImpl rightOp = (barat.parser.ParenExpressionImpl) cond.getRightOperand();
	            				 conditionList.add(rightOp);
	            			 }
	            			 else
	            			 {
	            				 if (cond.getRightOperand() instanceof barat.parser.BinaryOperationImpl)
		            			 {
	            					 barat.parser.BinaryOperationImpl rOp= (barat.parser.BinaryOperationImpl)cond.getRightOperand();
	            					 cond = (barat.parser.BinaryOperationImpl) rOp;
	            					 continue;
		            			 }
	            				 else
	            				 {
	            					 conditionList.add(cond);
	            				 }
	            			 }
	            			 if (cond.getLeftOperand()instanceof barat.parser.ParenExpressionImpl)
	            			 {
	            				 barat.parser.ParenExpressionImpl rightOp = (barat.parser.ParenExpressionImpl) cond.getLeftOperand();
	            				 conditionList.add(rightOp);
	            				 flag = 1;
	            			 }
	            			 else
	            			 {
	            				 if (cond.getRightOperand() instanceof barat.parser.BinaryOperationImpl)
		            			 {
	            				 barat.parser.BinaryOperationImpl rOp= (barat.parser.BinaryOperationImpl)cond.getLeftOperand();
	            				 cond = (barat.parser.BinaryOperationImpl) rOp;
	            				 continue;
		            			 }
	            				 else
	            				 {
	            				//	 barat.parser.LiteralImpl rop = (barat.parser.LiteralImpl)cond.getRightOperand();
	            				//	 conditionList.add(rop);
	            					
	            					 if (originalCondition.getLeftOperand() instanceof BinaryOperationImpl){
	            						 cond = (BinaryOperationImpl)originalCondition.getLeftOperand();
	            						 originalCondition = (BinaryOperationImpl)originalCondition.getLeftOperand();
	            					 }
	            					 else
	            						 break;
	            					 continue;
	            				 }
	            			 }
	            			 }while(flag==0);
	            			 
	            			 
	            			 for(int b = 0; b<conditionList.size();b++)
	            			 {
	            				 intA = getBinOperValue(conditionList.get(b),geneArray);
	    	            		 result = computeConditionValue(intA);
	    	            		 resultsList.add(result);
	    	            		 
	    	            		 //if ((intA == FALSE)||(intA == FALSE_AND_EQUAL) || (intA == EQUALITY_FALSE))
	    	            		// {
	    	            			 //intB = intA;
	    	            		 //}
	    	            		 
	            			 }
	            			 
	            			// intA = intB;
	            			 
	            			 int counter=-1;
	            			 double min;
	            			 
	            			 min = (Double)resultsList.get(0);
	            			 result = min;
	            			 
	            			 for(int b = 1; b<resultsList.size();b++)
	            			 {
	            				 counter++;
	            				 //System.out.println(((Double)resultsList.get(b)).doubleValue());
	            				 if (operandsList.get(counter).equals("||"))
	            				 {
	            					 if ((Double)resultsList.get(b)<min)
	            					 {
	            						 min = (Double)resultsList.get(b);
	            						 result = min;
	            					 }
	            				 }
	            				 else
	            				 {
	            					 result = result + (Double)resultsList.get(b);
	            				 }
	            				 
	            			 }
	            			 counter = 0;
	            		 }
	            		 
	            		 else
	        
	            		 {
	            		 intA = getBinOperValue(variable.getVariable().getInitializer(),geneArray);
	            		 
	            		 result = computeConditionValue(intA);
	            		 }
	            		 
	            		 if ((intA == FALSE)||(intA == FALSE_AND_EQUAL) || (intA == EQUALITY_FALSE))
	            		 {
	            			 y = kernel.getProgramNonRuntimeResults().getGraph().sizeVertices();
	            			 //System.out.println("stoped here "+variable.getVariable().getInitializer());
	            		 }            		 
	            		 globalResult = globalResult + 1/(condNum + result);
	            		
	            	 }
	            	 else
	            	 {
	            		 condNum = condNum -1;
	            	 }

	             }
				
			} catch (VertexMissingException ex) {
				ex.printStackTrace();
			}
		}
		
		if (globalResult >= 0.99999) {
            kernel.setHasCoverCriteria(true);	
            System.out.println("GR " + globalResult);
		}
		else
		{
			 System.out.println("GR " + globalResult);
		}
		return globalResult;
	}
	
	private int checkCondition (String operator)
	{

		if (operator.equals(">"))
		{
			 if(a.intValue() > b.intValue())
             	return TRUE;
			 else
			 {
				 if(a.intValue() == b.intValue())
		             	return FALSE_AND_EQUAL;
				 else
				 {
					 return FALSE;
				 }
					 
			 }
		}
		else if (operator.equals("<"))
		{
			 if(a.intValue() < b.intValue())
	             	return TRUE;
				 else
				 {
					 if(a.intValue() == b.intValue())
			             	return FALSE_AND_EQUAL;
					 else
					 {
						 return FALSE;
					 }
						 
				 }
		}
		else if (operator.equals(">="))
		{
			 if(a.intValue() >= b.intValue())
	             	return TRUE;
				 else
					return FALSE;

		}
		else if (operator.equals("<="))
		{
			 if(a.intValue() <= b.intValue())
	             	return TRUE;
				 else
					return FALSE;

		}
		else if (operator.equals("=="))
		{
			 if(a.intValue() == b.intValue())
	             	return TRUE;
				 else
					return EQUALITY_FALSE;

		}
		else if (operator.equals("!="))
		{
			 if(a.intValue() != b.intValue())
	             	return TRUE;
				 else
					return EQUALITY_FALSE;
		}
		
		return FALSE;
	}
	
	private double computeConditionValue (int flag)
	{
		double value = 0;
		
		if (flag==TRUE)
			value = 0.0;
		else
		{
			if (flag==FALSE_AND_EQUAL)
				value = 0.01;
			else
			{
				//if (flag==EQUALITY_FALSE)
				//	value = 100;
				//else
					value = Math.abs(this.a.intValue() - this.b.intValue());
			}
		}
		return value;
	}
	private Integer getBinOperValue(Object obj,Gene[] geneArray) {
	        Integer intA = null;
			org.prgtest.common.AVar var;
			int flag;
			
	        if (obj instanceof
	                LiteralImpl) {
	                LiteralImpl literalImpl = (LiteralImpl) obj;
	                intA = (Integer) literalImpl.constantValue();
	        }
	        else if (obj instanceof
	                   VariableAccessImpl) {
	        	VariableAccessImpl variableAccessImpl2 = (VariableAccessImpl)
                obj;
	       
	        	for(int i=0;i<geneArray.length;i++)
        		 {
        			 var = (org.prgtest.common.AVar)geneArray[i].getAllele();
        			 
           			 if (var.getVariableName().equals(variableAccessImpl2.getVariable().getName()))
           			 {
           				 intA = (Integer)var.getCurrentValue();
           				 break;
           			 }
        		 }		
	        }
	        else if (obj instanceof BinaryOperationImpl) {
	            BinaryOperationImpl binaryOperationImpl = (BinaryOperationImpl) obj;
	            a = getBinOperValue(binaryOperationImpl.getLeftOperand(),geneArray);
	            b = getBinOperValue(binaryOperationImpl.getRightOperand(),geneArray);
	            if (binaryOperationImpl.operator().equals("+")) {
	                intA = a.intValue() +
	                       b.intValue();
	            } else if (binaryOperationImpl.operator().equals("-")) {
	                intA = a.intValue() -
	                       b.intValue();
	            } else if (binaryOperationImpl.operator().equals("*")) {
	                intA = a.intValue() *
	                       b.intValue();
	            } else if (binaryOperationImpl.operator().equals("/")) {
	                if (b.intValue() == 0) {
	                    //System.out.println("**** ZERO DEVIDED ***");
	                    return 10;
	                }
	                intA = a.intValue() /
	                       b.intValue();
	            } else if (binaryOperationImpl.operator().equals(">")) {
	            	intA = checkCondition(">");
	            } else if (binaryOperationImpl.operator().equals("<")) {
	            	intA = checkCondition("<");	            	
	            }else if (binaryOperationImpl.operator().equals(">=")) {
	            	intA = checkCondition(">=");	            	
	            }else if (binaryOperationImpl.operator().equals("<=")) {
	            	intA = checkCondition("<=");	            	
	            }else if (binaryOperationImpl.operator().equals("==")) {
	            	intA = checkCondition("==");            	
	            }else if (binaryOperationImpl.operator().equals("!=")) {
	            	intA = checkCondition("!=");	            	
	            }else {
	                System.out.println(
	                        "Error not implement -getBinOperValue - instanceof BinaryOperationImpl");
	            }
	        } else if (obj instanceof barat.parser.ParenExpressionImpl) { //barat.parser.ParenExpressionImpl
	            barat.parser.ParenExpressionImpl parenExpressionImpl = (barat.
	                    parser.ParenExpressionImpl) obj;
	            
	            return getBinOperValue(parenExpressionImpl.getOperand(),geneArray);
	        	}
	        return intA;
	        }
}
