package org.academiadecodigo.enuminatti.fx.calculator;

/**
 * MIT License
 * (c) 2017 Ricardo Constantino
 */

public class CalculatorBrain {
    private final String ERROR_VALUE = "ERR";

    private Long firstOperator;
    private Long secondOperator;
    private Operation mathOperation;
    private Operation lastMathOperation;

    public CalculatorBrain() {
        reset();
    }

    public String getMathOperation() {
        return (mathOperation != null ? firstOperator + " " + mathOperation : null);
    }

    public String calculate(String numberOnScreen, Operation operation, boolean newNumber) {

        showDebugInfo("before", numberOnScreen);

        String result = doOperation(numberOnScreen, operation, newNumber);

        showDebugInfo("after", result);
        return result;

    }

    private String doOperation(String numberOnScreen, Operation operation, boolean newNumber) {
        String result = numberOnScreen;
        long currentNumber = !isDigit(numberOnScreen) ? 0L : Long.parseLong(numberOnScreen);
        switch (operation) {
            case RESET:
                reset();
                result = null;
                break;

            case EQUAL:
                if (!newNumber && lastMathOperation != null) {
                    // repeating previous operation
                    result = doMathOperation(currentNumber, secondOperator, lastMathOperation);
                    break;
                }

                if ((newNumber || secondOperator == null) && mathOperation != null) {
                    // showing result of operation
                    result = doMathOperation(firstOperator, currentNumber, mathOperation);
                    break;
                }

                // nothing/show same number
                break;

            default:
                if (firstOperator == null) {
                    // set first operator and operation
                    setMathOperation(operation);
                    firstOperator = currentNumber;
                    break;
                }

                if (newNumber && mathOperation != null) {
                    // got new number, show result of previous operation
                    result = doMathOperation(firstOperator, currentNumber, mathOperation);
                }

                // set new operation
                setMathOperation(operation);
        }
        return (result != null ? result : "0");
    }

    private String doMathOperation(Long firstOperator, Long secondOperator, Operation mathOperation) {
        if ((firstOperator == null ||
                secondOperator == null ||
                mathOperation == null)) {
            return null;
        }

        switch (mathOperation) {
            case ADD:
                firstOperator = add(firstOperator, secondOperator);
                break;
            case SUBTRACT:
                firstOperator = subtract(firstOperator, secondOperator);
                break;
            case MULTIPLY:
                firstOperator = multiply(firstOperator, secondOperator);
                break;
            case DIVIDE:
                if (secondOperator == 0) {
                    reset();
                    return ERROR_VALUE;
                }
                firstOperator = divide(firstOperator, secondOperator);
                break;
        }

        setMemory(firstOperator, secondOperator, mathOperation);

        this.mathOperation = null;
        return firstOperator.toString();
    }

    private void setMemory(Long firstOperator, Long secondOperator, Operation mathOperation) {
        this.firstOperator = firstOperator;
        this.secondOperator = secondOperator;
        this.lastMathOperation = mathOperation;
    }

    private void reset() {
        firstOperator = null;
        mathOperation = null;
        secondOperator = null;
        lastMathOperation = null;
    }

    private void showDebugInfo(String title, String numberOnScreen) {
//        System.out.printf("%s: firstoperator: %d, screen: %s, ", title, firstOperator, numberOnScreen);
//        System.out.printf("mathOperation: %s, lastoperation: %s, secondoperator: %d\n",
//                mathOperation, lastOperation, secondOperator);
    }

    private void setMathOperation(Operation operation) {
        if (!operation.isMathOperation()) {
            return;
        }
        mathOperation = operation;
        secondOperator = null;
        lastMathOperation = null;
    }

    private static long divide(long first, long second) {
        return first / second;
    }

    private static long multiply(long first, long second) {
        return first * second;
    }

    private static long subtract(long first, long second) {
        return first - second;
    }

    private static long add(long first, long second) {
        return first + second;
    }

    public boolean isDigit(String buttonId) {
        return buttonId != null && buttonId.matches("[\\d.]+");
    }

}
