(function() {
    if (! jasmine) {
        throw new Exception("jasmine library does not exist in global namespace!");
    }

    /**
     * Basic reporter that outputs spec results to for the Teamcity build system
     *
     * Usage:
     *
     * jasmine.getEnv().addReporter(new jasmine.TeamcityReporter());
     * jasmine.getEnv().execute();
     */
    var TeamcityReporter = function() {
        this.started = false;
        this.finished = false;
    };

    TeamcityReporter.prototype = {
        reportRunnerResults: function(runner) {
            this.log("##teamcity[progressFinish 'Running Jasmine Tests']");
        },

        reportRunnerStarting: function(runner) {
            this.log("##teamcity[progressStart 'Running Jasmine Tests']");
        },

        reportSpecResults: function(spec) { },

        reportSpecStarting: function(spec) { },

        reportSuiteResults: function(suite) {
            var results = suite.results();
            var path = [];
            while(suite) {
                path.unshift(suite.description);
                suite = suite.parentSuite;
            }
            var description = path.join(' ');

            this.log("##teamcity[testSuiteStarted name='" + this.escapeTeamcityString(description) + "']");

            var outerThis = this;
            var eachSpecFn = function(spec){
                if (spec.description) {
                    outerThis.log("##teamcity[testStarted name='" + outerThis.escapeTeamcityString(spec.description) + "' captureStandardOutput='true']");
                    var specResultFn = function(result){
                        if (!result.passed_) {
                            outerThis.log("##teamcity[testFailed name='" + outerThis.escapeTeamcityString(spec.description) + "' message='|[FAILED|]' details='" + outerThis.escapeTeamcityString(result.trace.stack) + "']");
                        }
                    };

                    for (var j = 0, jlen = spec.items_.length; j < jlen; j++) {
                        specResultFn(spec.items_[j]);
                    }
                    outerThis.log("##teamcity[testFinished name='" + outerThis.escapeTeamcityString(spec.description) + "']");
                }
            };
            for (var i = 0, ilen = results.items_.length; i < ilen; i++) {
                eachSpecFn(results.items_[i]);
            }



            this.log("##teamcity[testSuiteFinished name='" + outerThis.escapeTeamcityString(description) + "']");
        },

        log: function(str) {
            var console = jasmine.getGlobal().console;
            if (console && console.log) {
                console.log(str);
            }
        },

        hasGroupedConsole: function() {
            var console = jasmine.getGlobal().console;
            return console && console.info && console.warn && console.group && console.groupEnd && console.groupCollapsed;
        },

        escapeTeamcityString: function(message) {
            if(!message) {
                return "";
            }

            return message.replace(/\|/g, "||")
                          .replace(/\'/g, "|'")
                          .replace(/\n/g, "|n")
                          .replace(/\r/g, "|r")
                          .replace(/\u0085/g, "|x")
                          .replace(/\u2028/g, "|l")
                          .replace(/\u2029/g, "|p")
                          .replace(/\[/g, "|[")
                          .replace(/]/g, "|]");
        }
    };

    function suiteResults(suite) {
        console.group(suite.description);
        var specs = suite.specs();
        for (var i in specs) {
            if (specs.hasOwnProperty(i)) {
                specResults(specs[i]);
            }
        }
        var suites = suite.suites();
        for (var j in suites) {
            if (suites.hasOwnProperty(j)) {
                suiteResults(suites[j]);
            }
        }
        console.groupEnd();
    }

    function specResults(spec) {
        var results = spec.results();
        if (results.passed() && console.groupCollapsed) {
            console.groupCollapsed(spec.description);
        } else {
            console.group(spec.description);
        }
        var items = results.getItems();
        for (var k in items) {
            if (items.hasOwnProperty(k)) {
                itemResults(items[k]);
            }
        }
        console.groupEnd();
    }

    function itemResults(item) {
        if (item.passed && !item.passed()) {
            console.warn({actual:item.actual,expected: item.expected});
            item.trace.message = item.matcherName;
            console.error(item.trace);
        } else {
            console.info('Passed');
        }
    }

    // export public
    jasmine.TeamcityReporter = TeamcityReporter;
})();

