/**
 * Base class used to define new behavior classes.
 * @class GenericBehavior
 */
var GenericBehavior = new Object();


Object.extend(GenericBehavior, {

/**
 * Creates a new behavior class
 * @param name The name of the new class
 * @param body Object containing the definition of the new class
 * @returns A class that inherits from GenericBehavior
 * @member: GenericBehavior
 */
    create: function(name, body) {
        var theclass = Class.create();
            
        Object.extend(theclass.prototype, GenericBehavior.BaseClass);
        Object.extend(theclass.prototype, body);
        theclass.prototype._behaviorClassName = name;
        theclass.prototype.SUPER = GenericBehavior.BaseClass;
        theclass.prototype.tellSuper = function() {
            var args = $A(arguments);
            var func = args.shift();
            this.SUPER[func].apply(this, args);
        }
        theclass.prototype.sets = new Object();
        //TODO: reference behaviors by name
        
        return theclass;
    },
    
    // Attaches a behavior object to the given element
    addBehavior: function (elem, behavior_class, args) {
        elem = el(elem);
        if (!elem || typeof elem != 'object') { return false; }
        if (!args) { args = { }; }
        elem._behaviorized = 1;

        // Create and attach the behavior, return if attach fails
        var behavior = new behavior_class(elem, args);
        var success = behavior.onAttach({}, elem, args);
        if (!success) { return false; }

        // store behavior data with element
        if (!elem.behaviors) { 
            elem.behaviors = {
                behaviors: new GenericEventResponders(),
                realEventListeners: new Array()
            };
        };
        var b = elem.behaviors;
        b.behaviors.register(behavior);
        
        // Make the element respond to the synthetic events in the behavior
        for (var handler_name in behavior) {
            if (typeof elem[handler_name] == 'function') { continue; }            
            if (handler_name.match(/^on[A-Z]/) == null) { continue; }
            elem[handler_name] = this.callEventFunction(elem, handler_name);
        }
        
        // Make the behavior an observer of real events on the element
        for (var handler_name in behavior) {
            var func = behavior[handler_name];
            if (typeof func != 'function') { continue; }
            var bits = handler_name.match(/^on([a-z].*)/);
            if (bits == null) { continue; }
            var revent = bits[1];
            var listener = behavior[handler_name].bindAsEventListener(behavior);
            b.realEventListeners.push(listener);
            Event.observe(elem, revent, listener, false);
        }
  
         return behavior;
    },


    callEventFunction: function(elem, eventname) {
        return function(event) {
            GenericBehavior.tellElementBehaviors(
                elem, eventname, event);
        }
    },

    getElementBehaviors: function(elem) {
        return elem.behaviors.behaviors; 
    },
    getElementBehaviorsByName: function(elem, name) {
        var behaviors = this.getElementBehaviors(elem);
        return behaviors.findAll( function(b) {
            return b._behaviorClassName == name;
        });
    },
    tellElementBehaviors: function(elem, method, args) {
        var behaviors = this.getElementBehaviors(elem);
        if (!behaviors) { return false; }
        behaviors.tell(method, args);
        return true;
    },
    clearElementBehaviors: function(elem) {
        if (elem.behaviors) {
            elem.behaviors = {
                behaviors: new GenericEventResponders(),
                realEventListeners: new Array()
            };
        }
    }

});

GenericBehavior.BaseClass = {

    initialize: function(elem, args) {
        this.onNew({}, args);
    },
    
    snapshot: function() {
        this.origHTML = this.e.innerHTML;
    },

    addToPage: function(name) {
        GenericPage.addToPage(this, name);
        //this.onDraw({});
        return this;
    },
    
    addToSet: function(setname) {
        this.set = setname || 'default';
        this.sets[this.set] =
            this.sets[this.set] || 
            new GenericEventResponders();
        this.sets[this.set].register(this);
        return this;
    },
    tellSet: function(setname, action, args) {
        setname = this.set || setname;
        this.sets[setname].tell(action, args);
    },
    tellMySet: function(action, args) {
        this.tellSet(this.set, action, args);
    },
    
    setargs: function(args) {
        this.args = args;
    },
    // filter null/undefined arguments for external effects (i.e. scriptaculous)
    filterArgs: function(args,available_args) {
        var filtered_args = {};
        for (var property in available_args) {
            if (typeof args[property] != 'undefined') {
                filtered_args[property] = args[property];
            }
            else if (available_args[property] != null) {
                filtered_args[property] = available_args[property];
            }
        }
        return filtered_args;
    },
    
// Event handlers

    onNew: function(event, args) {
        this.setargs(args);
    },
    
    onAttach: function(event, elem, args) {
        this.e = elem;
        //this.snapshot();
        return true;
    },

    onWillClear: function(event) {

    },
    
    onDraw: function(event) {
    
    },
    
    onRedraw: function(event) {
        this.onWillClear(event);
        //Element.unelDeep(this.e);
        if (this.origHTML != null) this.e.innerHTML = this.origHTML;
        this.onDraw(event);
    }
};