/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.csu.sdmx.validation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author themi
 */
public class ExpressionConverter {
    // Associativity constants for operators
	private static final int LEFT_ASSOC = 0;
	private static final int RIGHT_ASSOC = 1;

	// Supported operators
	private static final Map<String, int[]> OPERATORS = new HashMap<String, int[]>();
	static {
		// Map<"token", []{precendence, associativity}>
		OPERATORS.put("+", new int[] { 0, LEFT_ASSOC });
		OPERATORS.put("-", new int[] { 0, LEFT_ASSOC });
		OPERATORS.put("*", new int[] { 5, LEFT_ASSOC });
		OPERATORS.put("/", new int[] { 5, LEFT_ASSOC });
		OPERATORS.put("%", new int[] { 5, LEFT_ASSOC });
		OPERATORS.put("^", new int[] { 10, RIGHT_ASSOC });
	}
        
	private static boolean isOperator(String token) {
            return OPERATORS.containsKey(token);
	}
        
	private static boolean isAssociative(String token, int type) {
            return OPERATORS.get(token)[1] == type;
	}

	private static int cmpPrecedence(String token1, String token2) {
		return OPERATORS.get(token1)[0] - OPERATORS.get(token2)[0];
	}

	public static Stack<String> infixToRPN(String infixExpression) {
		Stack<String> out = new Stack<>();
		Stack<String> stack = new Stack<>();
                //remove whitespaces
		infixExpression = infixExpression.replaceAll("\\s", "");
		//find infix expressions
                Matcher tokensMatcher = Pattern.compile("[0-9.]+|[+\\-/*^%]").matcher(infixExpression);
                List<String> tokens = new ArrayList<>();
                while(tokensMatcher.find())
                    tokens.add(tokensMatcher.group());
                
		for (String token : tokens) {
                    //if token is an operator
                    if (isOperator(token)) {
                        while (!stack.empty() && isOperator(stack.peek())) {
                            if ((isAssociative(token, LEFT_ASSOC) 
                                && cmpPrecedence(token, stack.peek()) <= 0)
                                || (isAssociative(token, RIGHT_ASSOC) 
                                && cmpPrecedence(token, stack.peek()) < 0)) {
                                out.add(stack.pop()); 
                                continue;
                            }
                            break;
                        }
                        //push the new operator on stack 
                        stack.push(token);
                    } else if (token.equals("(")) {
                        stack.push(token); 	
                    } else if (token.equals(")")) {
                        //Put all tokens to out stack until next token is '{'
                        while (!stack.empty() && !stack.peek().equals("(")) 
                                out.add(stack.pop()); 
                        stack.pop(); 
                    } else {
                        out.add(token); 
                    }
		}
                //get rid of remaining tokens
		while (!stack.empty()) 
                    out.add(stack.pop()); 
		
                return out;
	}

}
