ExpressionEngine = {
    evalExpression: function (expression) {
        expression = ExpressionEngine.replaceVariablesWithValue(expression);
        var curStartIndex, startBrk, endBrk, functionName, exp;
        functionName = "";
        do {
            curStartIndex = 0;
            startBrk = expression.indexOf("(");
            if (startBrk == -1) {
                return expression;
            }
            var i = startBrk + 1;
            do {
                i = expression.indexOf("(", i);
                if (i >= 0) {
                    curStartIndex = startBrk + 1;
                    startBrk = i;
                    i = i + 1;
                }
            }
            while (i >= 0);
            endBrk = expression.indexOf(")", startBrk);
            if (endBrk == -1) {
                displayError("Invalid expression:\n" + expression + "\nThe expression seems to be missing a closing bracket");
                return 0;
            }
            functionName = Ext.String.trim(expression.substring(curStartIndex, startBrk));
            exp = expression.substring(curStartIndex, endBrk + 1);
            if (functionName != "" || exp != "") {
                var chunk;
                if (functionName == "") {
                    chunk = eval(exp);
                }
                else {
                    chunk = ExpressionEngine.replaceAndEvalFunc(expression.substring(curStartIndex, endBrk + 1));
                }
                expression = expression.substring(0, curStartIndex) + chunk + expression.substring(endBrk + 1);
            }
        }
        while (functionName != "" || exp != "");
        return expression;
    },
    replaceAndEvalFunc: function (chunk) {
        var regex = /DATA\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                vName = vName.toUpperCase();
                if (Author.VariableManager.hasConstant(vName)) {
                    return Author.VariableManager.getConstant(vName);
                }
                else {
                    return "$(" + vName + ")";
                }
            });
            return chunk;
        }
        // String Functions
        regex = /CHR\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                if (!isNaN(vName)) {
                    return (String.fromCharCode(parseInt(vName)));
                }
                return vName;
            });
            return chunk;
        }
        regex = /UCASE\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                return vName.toUpperCase();
            });
            return chunk;
        }
        regex = /LCASE\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                return vName.toLowerCase();
            });
            return chunk;
        }
        regex = /TRIM\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                return  Ext.String.trim(vName);
            });
            return chunk;
        }
        regex = /LEN\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                return vName.length;
            });
            return chunk;
        }
        regex = /LEFT\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var params = vName.split(",");
                return params[0].substring(0, parseInt(params[1]));
            });
            return chunk;
        }
        regex = /RIGHT\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var params = vName.split(",");
                return params[0].substring(vName.length - parseInt(params[1]));
            });
            return chunk;
        }
        regex = /INSTR\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var params = vName.split(",");
                if (params.length == 2)
                    return params[0].indexOf(params[1]);
                else if (params.length >= 3)
                    return params[1].indexOf(params[2], parseInt(params[0]));
            });
            return chunk;
        }
        regex = /MID\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var params = vName.split(",");
                if (params.length == 2)
                    return params[0].substring(parseInt(params[1]) - 1);
                else if (params.length == 3)
                    return params[0].substring(parseInt(params[1]) - 1, parseInt(params[1]) + parseInt(params[2]) - 1);
            });
            return chunk;
        }
        // Numeric Functions
        regex = /RND\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                if (vName.indexOf(',') >= 0) {
                    var params = vName.split(",");
                    if (params.length == 2) {
                        var end = parseInt(params[1]);
                        var start = parseInt(params[0]);
                        return parseInt(Math.random() * (end - start + 1)) + start;
                    }
                    else {
                        Author.log('RND() can only accept 1 or 2 parameters.');
                        return 0;
                    }
                } else {
                    return parseInt(Math.random() * parseInt(vName));
                }
            });
            return chunk;
        }
        regex = /INT\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                return parseInt(String(eval(vName)));
            });
            return chunk;
        }
        regex = /SECONDS_TO_TIME\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var sec_num = parseInt(String(eval(vName))),
                    hours = Math.floor(sec_num / 3600),
                    minutes = Math.floor((sec_num - (hours * 3600)) / 60),
                    seconds = sec_num - (hours * 3600) - (minutes * 60);

                if (hours < 10) {
                    hours = hours == 0 ? '' : ( "0" + hours);
                }
                if (minutes < 10) {
                    minutes = "0" + minutes;
                }
                if (seconds < 10) {
                    seconds = "0" + seconds;
                }
                var time = hours + hours ? ':' : '' + minutes + ':' + seconds;

                return time;
            });
            return chunk;
        }
        regex = /ROUND\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                if (vName.indexOf(',') >= 0) {
                    var params = vName.split(",");
                    if (params.length == 2) {
                        var dec = Math.pow(10, parseInt(params[1]));
                        var number = parseFloat(params[0]);
                        return Math.round(number * dec) / dec;
                    }
                    else {
                        Author.log('Round(number, [decimal]) can only accept 1 or 2 parameters.');
                        return 0;
                    }
                } else {
                    return Math.round(parseFloat(String(eval(vName))));
                }
            });
            return chunk;
        }
        regex = /SUM\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var values = vName.split(",");
                var sum = 0;
                for (var i = 0; i < values.length; i++) {
                    if (!isNaN(values[i])) {
                        sum += parseFloat(values[i]);
                    }
                }
                return sum;
            });
            return chunk;
        }
        regex = /MAX\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var values = vName.split(",");
                var max = -99999999999;
                for (var i = 0; i < values.length; i++) {
                    if (!isNaN(values[i])) {
                        var temp = parseFloat(values[i]);
                        if (temp > max) {
                            max = temp;
                        }
                    }
                }
                return max;
            });
            return chunk;
        }
        regex = /MIN\((.*)\)/gi;
        if (regex.test(chunk)) {
            chunk = chunk.replace(regex, function (name, vName) {
                var values = vName.split(",");
                var min = 99999999999;
                for (var i = 0; i < values.length; i++) {
                    if (!isNaN(values[i])) {
                        var temp = parseFloat(values[i]);
                        if (temp < min) {
                            min = temp;
                        }
                    }
                }
                return min;
            });
            return chunk;
        }
    },
    /**
     * Replace the variables in the passed in expression with their values.
     * [vid] [vname] [actionid] [actionname]
     * @param {Object} expression
     */
    replaceVariablesWithValue: function (expression) {
        //replace action value by action ID;
        var regex = /\[\#(\d+)\]/gi;
        expression = expression.replace(regex, function (name, vName) {
            var actionID = vName.match(/(\d+)/gi)[0];
            var actionValue = Author.VariableManager.getActionValueByActionID(actionID);
            return actionValue == null ? name : actionValue;
        });
        //replace variable value by variable ID;
        regex = /\[(\d+)\]/gi;
        expression = expression.replace(regex, function (name, vName) {
            var variableID = vName.match(/(\d+)/gi)[0];
            var value = Author.VariableManager.getValueByVariableID(variableID);
            return value == null ? 'undefined' : value;
        });
        //replace variable value by names: actionName/variableName
        regex = /\[([%|&|\w|\s]+)\]/gi;
        do {
            var after = expression.replace(regex, function (name, vName) {
                //alert("name:"+name+"\nName:"+vName);
                //
                try {
                    var thisVar = Author.VariableManager.getVariableByName(vName);
                    if (thisVar == null) {
                        //try get actionName

                        var replaced = Author.VariableManager.getActionValueByActionName(vName);
                        Author.log('Try to search for action with name: ' + vName + ", value: " + replaced);
                        if (replaced == null) {
                            return "";
                        }
                        return replaced;
                    }

                    if (thisVar != null) {
                        var replaced = thisVar.getValue();
                        if (thisVar.Type == AuthorVariableTypes.Numeric) {
                            replaced = eval(replaced);
                        }
                        return replaced;
                    }
                    return "";
                }
                catch (err) {
                    Author.log("Error in replaceVariableNameWithValue:" + name + ", " + vName);
                    return "";
                }
            });
            var doAgain = (after != expression) && (after.search(regex) >= 0);
            // alert("expression: "+expression+"\nAfter: "+after+"\nafter != expression :"+(condition));
            if (doAgain) {
                expression = after;
            }
        }
        while (doAgain);
        return after;
    }
};
