package background.expressions;

import background.VariableManager;
import background.systemVariables.*;

public class LogicalTree extends EvaluationTree<Boolean>{
	
	
	public LogicalTree(String s, VariableManager vm) {
		super(s, vm);
	}
		
	
	public static Boolean hasOperator(String s) {		
		String[] log = {"&&", "||", "!", "==", ">=", "<=", ">", "<", "!=", "<>"};
		
		for (int i=0; i<log.length; i++) {
			if (s.contains(log[i])) return true;
		}
		return false;
	}

	
	@Override
	public ETreeNode decompose(String s) {
		if ((s.startsWith("(")) && (s.endsWith(")"))) s = s.substring(1, s.length()-1); else return null;
		// odobrali sme vonkajsie zatvorky, ak nenajdeme ziadnu logicku spojku, sme pri simplecase
		
		ETreeNode result = new ETreeNode();
		
		if (!s.contains("&&") && !s.contains("||") && !s.contains("!(") && !s.contains("AND") && !s.contains("OR") && !s.contains("NOT")) {
			result.value = s;
		} else if ((s.startsWith("!")) || (s.startsWith("NOT"))) {							// osetrenie negacie
			result.value = "!";
			if (s.startsWith("!")) result.children.add(decompose(new String(s.substring(1, s.length()))));
			else { s = s.replaceFirst("NOT", "").trim(); result.children.add(decompose(s));} 
		} else { 											// osetrenie binarnych operacii			
			String done = "";
			String operation = "";
			int counter = 0;			
			char c;
			
			for (int i=0; i<s.length(); i++) {
				c = s.charAt(i);				
				
				if (counter==0 && !operation.isEmpty() && c!='(') operation += c;
				
				if ((counter==0) && !done.isEmpty()) {
					result.children.add(decompose(new String(done)));
					done="";
					operation += c;
				}
			
				if (counter!=0) done += c;
			
				if (c==')') {counter--; }				
				if (c=='(') {
					if (counter==0) done = "(";
					counter++; 
					if (!operation.isEmpty()) { result.value = operation.trim(); operation = ""; } 		// ak nie som na zaciatku a teda mam aku operaciu riesit
				}
								
			}
			result.children.add(decompose(new String(done)));
		} 
		
		if (result.value == null) { result.value = s; }
		return result;
	}
		
	public Boolean getValueOf(ETreeNode n) {
		if (n.children.isEmpty()) return this.parseSimpleCase(n.value);
		else {
			switch (n.value) {
				case "&&": { 
					Boolean result = true; 
					for (ETreeNode child: n.children) if (result && this.getValueOf(child)) result = true; else result = false;
					return result;
				}
				case "AND": { 
					Boolean result = true; 
					for (ETreeNode child: n.children) if (result && this.getValueOf(child)) result = true; else result = false;
					return result;
				}

				case "||":  { 
					Boolean result = false; 
					for (ETreeNode child: n.children) if (result || this.getValueOf(child)) result = true; else result = false;
					return result;
				}
				case "OR":  { 
					Boolean result = false; 
					for (ETreeNode child: n.children) if (result || this.getValueOf(child)) result = true; else result = false;
					return result;
				}

				case "!":  { 					 
					for (ETreeNode child: n.children) return (!this.getValueOf(child));
				}
				case "NOT":  { 					 
					for (ETreeNode child: n.children) return (!this.getValueOf(child));
				}			

			}
		}
		return false;
	}
	
	public Boolean parseSimpleCase(String s) {		
		
	if (s.contains("=") || s.contains(">") || s.contains("<")) {
		String xLEQ[] = s.split("<=");				 		
		String xGEQ[] = s.split(">=");				
		String xNEQ[] = s.split("!=");
		String xNEQ2[] = s.split("<>");
		String xG[] = s.split(">");				
		String xL[] = s.split("<");										
		String xEQ[] = s.split("==");  // budeme akceptovat len dvojite rovnitko na porovnavanie, kvoli jednoznacnosti... pre priradenie budeme rozlisovat jazyk
		
		String operation = "";
		String left = "";
		String right = "";

		if (operation.isEmpty() && (xLEQ.length == 2)) { operation="<="; left = xLEQ[0]; right = xLEQ[1]; }
		if (operation.isEmpty() && (xGEQ.length == 2)) { operation=">="; left = xGEQ[0]; right = xGEQ[1]; }
		if (operation.isEmpty() && (xNEQ.length == 2)) { operation="!="; left = xNEQ[0]; right = xNEQ[1]; }
		if (operation.isEmpty() && (xNEQ2.length == 2)) { operation="<>"; left = xNEQ2[0]; right = xNEQ2[1]; }
		if (operation.isEmpty() && (xL.length == 2)) { operation="<"; left = xL[0]; right = xL[1]; }
		if (operation.isEmpty() && (xG.length == 2)) { operation=">"; left = xG[0]; right = xG[1]; }
		if (operation.isEmpty() && (xEQ.length == 2)) { operation="=="; left = xEQ[0]; right = xEQ[1]; }
		
		left = left.trim();
		right = right.trim();
				
		SimpleVariable leftVar = new SimpleVariable();
		SimpleVariable rightVar = new SimpleVariable();			
				
		if (StringProcess.hasOperator(left) || StringProcess.hasOperator(right)) {						// ak pracujeme so stringami, ide o stringove premenne
			StringProcess sp = new StringProcess(left, variableManager);
			leftVar = new StringVariable(sp.getStringValue(), variableManager); 
			leftVar.initialized = true;
			sp = new StringProcess(right, variableManager);
			rightVar = new StringVariable(sp.getStringValue(), variableManager);
			rightVar.initialized = true;
		} else {			
			right.replace("][", ":");
			left.replace("][", ":");
			
			String arrayV[] = right.split("[\\[\\]]");
			String arrayX[] = left.split("[\\[\\]]");
			boolean hasMathInLeft = false;
			boolean hasMathInRight = false;
			boolean hasMathOut = false;
								
			for (int i=0; i<arrayV.length; i=i+2) {
				if (arrayV[i].matches(".*[0-9].*") || MathTree.hasOperator(arrayV[i])) 
					if (i % 2 == 0) hasMathOut = true; else hasMathInRight = true;				
			}
				
			for (int i=0; i<arrayX.length; i=i+2) {
				if (arrayX[i].matches(".*[0-9].*") || MathTree.hasOperator(arrayX[i])) 
					if (i % 2 == 0) hasMathOut = true; else hasMathInLeft = true;				
			}
					
			if (hasMathOut) {
				MathTree mt = new MathTree(left, variableManager);
				leftVar = new IntegerVariable(mt.getTreeValue().intValue());
				leftVar.initialized = true;
				mt = new MathTree(right, variableManager);			 
				rightVar = new IntegerVariable(mt.getTreeValue().intValue());
				rightVar.initialized = true;
			} else {																	// takze obe strany su len "premenne", mozno referencovane matikou
				if (hasMathInLeft) {
					if (arrayX[1].contains(":")) {
						String arrayParts[] = arrayX[1].split(":");
						MathTree mt = new MathTree(arrayParts[0], variableManager);
						MathTree mt2 = new MathTree(arrayParts[1], variableManager);
						leftVar = variableManager.getTableVariableByName(arrayX[0]).getNth(mt.getTreeValue().intValue(), mt2.getTreeValue().intValue());
					} else {
						MathTree mt = new MathTree(arrayX[1], variableManager);			 
						leftVar = variableManager.getArrayVariableByName(arrayX[0]).getNth(mt.getTreeValue().intValue());
					}
				} else leftVar = variableManager.getVariableByName(left);
				if (hasMathInRight) {
					if (arrayV[1].contains(":")) {
						String arrayParts[] = arrayV[1].split(":");
						MathTree mt = new MathTree(arrayParts[0], variableManager);
						MathTree mt2 = new MathTree(arrayParts[1], variableManager);
						rightVar = variableManager.getTableVariableByName(arrayV[0]).getNth(mt.getTreeValue().intValue(), mt2.getTreeValue().intValue());
					} else {
						MathTree mt = new MathTree(arrayV[1], variableManager);			 
						rightVar = variableManager.getArrayVariableByName(arrayV[0]).getNth(mt.getTreeValue().intValue());
					}
				} else rightVar = variableManager.getVariableByName(right);
			}
				
		}
		if (leftVar.initialized && rightVar.initialized) return compareVariables(leftVar, rightVar, operation);
		else { variableManager.errorHandler.errorHappened("Initialized"); return false; }
		
		} else {
			SimpleVariable sim = variableManager.getVariableByName(s);
			if (!sim.initialized) {
				variableManager.errorHandler.errorHappened("Initialized"); return false;
			} else return sim.getValue(true);
		}
	}
	
	
	
	
	
	private Boolean compareVariables(SimpleVariable a, SimpleVariable b, String operation) {
		if (!a.getType().equals(b.getType())) return false;
		if (a.getType().equals("string")) {
			switch (operation) {
				case "==": return a.getValue("string").equals(b.getValue("string"));
				case "!=": return !(a.getValue("string").equals(b.getValue("string")));
				case "<>": return !(a.getValue("string").equals(b.getValue("string")));
			}
		}
		if (a.getType().equals("char")) {
			switch (operation) {
				case "==": return a.getValue('c') == b.getValue('c');
				case "!=": return !(a.getValue('c') == b.getValue('c'));
				case "<>": return !(a.getValue('c') == b.getValue('c'));
			}
		}

		if (a.getType().equals("boolean")) {			
			switch (operation) {
				case "==": return a.getValue(true) == b.getValue(true);
				case "!=": return a.getValue(true) != b.getValue(true);
				case "<>": return a.getValue(true) != b.getValue(true);
			}
		}		
		if (a.getType().equals("int")) {
			switch (operation) {
				case "<": return a.getValue(1) < b.getValue(1);
				case "<=": return a.getValue(1) <= b.getValue(1);
				case ">": return a.getValue(1) > b.getValue(1);
				case ">=": return a.getValue(1) >= b.getValue(1);
				case "!=": return a.getValue(1) != b.getValue(1);
				case "<>": return a.getValue(1) != b.getValue(1);
				case "==": return a.getValue(1) == b.getValue(1);
			}					
		}
		return false;
	}
	
}
