/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *  
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *  
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 * 
 */

define(function (require, exports) {
    "use strict";

    var _ = require("lodash");

    var log = require("js/util/log");

    /**
     * A map from namespaces to sets of action times.
     * 
     * @private
     * @type {{string: {string: {idle: number, executing: number}}}}
     */
    var _actionTimes = {};

    /**
     * Records a single action time event.
     * 
     * @param {string} namespace
     * @param {string} action
     * @param {number} enqueueTime
     * @param {number} startTime
     * @param {number} finishTime
     */
    var recordAction = function (namespace, action, enqueueTime, startTime, finishTime) {
        if (!_actionTimes.hasOwnProperty(namespace)) {
            _actionTimes[namespace] = {};
        }

        var times = _actionTimes[namespace];

        if (!times.hasOwnProperty(action)) {
            times[action] = [];
        }

        times[action].push({
            idle: startTime - enqueueTime,
            executing: finishTime - startTime
        });
    };

    /**
     * Log a summary of the given action times to the console.
     * 
     * @private
     * @param {string} prop Either "idle" or "executing"
     */
    var _printActionTimes = function (prop) {
        var allTimes = Object.keys(_actionTimes)
            .reduce(function (allActions, moduleName) {
                var times = _actionTimes[moduleName];
                Object.keys(times).forEach(function (actionName) {
                    allActions[moduleName + "." + actionName] = times[actionName];
                });
                return allActions;
            }, {});

        var sum = function (a, b) {
            return a + b;
        };

        var timeTable = Object.keys(allTimes).map(function (action) {
            var times = allTimes[action],
                count = times.length,
                values = _.pluck(times, prop).sort(),
                max = _.max(values),
                med = values[Math.floor(values.length / 2)],
                min = _.min(values),
                total = _.reduce(values, sum),
                avg = total / count;

            return {
                action: action,
                count: count,
                total: total,
                max: max,
                med: med,
                avg: avg,
                min: min
            };
        });

        log.table(timeTable);
        log.debug("Total: %d actions in %dms", _.size(timeTable),
            _.chain(timeTable).pluck("total").reduce(sum).value());
    };

    /**
     * Log a summary of action idle times to the console.
     */
    var printIdleTimes = function () {
        _printActionTimes("idle");
    };

    /**
     * Log a summary of action execution times to the console.
     */
    var printExecTimes = function () {
        _printActionTimes("executing");
    };

    // TODO: occasionally save these records in localStorage, and restore them
    // in memory from localStorage on startup.

    exports.recordAction = recordAction;
    exports.printIdleTimes = printIdleTimes;
    exports.printExecTimes = printExecTimes;
    exports._actionTimes = _actionTimes;
});
