"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("../common");
const ast_1 = require("../ast");
const toCodeString_1 = __importDefault(require("../toCodeString"));
const reassociateLeft = {
    "|": true,
    "&": true,
    "||": true,
    "&&": true,
    "+": true,
    "*": true,
};
const reflectOperators = {
    "<": ">",
    ">": "<",
    ">=": "<=",
    "<=": ">=",
    "==": "==",
    "!=": "!=",
};
function shouldSwapOrder(left, right) {
    if (ast_1.Literal.is(left) && !ast_1.Literal.is(right)) {
        return true;
    }
    return toCodeString_1.default(left).localeCompare(toCodeString_1.default(right)) > 0;
}
const normalize = common_1.memoize(function (e) {
    if (ast_1.BinaryExpression.is(e)) {
        let left = normalize(e.left);
        let right = normalize(e.right);
        let operator = e.operator;
        if (reassociateLeft[e.operator]) {
            if (ast_1.BinaryExpression.is(right) && right.operator === e.operator) {
                left = new ast_1.BinaryExpression({
                    location: right.location,
                    left: left,
                    operator: e.operator,
                    right: right.left
                }),
                    right = right.right;
            }
        }
        // literal values should always be on the right.
        let canSwap = reflectOperators[operator] != null;
        if (canSwap && shouldSwapOrder(left, right)) {
            [left, right] = [right, left];
            operator = reflectOperators[operator];
        }
        if (left !== e.left || right !== e.right || operator !== e.operator) {
            e = e.patch({ left, operator, right });
        }
    }
    return e;
}, true);
exports.default = normalize;
