(function (root, factory) {
    define([], factory);
} (this, function () {
    "use strict";

    var noopTimer = {
        start: function () {},
        elapsed: function () { return 0; }
    };

    function ConsoleReporter (options) {
        var timer = noopTimer,
            specCount,
            failureCount,
            failedSpecs = [],
            pendingCount,
            ansi = {
                green: '\x1B[32m',
                red: '\x1B[31m',
                yellow: '\x1B[33m',
                none: '\x1B[0m'
            },
            failedSuites = [];

        var print = function print (message) {
            console.log(message + '\x03\b');
        }

        this.jasmineStarted = function () {
            specCount = 0;
            failureCount = 0;
            pendingCount = 0;
            print('Started');
            printNewline();
            timer.start();
        };

        this.jasmineDone = function () {
            print("jasmineDone");
            printNewline();
            for (var i = 0; i < failedSpecs.length; i++) {
                specFailureDetails(failedSpecs[i]);
            }

            if(specCount > 0) {
                printNewline();
                var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
                        failureCount + ' ' + plural('failure', failureCount);
                if (pendingCount) {
                    specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
                }
                print(specCounts);
            } else {
                print('No specs found');
            }

            printNewline();
            var seconds = timer.elapsed() / 1000;
            print('Finished in ' + seconds + ' ' + plural('second', seconds));
            printNewline();
            for (i = 0; i < failedSuites.length; i++) {
                suiteFailureDetails(failedSuites[i]);
            }
            var exitCode = failureCount === 0 ? 0 : 1;
            console.info('All tests completed!' + exitCode);
        };

        this.specDone = function (result) {
            specCount++;
            if (result.status == 'pending') {
                pendingCount++;
                print(colored('yellow', '*'));
                return;
            }
            if (result.status == 'passed') {
                print(colored('green', '.'));
                return;
            }
            if (result.status == 'failed') {
                failureCount++;
                failedSpecs.push(result);
                print(colored('red', 'F'));
            }
        };

        this.suiteDone = function (result) {
            if (result.failedExpectations && result.failedExpectations.length > 0) {
                failureCount++;
                failedSuites.push(result);
            }
        };
        return this;

        function printNewline() {
            print('\n');
        }

        function colored (color, str) {
            return ansi[color] + str + ansi.none;
        }

        function plural (str, count) {
            return count == 1 ? str : str + 's';
        }

        function repeat (thing, times) {
            var arr = [];
            for (var i = 0; i < times; i++) {
                arr.push(thing);
            }
            return arr;
        }

        function indent (str, spaces) {
            var lines = (str || '').split('\n');
            var newArr = [];
            for (var i = 0; i < lines.length; i++) {
                newArr.push(repeat(' ', spaces).join('') + lines[i]);
            }
            return newArr.join('\n');
        }

        function specFailureDetails (result) {
            printNewline();
            print(result.fullName);
            for (var i = 0; i < result.failedExpectations.length; i++) {
                var failedExpectation = result.failedExpectations[i];
                printNewline();
                print(indent(failedExpectation.message, 2));
                print(indent(failedExpectation.stack, 2));
            }
            printNewline();
        }

        function suiteFailureDetails (result) {
            for (var i = 0; i < result.failedExpectations.length; i++) {
                printNewline();
                print(colored('red', 'An error was thrown in an afterAll'));
                printNewline();
                print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
            }
            printNewline();
        }
    }
    return ConsoleReporter;
}));
