/*--------------------------------------------------------------------------
 *  sabinos.Log.Appenders , version 0.1
 *  (c) 2008 Felipe Sabino
 *
 *  http://www.sabinos.net
 *  sabinosfelipe@gmail.com
 *
 * This code is under GNU General Public License (GPL) http://www.gnu.org/licenses/gpl-3.0.txt
 *
 *--------------------------------------------------------------------------*/
 
 //Check if sabinos.Log was loaded
if(Object.isUndefined(sabinos.Log) || sabinos.Log == null)
    throw 'sabinos.Log.Appenders Exception - Missing sabinos.Log namespace. Probably sabino.Log file was not properly loaded';

//Set Filter Hash Values
sabinos.Log.Level.set(sabinos.Log.Level.All, 'All');
sabinos.Log.Level.set(sabinos.Log.Level.Trace, 'Trace');
sabinos.Log.Level.set(sabinos.Log.Level.Debug, 'Debug');
sabinos.Log.Level.set(sabinos.Log.Level.Info, 'Info');
sabinos.Log.Level.set(sabinos.Log.Level.Warn, 'Warn');
sabinos.Log.Level.set(sabinos.Log.Level.Error, 'Error');
sabinos.Log.Level.set(sabinos.Log.Level.Fatal, 'Fatal');

//Enable logging
sabinos.Log.Enabled = true;


//Create AppenderCollection class properly
sabinos.Log.AppenderCollection.prototype = {
    initialize : function(enumerable){
        var args = $A(enumerable);
        args = args.compact();
        if(!Object.isUndefined(sabinos.Log.Appenders)) {
                if (!args.all( function(arg) { return arg instanceof sabinos.Log.Appenders.Base; }))
                    throw 'sabinos.Log.AppenderCollection Exception - Constructor: One or more collection itens is not a sabinos.Log.Appender.Base derived object';
                this.appenders = args;
        } else {
            if(args.length >0)
                throw sabinos.Log.AppenderMisingException;
            else
                this.appenders = args;
        }
    },
    add : function(appender) {
        if(!Object.isUndefined(sabinos.Log.Appenders)) {
            if(appender instanceof sabinos.Log.Appenders.Base)
                this.appenders.push(appender);
            else
                throw 'sabinos.Log.AppenderCollection Exception - Only sabinos.Log.Appender.Base derived objects can be added to collection';
        }
        else
            throw sabinos.Log.AppenderMisingException;
    },
    log : function(logType, log, message, objects) {
        if(!Object.isUndefined(sabinos.Log.Appenders)) {
            callTime = new Date();
            this._each(function(appender){
                if(appender.isLevelSelected(logType)) {
                    appender.log(logType, log, message, callTime, objects);                        
                }
            });
        }
    },
    loadConfigurationFile : Prototype.emptyFunction, //To be implemented.
    _each: function(iterator) {
        return this.appenders._each(iterator);
    }
}

//Check if sabinos.Log.Appenders was already loaded
if(Object.isUndefined(sabinos.Log.Appenders) || sabinos.Log.Appenders == null)
{
    //Creates sabinos.Log.Appenders namespace
    sabinos.Log.Appenders = {};

    sabinos.Log.Appenders.Base = Class.create();

    //Appenders Base Class
    //All Appenders must derive from this class, including custom ones.
    sabinos.Log.Appenders.Base.prototype = {
        initialize: function(type, pattern, level) {
            this.Type = type;
            this.Pattern = pattern;
            if(level == null)
                this.Level = sabinos.Log.Level.All;
            else
                this.Level = level;
        },
        isLevelSelected : function(logType) {
            return (logType & this.Level) == logType;
        },
        getMessage : function(args, pattern) {
            msg = pattern;
            if(msg == null)
                msg = this.Pattern;
            msg = msg.replace(/{level}/,sabinos.Log.Level.get(args[0]));
            msg = msg.replace(/{caller}/,args[1].Caller);
            msg = msg.replace(/{message}/, args[2]);
            msg = msg.replace(/{time}/, args[3]);
            if(msg.indexOf('{objJSON}')>0) {
                objJson =  Object.toJSON(this.getObjects(args));
                msg = msg.replace(/{objJSON}/, objJson);
            }
            return msg;
        },
        getObjects : function(args)
        {
            if(typeof(args[4])!='undefined')
                return args[4];
            else
               return null;
        },
        log : Prototype.emptyFunction
    }

    //Alert Appender
    //Display log messages with alert() native function
    sabinos.Log.Appenders.Alert = Class.create(sabinos.Log.Appenders.Base, {
        initialize : function($super, pattern, level) {
            $super('alert', pattern, level);
        },
        log : function(){
            msg = this.getMessage(arguments);
            alert(msg);
        }
    });


    //Console Appender
    //Displays messages using console.log() firebug function if firebug is available
    sabinos.Log.Appenders.Console = Class.create(sabinos.Log.Appenders.Base, {
        initialize : function($super, pattern, level) {
            $super('console', pattern, level);
        },
        log : function(){
            try {
                if(!Object.isUndefined(console)) {
                    msg = this.getMessage(arguments);
                    console.log(msg);
                } 
            } catch(ex) {}
        }
    });

    //Element Appender
    //Displays message inside an HTML element.
    sabinos.Log.Appenders.Element = Class.create(sabinos.Log.Appenders.Base, {
        initialize : function($super, pattern, level, element) {
            $super('element', pattern, level);
            this.Element = $(element);
            if(this.Element == null)
                throw 'sabinos.Log.Appenders.Element Exception - null Element provided. Probably DOM not loaded or element with id does not exists';
            if(this.Element.type == 'textarea' || this.Element.type == 'text')
                this.ElementProperty = 'value';
            else
                this.ElementProperty = 'innerHTML';
             this.Element[this.ElementProperty] = '';
        },
        log : function(){
            if(this.Element!=null) {
                msg = this.getMessage(arguments);
                this.Element[this.ElementProperty] += msg;
            }
        }
    });


    //Event Appender
    //Fires messages using Prototype document.fire function
    sabinos.Log.Appenders.Event = Class.create(sabinos.Log.Appenders.Base, {
        initialize : function($super, pattern, level, eventName) {
            $super('event', pattern, level);
            this.Event = eventName;
        },
        log : function(){
            msg = this.getMessage(arguments);
            objs = this.getObjects(arguments);
            document.fire(this.Event, { Message: msg, Objects:  objs});
        }
    });


    //Floating Div Appender
    //Displays messages in a floating DIV element. Allow javascript evaluation
    sabinos.Log.Appenders.Floating = Class.create(sabinos.Log.Appenders.Base, {
        initialize : function($super, pattern, level, options) {
            $super('floating div', pattern, level);
                
            //Create Elements
            //giveupandusetables.com :p
            var floatDiv = new Element('div');
            tb = '<table width="100%"  cellpadding="0" cellspacing="3">                     \
            <tr>                                                                            \
                <td><input type="text" style="font-family:Verdana;font-size:11px;"></td>    \
                <td><input type="button" value="Evaluate"></td>                             \
                <td><input type="button" value="Clear"></td>                                \
            </tr>                                                                           \
            <tr>                                                                            \
                <td colspan="3"><div style="border:1px solid silver;overflow:auto;          \
                        width:100%;height:155px;background-color:#ffffff;                   \
                        font-family:Verdana;font-size:11px;"></td>                          \
            </tr>                                                                           \
            </table>';
            floatDiv.update(tb);
            //write style to kep the div floating at bottom right of screen
            floatDiv.writeAttribute('style','background-color:whitesmoke;border:            \
                1px solid silver;height:200px;right:5px;padding:5px;position:fixed;         \
                bottom:5px;width:300px;overflow:hidden;');
            document.body.appendChild(floatDiv);
            elems = floatDiv.getElementsBySelector('input');
            this.Evaluate = elems[0];
            this.Log = floatDiv.getElementsBySelector('div')[0];
            //Observer Evaluate and Clear buttons
            Element.observe(elems[1],'click',this.evaluate.bind(this));
            Element.observe(elems[2],'click',this.clear.bind(this));
        },
        log : function(){
            msg = this.getMessage(arguments);
            this.writeMessage(msg);
        },
        writeMessage : function(msg) {
            if(this.Log!=null) {
                this.Log.innerHTML += msg;
                this.Log.scrollTop = this.Log.scrollHeight;
            }
        },
        evaluate : function(){
            try{
                expEvaluate = $F(this.Evaluate);
                this.writeMessage('>>> ' + expEvaluate + '<br>')
                evalResult = eval(expEvaluate);
                typeOf = typeof(evalResult)
                switch(typeOf) {
                    case 'object':
                        this.writeMessage(Object.toJSON(evalResult)+'<br>');
                        break;
                    case 'function':
                        this.writeMessage(evalResult.constructor+'<br>');
                        break;
                    case 'undefined':
                        break;
                    default:
                        this.writeMessage(evalResult+'<br>');
                        break;
                }
            } catch(ex) {
                this.writeMessage('Error evaluating expression.<br>');
            }
        },
        clear : function() {
            this.Log.innerHTML = '';
        }
    });
}
