/*
 Blackbird - Open Source JavaScript Logging Utility
 Author: G Scott Olson
 Web: http://blackbirdjs.googlecode.com/
 http://www.gscottolson.com/blackbirdjs/
 Version: 1.0

 The MIT License - Copyright (c) 2008 Blackbird Project

 Heavily modified for Tapestry to rename namespace, make use of Prototype: March 2009
 */
( function()
{
    var IE6_POSITION_FIXED = true; // enable IE6 {position:fixed}

    var bbird, checkbox, filters, controls, size;
    var outputList;
    var cache = [];

    var state = { pos:null, size:null, load:null };
    var classes = {};
    var profiler = {};

    var messageTypes = { //order of these properties imply render order of filter controls
        debug: true,  // May be set to false based on Tapestry.DEBUG_ENABLED
        info: true,
        warn: true,
        error: true,
        profile: true
    };

    function constructUI()
    {
        bbird = new Element("div", { 'id': 't-console', 'title': 'F2 toggles / Shift-F2 moves' }).hide();
        var header = new Element("div", { 'class': 't-header' });
        var left = new Element("div", { 'class' : 't-left' });

        left.insert(filters = new Element("div", { 'class': 't-filters' }));

        for (var type in messageTypes)
        {
            var className = messageTypes[type] ? type : type + "Disabled";

            filters.insert(new Element("span", {'class': className, 'title': "filter by " + type, 'type': type }));
        }

        var right = new Element("div", { 'class': 't-right'});

        right.insert(controls = new Element("div", { 'class': 't-controls'}));

        controls.insert(size = new Element("span", { 'title': 'contract', 'op': 'resize' }));

        controls.insert(new Element("span", { 'class': 't-clear', 'title': 'clear', 'op': 'clear' }));
        controls.insert(new Element("span", { 'class': 't-close', 'title': 'close', 'op': 'close' }));

        header.insert(left);
        header.insert(right);

        bbird.insert(header);

        var main = new Element("div", { 'class': 't-main' });
        main.insert(new Element("div", {'class': 't-left'}));

        var mainBody = new Element("div", { 'class': 't-mainBody' });

        mainBody.insert(outputList = new Element("ol"));

        $A(cache).each(function(element)
        {
            outputList.insert(element);
        });

        cache = undefined;

        main.insert(mainBody);
        main.insert(new Element("div", { 'class': 't-right' }));

        bbird.insert(main);

        var footer = new Element("div", { 'class': 't-footer'});

        footer.insert(left = new Element("div", { 'class': 't-left' }));

        left.insert();

        footer.insert(new Element("div", { 'class': 't-right' }));

        bbird.insert(footer);

        $(document.body).insert(bbird);
    }

    function backgroundImage()
    { //(IE6 only) change <BODY> tag's background to resolve {position:fixed} support
        var bodyTag = $(document.body);

        if (bodyTag.currentStyle && IE6_POSITION_FIXED)
        {
            if (bodyTag.currentStyle.backgroundImage == 'none')
            {
                bodyTag.addClassName('t-fix-ie6-background');
            }
            if (bodyTag.currentStyle.backgroundAttachment == 'scroll')
            {
                bodyTag.style.backgroundAttachment = 'fixed';
            }
        }
    }

    function addMessage(type, content)
    { //adds a message to the output list

        content = ( content.constructor == Array ) ? content.join('') : content;

        var newMsg = new Element("li", { 'class': type});

        newMsg.insert(new Element("span", { 'class': 'icon'}));
        newMsg.insert(content);

        if (outputList)
        {
            outputList.insert(newMsg);

            // If the added message is not being filtered out, then
            // make sure it is visible to the user.
            if (messageTypes[type] && !isVisible())
            {
                scrollToBottom();
                show();
            }
        }
        else
        {
            cache.push(newMsg);
        }

    }

    function clear()
    { //clear list output
        outputList.update();
    }

    function clickControl(evt)
    {
        var el = evt.element();

        if (el.tagName == 'SPAN')
        {
            switch (el.getAttributeNode('op').nodeValue)
                    {
                case 'resize': resize(); break;
                case 'clear':  clear();  break;
                case 'close':  hide();   break;
            }
        }
    }

    function clickFilter(evt)
    { //show/hide a specific message type
        var span = evt.element();

        if (span && span.tagName == 'SPAN')
        {
            var type = span.readAttribute('type');

            if (evt.altKey)
            {
                var active = 0;
                for (var entry in messageTypes)
                {
                    if (messageTypes[ entry ]) active++;
                }

                var oneActiveFilter = ( active == 1 && messageTypes[ type ] );

                filters.childElements().each(function (child)
                {
                    var childType = child.readAttribute('type');

                    var enabled = oneActiveFilter || (childType == type);

                    messageTypes[ childType ] = enabled;

                    child.className = enabled ? childType : childType + 'Disabled';
                });
            }
            else
            {
                messageTypes[ type ] = ! messageTypes[ type ];
                span.className = ( messageTypes[ type ] ) ? type : type + 'Disabled';
            }

            rebuildOutputListClassName();

            scrollToBottom();
        }
    }

    function rebuildOutputListClassName()
    {
        //build outputList's class from messageTypes object
        var disabledTypes = [];
        for (type in messageTypes)
        {
            if (! messageTypes[ type ]) disabledTypes.push(type);
        }
        disabledTypes.push('');
        outputList.className = disabledTypes.join('Hidden ');
    }


    function scrollToBottom()
    { //scroll list output to the bottom
        outputList.scrollTop = outputList.scrollHeight;
    }

    function isVisible()
    {
        return bbird.visible();
    }

    function hide()
    {
        bbird.style.display = 'none';
    }

    function show()
    {
        var body = $(document.body);

        body.removeChild(bbird);
        body.appendChild(bbird);

        bbird.style.display = 'block';
    }

    //sets the position
    function reposition(position)
    {
        if (position === undefined || position == null)
        {
            position = ( state && state.pos === null ) ? 1 : ( state.pos + 1 ) % 4; //set to initial position ('topRight') or move to next position
        }

        switch (position)
                {
            case 0: classes[ 0 ] = 'bbTopLeft'; break;
            case 1: classes[ 0 ] = 'bbTopRight'; break;
            case 2: classes[ 0 ] = 'bbBottomLeft'; break;
            case 3: classes[ 0 ] = 'bbBottomRight'; break;
        }
        state.pos = position;
        saveState();
    }

    function resize(big)
    {
        if (big === undefined || big === null)
        {
            big = ( state && state.size == null ) ? 0 : ( state.size + 1 ) % 2;
        }

        classes[ 1 ] = ( big === 0 ) ? 'bbSmall' : 'bbLarge'

        size.title = ( big === 1 ) ? 'small' : 'large';
        size.className = "t-" + size.title;

        state.size = big;

        saveState();
        scrollToBottom();
    }

    function saveState()
    {
        var newClass = [];
        for (word in classes)
        {
            newClass.push(classes[ word ]);
        }
        bbird.className = newClass.join(' ');
    }

    //event handler for 'keyup' event for window
    function readKey(evt)
    {
        var code = 113; //F2 key

        if (evt && evt.keyCode == code)
        {

            var visible = isVisible();

            if (visible && evt.shiftKey && evt.altKey) clear();
            else if (visible && evt.shiftKey) reposition();
            else if (!evt.shiftKey && !evt.altKey)
                {
                    ( visible ) ? hide() : show();
                }
        }
    }


    Tapestry.Logging = {
        toggle:
                function()
                {
                    ( isVisible() ) ? hide() : show();
                },
        hide:
                function()
                {
                    hide();
                },
        resize:
                function()
                {
                    resize();
                },
        clear:
                function()
                {
                    clear();
                },
        move:
                function()
                {
                    reposition();
                },
        debug:
                function(msg)
                {
                    addMessage('debug', msg);
                },
        warn:
                function(msg)
                {
                    addMessage('warn', msg);
                },
        info:
                function(msg)
                {
                    addMessage('info', msg);
                },
        error:
                function(msg)
                {
                    addMessage('error', msg);
                },
        profile:
                function(label)
                {
                    var currentTime = new Date(); //record the current time when profile() is executed

                    if (label == undefined || label == '')
                    {
                        addMessage('error', '<b>ERROR:</b> Please specify a label for your profile statement');
                    }
                    else if (profiler[ label ])
                    {
                        addMessage('profile', [ label, ': ', currentTime - profiler[ label ],    'ms' ].join(''));
                        delete profiler[ label ];
                    }
                    else
                    {
                        profiler[ label ] = currentTime;
                        addMessage('profile', label);
                    }
                    return currentTime;
                }
    }

    Tapestry.onDOMLoaded(
        /* initialize Blackbird when the page loads */
            function()
            {
                messageTypes.debug = Tapestry.DEBUG_ENABLED;

                constructUI();

                rebuildOutputListClassName();

                backgroundImage();

                filters.observe("click", clickFilter.bindAsEventListener());
                controls.observe("click", clickControl.bindAsEventListener());

                document.observe("keyup", readKey.bindAsEventListener());

                resize(state.size);
                reposition(state.pos);              

                scrollToBottom();

                // The original Blackbird code would unregister the events, but I believe that's not
                // necessary due to Prototype.
            }.bind(this));
})();

