/**
 * 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) {
        var num = elem._allelsid;
		return function(event) {
			GenericBehavior.tellElementBehaviors(
                _allels[num], 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;
	}

});

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;
    },
    
// 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);
	}
};