define(['bluebird.min', 'tnc/tnc-macro'], function () {
    var Promise = require('bluebird.min');
    var Macro = require('tnc/tnc-macro');

    var Macros = {};
    Macros.registeredMacros = {};
    Macros.data = {};

    Macros.registerMacro = function (macro) {
        if (!( macro instanceof Macro) ){
            throw "Registered macro should be an instance of the Macro class.";
        }
        var self = this;
        self.registeredMacros[macro.name.toLowerCase()] = macro;
    };

    Macro.escapeRegExp = function(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    };

    Macros.resolve = function (string) {
        var pattern = /{%([^%]*)%}/g; // Get non-nested macro's from string

        return string.replace(pattern, function (macroText) {

            // remove the {% %}
            var matchObject = pattern.exec(string);
            var match = matchObject[1];

            // get the macroname
            var rawMacroNameMatch = /[^(]*/.exec(match);
            var rawMacroName = !rawMacroNameMatch[1] ? rawMacroNameMatch[0] : rawMacroNameMatch[1];
            rawMacroName = rawMacroName.toLowerCase();

            // get the macro parameters
            var rawValueMatches = /\((.*)\)/.exec(match);
            var rawValueMatch = rawValueMatches && rawValueMatches.length > 1 ? rawValueMatches[1] : "";
            var rawValue = rawValueMatch;

            // macorparameters from string to objects
            if (rawValueMatch instanceof Array) {
                rawValue = !rawValueMatch[1] ? rawValueMatch[0] : rawValueMatch[1];
            }
            var rawParams = "[" + rawValue.replace(" ", "") + "]";
            var params = JSON.parse(rawParams);

            // get the marcro result
            var result = "";
            if (Macros.registeredMacros[rawMacroName]) {
                result = Macros.registeredMacros[rawMacroName].resolve(params);
            }
            else {
                throw "The macro with the name '" + rawMacroName + "' has not been registered/does not exsist.";
            }

            return result;

        });
    };

    var Documentation = function(options) {
            this.description = options.description;
            this.syntax = options.syntax;
            this.example = options.example;
            this.canRunOutsideKnockout = options.canRunOutsideKnockout;
    };

    // New Macro
    Macros.registerMacro(new Macro("Pages.count", function (args) {
        var items = Macros.data.items;
        var amountOnPage = Macros.data.widget.amountOnPage;
        return Math.ceil(items.length / amountOnPage);
    }, new Documentation({ // Documentation
            description: "Geeft het totaal aantal pagina's terug dat in de widget wordt afgespeeld. Totaal aantal pagina's = totaal aantal items / aantal items per pagina.",
            syntax: "<span class=\"highlight\">{% Pages.count %}</span> of <span class=\"highlight\">doMacro(\"Pages.count\")</span>",
            example: "<span ng-bind=\"text: {% Pages.count %}\"></span>\r\n<span ng-bind=\"text: $root.doMacro('Pages.count')\"></span>",
            canRunOutsideKnockout: true
        })
    ));

    // New Macro
    Macros.registerMacro(new Macro("Pages.current", function (args) {
        var index = Macros.data.runtime.currentPageIndex || 0;
        return index + 1;
    },  new Documentation({ // Documentation
            description: "Kan alleen in een Knockout transformatie met doMacro() worden gebruikt. Buiten een Knockouttransformatie werkt deze Macro niet.",
            syntax: "<span class=\"highlight\">{% Pages.current %}</span> of <span class=\"highlight\">doMacro(\"Pages.current\")</span>",
            example: "<span ng-bind=\"text: {% Pages.current %}\"></span>\r\n<span ng-bind=\"text: $root.doMacro('Pages.current')\"></span>",
            canRunOutsideKnockout: true
        })
    ));

    return Macros;
});
