/*
* ADOBE CONFIDENTIAL
*
* Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*/

"use strict";

var _ = require("lodash");

var LOGLEVEL_INFO       = 3,
    LOGLEVEL_WARNING    = 2,
    LOGLEVEL_ERROR      = 1,
    LOGLEVEL_NONE       = 0;
var loggingLevel        = LOGLEVEL_WARNING;

/**
 * Verifies that condition is true and logs an error if it's not.
 *
 * @param {boolean} condition Condition to check
 * @param Remaining arguments are passed to the logging function
 */
function assert(condition) {
    if (!condition) {
        console.error.apply(console, _.slice(arguments, 1));
        return false;
    }
    return true;
}

/**
 * Conditionally logs a message to the console
 */
function logMessage(msg, level) {
    if (level === undefined) {
        level = LOGLEVEL_INFO;
    }
    if (level <= loggingLevel) {
        var msgString = "preview.generate] " + msg;
        if (level === LOGLEVEL_ERROR) {
            console.error("[error:" + msgString);
        } else if (level === LOGLEVEL_WARNING) {
            console.warn("[warning:" + msgString);
        } else {
            console.log("[info:" + msgString);
        }
    }
}

var timerID = 0,
    timerData = {},
    enabled = false;

/**
 * Start a timer to track performance.
 */
function timerStart(name) {
    if (!enabled) {
        return timerID;
    }
    timerData[timerID] = {
        name: name,
        id: timerID,
        start: process.hrtime()
    };
    return timerID++;
}

/**
 * Records the end of a timed event.
 */
function timerEnd(id) {
    if (!enabled) {
        return;
    }
    var data = timerData[id];
    if (data) {
        if (data.timeTaken) {
            logMessage("End time already recorded for " + data, LOGLEVEL_ERROR);
            return;
        }
        data.timeTaken = process.hrtime(data.start);
    }
}

/**
 * Straight comparison function
 */
function numberCompare(a, b) {
    if (a < b) {
        return -1;
    } else if (a > b) {
        return 1;
    }
    return 0;
}

/**
 * Compares node's high res timers (two element array: seconds, nanoseconds)
 */
function hrTimeCompare(a, b) {
    var secondsCompare = numberCompare(a[0], b[0]);
    if (secondsCompare === 0) {
        return numberCompare(a[1], b[1]);
    }
    return secondsCompare;
}

var ONE_BILLION = 1000000000;

/**
 * Logs a report of the timers that have completed.
 * Clears the completed data after display.
 */
function timerReport(deleteDisplayed) {
    var byName = _.groupBy(_.values(timerData), "name");
    _.each(byName, function (listForName, name) {
        listForName.sort(hrTimeCompare);
        var totalSeconds = 0,
            totalNanoseconds = 0,
            count = 0;
        listForName.forEach(function (data) {
            if (data.timeTaken) {
                totalSeconds += data.timeTaken[0];
                totalNanoseconds += data.timeTaken[1];
                count++;
            }
        });

        if (count === 0) {
            return;
        }
        var overflow = Math.floor(totalNanoseconds / ONE_BILLION);
        totalSeconds += overflow;
        totalNanoseconds = totalNanoseconds % ONE_BILLION;
        logMessage(name);
        logMessage("---------");
        logMessage("Events: " + count);

        var ms = Math.round(totalNanoseconds / 1000000);
        logMessage("Total time: " + totalSeconds + "s " + ms + "ms");
        var average = (totalSeconds * 1000 + ms) / count;
        logMessage("Average: " + average + "ms");
        logMessage("\n");
    });

    if (deleteDisplayed) {
        Object.keys(timerData).forEach(function (id) {
            if (timerData[id].timeTaken) {
                delete timerData[id];
            }
        });
    }
}

/**
 * Log the timer results to the console periodically.
 */
function logTimersPeriodically(seconds) {
    setInterval(timerReport, seconds * 1000);
}

function init(setEnabled) {
    if (setEnabled) {
        enabled = true;
        logTimersPeriodically(10);
    }
}

exports.LOGLEVEL_INFO           = LOGLEVEL_INFO;
exports.LOGLEVEL_ERROR          = LOGLEVEL_ERROR;
exports.LOGLEVEL_WARNING        = LOGLEVEL_WARNING;
exports.LOGLEVEL_NONE           = LOGLEVEL_NONE;
exports.assert                  = assert;
exports.timerStart              = timerStart;
exports.timerEnd                = timerEnd;
exports.timerReport             = timerReport;
exports.logTimersPeriodically   = logTimersPeriodically;
exports.logMessage              = logMessage;
exports.init                    = init;
exports.loggingLevel            = loggingLevel;
