/*
Script: Class.js
Contains the Class Function for easily creating, extending, and implementing reusable Classes.

License:
MIT-style license.
*/

var Class = new Native({

name: 'Class',

initialize: function(properties){
properties = properties || {};
var klass = function(empty){
for (var key in this) this[key] = $unlink(this[key]);
for (var mutator in Class.Mutators){
if (!this[mutator]) continue;
Class.Mutators[mutator](this, this[mutator]);
delete this[mutator];
}

this.constructor = klass;
if (empty === $empty) return this;

var self = (this.initialize) ? this.initialize.apply(this, arguments) : this;
if (this.options && this.options.initialize) this.options.initialize.call(this);
return self;
};

$extend(klass, this);
klass.constructor = Class;
klass.prototype = properties;
return klass;
}

});

Class.implement({

implement: function(){
Class.Mutators.Implements(this.prototype, Array.slice(arguments));
return this;
}

});

Class.Mutators = {
  
  Implements: function(self, klasses){
  $splat(klasses).each(function(klass){
  $extend(self, ($type(klass) == 'class') ? new klass($empty) : klass);
  });
  },
  
  Extends: function(self, klass){
  var instance = new klass($empty);
  delete instance.parent;
  delete instance.parentOf;

  for (var key in instance){
  var current = self[key], previous = instance[key];
  if (current == undefined){
  self[key] = previous;
  continue;
  }

  var ctype = $type(current), ptype = $type(previous);
  if (ctype != ptype) continue;

  switch (ctype){
  case 'function': 
  // this code will be only executed if the current browser does not support function.caller (currently only opera).
  // we replace the function code with brute force. Not pretty, but it will only be executed if function.caller is not supported.

  if (!arguments.callee.caller) self[key] = eval('(' + String(current).replace(/\bthis\.parent\(\s*(\))?/g, function(full, close){
  return 'arguments.callee._parent_.call(this' + (close || ', ');
  }) + ')');

  // end "opera" code
  self[key]._parent_ = previous;
    break;
  case 'object': self[key] = $merge(previous, current);
  }

  }

  self.parent = function(){
  return arguments.callee.caller._parent_.apply(this, arguments);
  };

  self.parentOf = function(descendant){
  return descendant._parent_.apply(this, Array.slice(arguments, 1));
  };
  }
  
};
