package cn.iocoder.yudao.framework.common.thealgorithms.stacks;

import java.util.Stack;

/**
 * Postfix to Infix implementation via Stack
 *
 * Function: String getPostfixToInfix(String postfix)
 * Returns the Infix Expression for the given postfix parameter.
 *
 * Avoid using parentheses/brackets/braces for the postfix string.
 * Postfix Expressions don't require these.
 *
 *
 * @author nikslyon19 (Nikhil Bisht)
 *
 */

public final class PostfixToInfix {
    private PostfixToInfix() {
    }

    public static boolean isOperator(char token) {
        switch (token) {
        case '+':
        case '-':
        case '/':
        case '*':
        case '^':
            return true;
        default:
            return false;
        }
    }

    public static boolean isValidPostfixExpression(String postfix) {
        /* Postfix expression length should NOT be less than 3 */
        if (postfix.length() < 3) {
            return false;
        }

        /* First two characters should NOT be operators */
        if (isOperator(postfix.charAt(0))) {
            return false;
        }
        if (isOperator(postfix.charAt(1))) {
            return false;
        }

        int operandCount = 0;
        int operatorCount = 0;

        /* Traverse the postfix string to check if --> Number of operands = Number of operators + 1
         */
        for (int i = 0; i < postfix.length(); i++) {
            char token = postfix.charAt(i);

            if (isOperator(token)) {
                operatorCount++;
                if (operatorCount >= operandCount) {
                    return false;
                }
            } else {
                if (operatorCount == 0) {
                    operandCount++;
                    continue;
                }

                if (operandCount != operatorCount + 1) {
                    return false;
                }

                /* Operand count is set to 2 because:-
                 *
                 * 1) the previous set of operands & operators combined have become a single valid
                 * expression, which could be considered/assigned as a single operand.
                 *
                 * 2) the operand in the current iteration.
                 */
                operandCount = 2;

                /* Reset operator count */
                operatorCount = 0;
            }
        }

        return (operandCount == operatorCount + 1);
    }

    public static String getPostfixToInfix(String postfix) {
        String infix = "";

        if (postfix.isEmpty()) {
            return infix;
        }

        /* Validate Postfix expression before proceeding with the Infix conversion */
        if (!isValidPostfixExpression(postfix)) {
            throw new IllegalArgumentException("Invalid Postfix Expression");
        }

        Stack<String> stack = new Stack<>();
        StringBuilder valueString = new StringBuilder();

        String operandA;
        String operandB;
        char operator;

        for (int index = 0; index < postfix.length(); index++) {
            char token = postfix.charAt(index);

            if (!isOperator(token)) {
                stack.push(Character.toString(token));
                continue;
            }

            operator = token;
            operandB = stack.pop();
            operandA = stack.pop();

            valueString.append('(');

            valueString.append(operandA);
            valueString.append(operator);
            valueString.append(operandB);

            valueString.append(')');

            stack.push(valueString.toString());
            valueString.setLength(0);
        }

        infix = stack.pop();
        return infix;
    }

    public static void main(String[] args) {
        assert getPostfixToInfix("ABC+/").equals("(A/(B+C))");
        assert getPostfixToInfix("AB+CD+*").equals("((A+B)*(C+D))");
        assert getPostfixToInfix("AB+C+D+").equals("(((A+B)+C)+D)");
        assert getPostfixToInfix("ABCDE^*/-").equals("(A-(B/(C*(D^E))))");
        assert getPostfixToInfix("AB+CD^/E*FGH+-^").equals("((((A+B)/(C^D))*E)^(F-(G+H)))");
    }
}
