var Logger =
{
    DEBUG: 0,
    INFO:  1,
    WARN:  2,
    ERROR: 3,
    FATAL: 4,
    OFF:   5,
    levelNames: ["DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF"],
    out:   null,
    level: 2,

    initialize: function(out, level, showControls)
    {
        this.out = $(out);
        // Default level is WARN
        this.level = level;

        // We don't need to do anything else if the Logger is set to OFF.
        if (level == this.OFF)
        {
            return;
        }

        // Initialise the output area if it does not exist
        if (this.out == null)
        {
            var div = document.createElement("DIV");
            div.id = out;
            document.body.appendChild(div);
            this.out = div;
            this.debug("Created logging area");
        }

        // Bind window.onerror event handler
        window.onerror = this._logWindowError.bind(this);

        if (showControls)
        {
            // Create form elements
            var logForm = document.createElement("FORM");
            logForm.name = "loggingForm";
            logForm.id = "loggingForm";
            var levelLabel = document.createElement("LABEL");
            levelLabel.setAttribute("for", "level");
            levelLabel.appendChild(document.createTextNode("Level:"));
            var levelSelect = document.createElement("SELECT");
            levelSelect.name = "level";
            levelSelect.id = "level";
            var options = [];
            for (var i = 0, optionName; optionName = this.levelNames[i]; i++)
            {
                options.push({name: optionName, value: i});
            }
            this._populateSelect(levelSelect, options);
            levelSelect.selectedIndex = this.level;
            var levelButton = document.createElement("INPUT");
            levelButton.type = "button";
            levelButton.value = "Set level";
            levelButton.onclick =  function()
            {
                var level = this.form.elements["level"].options[
                                this.form.elements["level"].selectedIndex].value;
                Logger._setLevel(level);
            }
            var clearButton = document.createElement("INPUT");
            clearButton.type = "button";
            clearButton.value = "Clear log";
            clearButton.onclick =  function()
            {
                Logger._clearMessages();
            }
            // Add form elements
            logForm.appendChild(levelLabel);
            logForm.appendChild(document.createTextNode(" "));
            logForm.appendChild(levelSelect);
            logForm.appendChild(document.createTextNode(" "));
            logForm.appendChild(levelButton);
            logForm.appendChild(document.createTextNode(" "));
            logForm.appendChild(clearButton);

            // Insert the form
            this.out.parentNode.insertBefore(logForm, this.out);
        }

        // Hello Logging World!
        this.debug("Logger initialised");
    },

    /* Logging Functions
    ------------------------------------------------------------------------- */
    debug: function(message)
    {
        if (this.level < 1)
        {
            this._displayMessage(message, "debug", "DEBUG ");
        }
    },

    info: function(message)
    {
        if (this.level < 2)
        {
            this._displayMessage(message, "info", "INFO  ");
        }
    },

    props: function(obj)
    {
        if (this.level < 2)
        {
            var message = "";
            for (prop in obj)
            {
                message += prop + ": " + obj[prop] + "\n";
            }
            this._displayMessage(message, "info", "INFO  ");
        }
    },

    warn: function(message)
    {
        if (this.level < 3)
        {
            this._displayMessage(message, "warn", "WARN  ");
        }
    },

    error: function(message)
    {
        if (this.level < 4)
        {
            this._displayMessage(message, "error", "ERROR ");
        }
    },

    fatal: function(message)
    {
        if (this.level < 5)
        {
            this._displayMessage(message, "fatal", "FATAL ");
        }
    },

    /* Logging Level Checking Functions
     * Used to avoid the overhead of building a "complex" logging message, such
     * as those which involve string concatenations or additional method calls,
     * unless the message will be shown.
    ------------------------------------------------------------------------- */
    isDebugEnabled: function()
    {
        return (this.level < 1);
    },

    isInfoEnabled: function()
    {
        return (this.level < 2);
    },

    isWarnEnabled: function()
    {
        return (this.level < 3);
    },

    isErrorEnabled: function()
    {
        return (this.level < 4);
    },

    isFatalEnabled: function()
    {
        return (this.level < 5);
    },

    _displayMessage: function(message, className, label)
    {
        var div = document.createElement("div");
        div.className = className;
        div.appendChild(document.createTextNode(label + this._getLogTime() +
                                                message));
        //this.out.appendChild(div);
        this.out.insertBefore(div, this.out.firstChild);
    },

    _getLogTime: function()
    {
        var now = new Date();
        var hours = now.getHours();
        if (hours < 10) hours = "0" + hours;
        var minutes = now.getMinutes();
        if (minutes < 10) minutes = "0" + minutes;
        var seconds = now.getSeconds();
        if (seconds < 10) seconds = "0" + seconds;
        var milliseconds = now.getMilliseconds();
        if (milliseconds < 10) milliseconds = "00" + milliseconds;
        else if (milliseconds < 100) milliseconds = "0" + milliseconds;
        return hours + ":" + minutes + ":" + seconds + "." + milliseconds + " ";
    },

    _internal: function(message)
    {
        this._displayMessage(message, "error", "INTER ");
    },

    _logWindowError: function(message, url, line)
    {
        this._internal("Error on line " + line + " of " + url + "\n" + message);
    },

    _clearMessages: function()
    {
        this._removeChildNodes(this.out);
    },

    _setLevel: function(level)
    {
        this.level = level;
        this._internal("Logging level set to " + this.levelNames[this.level]);
    },

    _populateSelect: function(select, options)
    {
        for(var i = 0, option; option = options[i]; i++)
        {
            var opt = document.createElement("OPTION");
            opt.value = option.value;
            opt.appendChild(document.createTextNode(option.name));
            select.appendChild(opt);
        }
    },

    _removeChildNodes: function(node)
    {
        while (node.childNodes.length)
        {
            node.removeChild(node.childNodes[0]);
        }
    }
}