--[[
Name: AceOO-2.0
Revision: $Rev: 8313 $
Author(s): ckknight (ckknight@gmail.com)
           kergoth (kergoth@handhelds.org)
Website: http://www.wowace.com/
Documentation: http://wiki.wowace.com/index.php/AceOO-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0
Description: Library to provide an object-orientation framework.
Dependencies: AceLibrary, Compost-2.0 (optional)
]]

local MAJOR_VERSION = "AceOO-2.0"
local MINOR_VERSION = "$Revision: 8313 $"

-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

local Compost = AceLibrary:HasInstance("Compost-2.0") and AceLibrary("Compost-2.0")

local AceOO = {
	error = AceLibrary.error,
	argCheck = AceLibrary.argCheck
}

-- @function	getuid
-- @brief		Obtain a unique string identifier for the object in question.
-- @param t		The object to obtain the uid for.
-- @return		The uid string.
local function pad(cap)
	return string.rep('0', 8 - string.len(cap)) .. cap
end
local function getuid(t)
	local mt = getmetatable(t)
	setmetatable(t, nil)
	local str = tostring(t)
	setmetatable(t, mt)
	local _,_,cap = string.find(str, '[^:]*: 0x(.*)$')
	if cap then return pad(cap) end
	_,_,cap = string.find(str, '[^:]*: (.*)$')
	if cap then return pad(cap) end
end

local function getlibrary(o)
	if type(o) == "table" then
		return o
	elseif type(o) == "string" then
		if not AceLibrary:HasInstance(o) then
			AceOO:error("Library %q does not exist.", o)
		end
		return AceLibrary(o)
	end
end

-- @function		Factory
-- @brief			Construct a factory for the creation of objects.
-- @param obj		The object whose init method will be called on the new factory
--					object.
-- @param newobj	The object whose init method will be called on the new
--					objects that the Factory creates, to initialize them.
-- @param (a1..a20) Arguments which will be passed to obj.init() in addition
--					to the Factory object.
-- @return			The new factory which creates a newobj when its new method is called,
--					or when it is called directly (__call metamethod).
local Factory
do
	local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
							a13, a14, a15, a16, a17, a18, a19, a20)
		local t = Compost and Compost:Acquire() or {}
		local uid = getuid(t)
		local l = getlibrary
		obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
					l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
					l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
					l(a20))
		t.uid = uid
		return t
	end
	
	local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
									a11, a12, a13, a14, a15, a16, a17, a18,
									a19, a20)
		local o = self.prototype
		local l = getlibrary
		return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7),
						l(a8), l(a9), l(a10), l(a11), l(a12), l(a13),
						l(a14), l(a15), l(a16), l(a17), l(a18), l(a19),
						l(a20))
	end

	function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
									a11, a12, a13, a14, a15, a16, a17, a18,
									a19, a20)
		local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
							a13, a14, a15, a16, a17, a18, a19, a20)
		t.prototype = newobj
		t.new = createnew
		getmetatable(t).__call = t.new
		return t
	end
end


local function objtostring(self)
	if self.ToString then
		return self:ToString()
	elseif self.GetLibraryVersion then
		return (self:GetLibraryVersion())
	elseif self.super then
		local s = "Sub-" .. tostring(self.super)
		local first = true
		if self.interfaces then
			for interface in pairs(self.interfaces) do
				if first then
					s = s .. "(" .. tostring(interface)
					first = false
				else
					s = s .. ", " .. tostring(interface)
				end
			end
		end
		if self.mixins then
			for mixin in pairs(self.mixins) do
				if first then
					s = s .. tostring(mixin)
					first = false
				else
					s = s .. ", " .. tostring(mixin)
				end
			end
		end
		if first then
			if self.uid then
				return s .. ":" .. self.uid
			else
				return s
			end
		else
			return s .. ")"
		end
	else
		return self.uid and 'Subclass:' .. self.uid or 'Subclass'
	end
end

-- @table			Object
-- @brief			Base of all objects, including Class.
--
-- @method			init
-- @brief			Initialize a new object.
-- @param newobject The object to initialize
-- @param class		The class to make newobject inherit from
local Object
do
	Object = {}
	function Object:init(newobject, class)
		local parent = class or self
		if not rawget(newobject, 'uid') then
			newobject.uid = getuid(newobject)
		end
		local mt = Compost and Compost:AcquireHash(
			'__index', parent,
			'__tostring', objtostring
		) or {
			__index = parent,
			__tostring = objtostring,
		}
		setmetatable(newobject, mt)
	end
	Object.uid = getuid(Object)
	setmetatable(Object, { __tostring = function() return 'Object' end })
end

local Interface

local function validateInterface(object, interface)
	if not object.class and object.prototype then
		object = object.prototype
	end
	for k,v in pairs(interface.interface) do
		if tostring(type(object[k])) ~= v then
			return false
		end
	end
	if interface.superinterfaces then
		for superinterface in pairs(interface.superinterfaces) do
			if not validateInterface(object, superinterface) then
				return false
			end
		end
	end
	if type(object.class) == "table" and rawequal(object.class.prototype, object) then
		if not object.class.interfaces then
			rawset(object.class, 'interfaces', Compost and Compost:Acquire() or {})
		end
		object.class.interfaces[interface] = true
	elseif type(object.class) == "table" and type(object.class.prototype) == "table" then
		validateInterface(object.class.prototype, interface)
		-- check if class is proper, thus preventing future checks.
	end
	return true
end

-- @function		inherits
-- @brief			Return whether an Object or Class inherits from a given
--					parent.
-- @param object	Object or Class to check
-- @param parent	Parent to test inheritance from
-- @return			whether an Object or Class inherits from a given
--					parent.
local function inherits(object, parent)
	object = getlibrary(object)
	if type(parent) == "string" then
		if not AceLibrary:HasInstance(parent) then
			return false
		else
			parent = AceLibrary(parent)
		end
	end
	AceOO:argCheck(parent, 2, "table")
	if type(object) ~= "table" then
		return false
	end
	local current
	if object.class then
		current = object.class
	else
		current = object
	end
	if type(current) ~= "table" then
		return false
	end
	if rawequal(current, parent) then
		return true
	end
	if parent.class then
		while true do
			if rawequal(current, Object) then
				break
			end
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if rawequal(mixin, parent) then
						return true
					end
				end
			end
			if current.interfaces then
				for interface in pairs(current.interfaces) do
					if rawequal(interface, parent) then
						return true
					end
				end
			end
			current = current.super
			if type(current) ~= "table" then
				break
			end
		end
		
		local isInterface = false
		local curr = parent.class
		while true do
			if rawequal(curr, Object) then
				break
			elseif rawequal(curr, Interface) then
				isInterface = true
				break
			end
			curr = curr.super
			if type(curr) ~= "table" then
				break
			end
		end
		return isInterface and validateInterface(object, parent)
	else
		while true do
			if rawequal(current, parent) then
				return true
			elseif rawequal(current, Object) then
				return false
			end
			current = current.super
			if type(current) ~= "table" then
				return false
			end
		end
	end
end

-- @table			Class
-- @brief			An object factory which sets up inheritence and supports
--					'mixins'.
--
-- @metamethod		Class call
-- @brief			Call ClassFactory:new() to create a new class.
--
-- @method			Class new
-- @brief			Construct a new object.
-- @param (a1..a20) Arguments to pass to the object init function.
-- @return			The new object.
--
-- @method			Class init
-- @brief			Initialize a new class.
-- @param parent	Superclass.
-- @param (a1..a20) Mixins.
--
-- @method			Class ToString
-- @return			A string representing the object, in this case 'Class'.
local initStatus
local Class
local Mixin
local autoEmbed = false
local function traverseInterfaces(bit, total)
	if bit.superinterfaces then
		for interface in pairs(bit.superinterfaces) do
			if not total[interface] then
				total[interface] = true
				traverseInterfaces(interface, total)
			end
		end
	end
end
local class_new
do
	Class = Factory(Object, setmetatable({}, {__index = Object}), Object)
	Class.super = Object
	
	local function protostring(t)
		return '<' .. tostring(t.class) .. ' prototype>'
	end
	local function classobjectstring(t)
		if t.ToString then
			return t:ToString()
		elseif t.GetLibraryVersion then
			return (t:GetLibraryVersion())
		else
			return '<' .. tostring(t.class) .. ' instance>'
		end
	end
	local function classobjectequal(self, other)
		if type(self) == "table" and self.Equals then
			return self:Equals(other)
		elseif type(other) == "table" and other.Equals then
			return other:Equals(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) == 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) == 0
		else
			return rawequal(self, other)
		end
	end
	local function classobjectlessthan(self, other)
		if type(self) == "table" and self.IsLessThan then
			return self:IsLessThan(other)
		elseif type(other) == "table" and other.IsLessThanOrEqualTo then
			return not other:IsLessThanOrEqualTo(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) < 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) > 0
		elseif type(other) == "table" and other.IsLessThan and other.Equals then
			return other:Equals(self) or other:IsLessThan(self)
		else
			AceOO:error("cannot compare two objects")
		end
	end
	local function classobjectlessthanequal(self, other)
		if type(self) == "table" and self.IsLessThanOrEqualTo then
			return self:IsLessThanOrEqualTo(other)
		elseif type(other) == "table" and other.IsLessThan then
			return not other:IsLessThan(self)
		elseif type(self) == "table" and self.CompareTo then
			return self:CompareTo(other) <= 0
		elseif type(other) == "table" and other.CompareTo then
			return other:CompareTo(self) >= 0
		elseif type(self) == "table" and self.IsLessThan and self.Equals then
			return self:Equals(other) or self:IsLessThan(other)
		else
			AceOO:error("cannot compare two incompatible objects")
		end
	end
	local function classobjectadd(self, other)
		if type(self) == "table" and self.Add then
			return self:Add(other)
		else
			AceOO:error("cannot add two incompatible objects")
		end
	end
	local function classobjectsub(self, other)
		if type(self) == "table" and self.Subtract then
			return self:Subtract(other)
		else
			AceOO:error("cannot subtract two incompatible objects")
		end
	end
	local function classobjectunm(self, other)
		if type(self) == "table" and self.UnaryNegation then
			return self:UnaryNegation(other)
		else
			AceOO:error("attempt to negate an incompatible object")
		end
	end
	local function classobjectmul(self, other)
		if type(self) == "table" and self.Multiply then
			return self:Multiply(other)
		else
			AceOO:error("cannot multiply two incompatible objects")
		end
	end
	local function classobjectdiv(self, other)
		if type(self) == "table" and self.Divide then
			return self:Divide(other)
		else
			AceOO:error("cannot divide two incompatible objects")
		end
	end
	local function classobjectpow(self, other)
		if type(self) == "table" and self.Exponent then
			return self:Exponent(other)
		else
			AceOO:error("cannot exponentiate two incompatible objects")
		end
	end
	local function classobjectconcat(self, other)
		if type(self) == "table" and self.Concatenate then
			return self:Concatenate(other)
		else
			AceOO:error("cannot concatenate two incompatible objects")
		end
	end
	function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		if self.virtual then
			AceOO:error("Cannot instantiate a virtual class.")
		end
		
		local o = self.prototype
		local newobj = Compost and Compost:Acquire() or {}
		if o.class and o.class.instancemeta then
			setmetatable(newobj, o.class.instancemeta)
		else
			Object:init(newobj, o)
		end
		
		if self.interfaces and not self.interfacesVerified then
			-- Verify the interfaces
			
			for interface in pairs(self.interfaces) do
				for field,kind in pairs(interface.interface) do
					if tostring(type(newobj[field])) ~= kind then
						AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field])))
					end
				end
			end
			self.interfacesVerified = true
		end
		local tmp = initStatus
		initStatus = newobj
		newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		if initStatus then
			initStatus = tmp
			AceOO:error("Initialization not completed, be sure to call the superclass's init method.")
			return
		end
		initStatus = tmp
		return newobj
	end
	local classmeta = {
		__tostring = objtostring,
		__call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
			return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
						a13, a14, a15, a16, a17, a18, a19, a20)
		end,
	}
	function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
											a10, a11, a12, a13, a14, a15, a16,
											a17, a18, a19, a20)
		parent = parent or self
		
		local total
		
		if parent.class then
			total = {
				parent, a1, a2, a3, a4, a5, a6, a7, a8, a9,
						a10, a11, a12, a13, a14, a15, a16,
						a17, a18, a19, a20
			}
			parent = self
		else
			total = {
				a1, a2, a3, a4, a5, a6, a7, a8, a9,
				a10, a11, a12, a13, a14, a15, a16,
				a17, a18, a19, a20
			}
		end
		if not inherits(parent, Class) then
			AceOO:error("Classes must inherit from a proper class")
		end
		if parent.sealed then
			AceOO:error("Cannot inherit from a sealed class")
		end
		for i,v in ipairs(total) do
			if inherits(v, Mixin) and v.class then
				if not newclass.mixins then
					newclass.mixins = Compost and Compost:Acquire() or {}
				end
				if newclass.mixins[v] then
					AceOO:error("Cannot explicitly inherit from the same mixin twice")
				end
				newclass.mixins[v] = true
			elseif inherits(v, Interface) and v.class then
				if not newclass.interfaces then
					newclass.interfaces = Compost and Compost:Acquire() or {}
				end
				if newclass.interfaces[v] then
					AceOO:error("Cannot explicitly inherit from the same interface twice")
				end
				newclass.interfaces[v] = true
			else
				AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces")
			end
		end
		if parent.interfaces then
			if newclass.interfaces then
				for interface in pairs(parent.interfaces) do
					newclass.interfaces[interface] = true
				end
			else
				newclass.interfaces = parent.interfaces
			end
		end
		for k in pairs(total) do
			total[k] = nil
		end
		table.setn(total, 0)
		
		newclass.super = parent
		
		newclass.prototype = setmetatable(total, Compost and Compost:AcquireHash(
			'__index',  parent.prototype,
			'__tostring', protostring
		) or {
			__index = parent.prototype,
			__tostring = protostring,
		})
		total = nil
		
		newclass.instancemeta = {
			__index = newclass.prototype,
			__tostring = classobjectstring,
			__eq = classobjectequal,
			__lt = classobjectlessthan,
			__le = classobjectlessthanequal,
			__add = classobjectadd,
			__sub = classobjectsub,
			__unm = classobjectunm,
			__mul = classobjectmul,
			__div = classobjectdiv,
			__pow = classobjectpow,
			__concat = classobjectconcat,
		}
		
		setmetatable(newclass, classmeta)
		
		newclass.new = class_new
		
		if newclass.mixins then
			-- Fold in the mixins
			local err, msg
			for mixin in pairs(newclass.mixins) do
				local ret
				autoEmbed = true
				ret, msg = pcall(mixin.embed, mixin, newclass.prototype)
				autoEmbed = false
				if not ret then
					err = true
					break
				end
			end
	
			if err then
				local pt = newclass.prototype
				for k,v in pairs(pt) do
					pt[k] = nil
				end
	
				-- method conflict
				AceOO:error(msg)
			end
		end
		
		newclass.prototype.class = newclass
		
		if newclass.interfaces then
			for interface in pairs(newclass.interfaces) do
				traverseInterfaces(interface, newclass.interfaces)
			end
		end
		if newclass.mixins then
			for mixin in pairs(newclass.mixins) do
				if mixin.interfaces then
					if not newclass.interfaces then
						newclass.interfaces = Compost and Compost:Acquire() or {}
					end
					for interface in pairs(mixin.interfaces) do
						newclass.interfaces[interface] = true
					end
				end
			end
		end
	end
	function Class:ToString()
		if type(self.GetLibraryVersion) == "function" then
			return (self:GetLibraryVersion())
		else
			return "Class"
		end
	end
	
	local tmp
	function Class.prototype:init()
		if rawequal(self, initStatus) then
			initStatus = nil
		else
			AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2)
		end
		self.uid = getuid(self)
		local current = self.class
		while true do
			if current == Class then
				break
			end
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if type(mixin.OnInstanceInit) == "function" then
						mixin:OnInstanceInit(self)
					end
				end
			end
			current = current.super
		end
	end
end


-- @object	ClassFactory
-- @brief	A factory for creating classes.	Rarely used directly.
local ClassFactory = Factory(Object, Class, Object)

function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
					a12, a13, a14, a15, a16, a17, a18, a19, a20)
	local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
							a12, a13, a14, a15, a16, a17, a18, a19, a20)
	if AceOO.classes then
		AceOO.classes[x] = true
	end
	return x
end
getmetatable(Class).__call = Class.new

-- @class	Mixin
-- @brief	A class to create mixin objects, which contain methods that get
-- "mixed in" to class prototypes.
--
-- @object	Mixin prototype
-- @brief	The prototype that mixin objects inherit their methods from.
--
-- @method	Mixin prototype embed
-- @brief	Mix in the methods of our object which are listed in our interface
--		 to the supplied target table.
--
-- @method	Mixin prototype init
-- @brief	Initialize the mixin object.
-- @param	newobj	 The new object we're initializing.
-- @param	interface	The interface we implement (the list of methods our
--					prototype provides which should be mixed into the target
--					table by embed).
do
	Mixin = Class()
	function Mixin:ToString()
		if self.GetLibraryVersion then
			return (self:GetLibraryVersion())
		else
			return 'Mixin'
		end
	end
	local function _Embed(state, field, target)
		field = next(state.export, field)
		if field == nil then
			return
		end

		if rawget(target, field) or (target[field] and target[field] ~= state[field]) then
			AceOO:error("Method conflict in attempt to mixin. Field %q", field)
		end

		target[field] = state[field]

		local ret,msg = pcall(_Embed, state, field, target)
		if not ret then
			-- Mix in the next method according to the defined interface.	If that
			-- fails due to a conflict, re-raise to back out the previous mixed
			-- methods.

			target[field] = nil
			AceOO:error(msg)
		end
	end
	function Mixin.prototype:embed(target)
		local mt = getmetatable(target)
		setmetatable(target, nil)
		local err, msg = pcall(_Embed, self, nil, target)
		if not err then
			setmetatable(target, mt)
			AceOO:error(msg)
			return
		end
		if type(self.embedList) == "table" then
			self.embedList[target] = true
		end
		if type(target.class) ~= "table" then
			target[self] = true
		end
		if not autoEmbed and type(self.OnManualEmbed) == "function" then
			self:OnManualEmbed(target)
		end
		setmetatable(target, mt)
	end
	
	function Mixin.prototype:activate(oldLib, oldDeactivate)
		if oldLib and oldLib.embedList then
			for target in pairs(oldLib.embedList) do
				local mt = getmetatable(target)
				setmetatable(target, nil)
				for field in pairs(oldLib.export) do
					target[field] = nil
				end
				setmetatable(target, mt)
			end
			self.embedList = oldLib.embedList
			for target in pairs(self.embedList) do
				self:embed(target)
			end
		else
			self.embedList = setmetatable(Compost and Compost:Acquire() or {}, Compost and Compost:AcquireHash('__mode', 'k') or {__mode="k"})
		end
	end
	
	function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9,
											a10, a11, a12, a13, a14, a15, a16,
											a17, a18, a19, a20)
		AceOO:argCheck(export, 2, "table")
		for k,v in pairs(export) do
			if type(k) ~= "number" then
				AceOO:error("All keys to argument #2 must be numbers.")
			elseif type(v) ~= "string" then
				AceOO:error("All values to argument #2 must be strings.")
			end
		end
		local num = table.getn(export)
		for i = 1, num do
			local v = export[i]
			export[i] = nil
			export[v] = true
		end
		table.setn(export, 0)
		local interfaces
		if a1 then
			local l = getlibrary
			interfaces = Compost and Compost:Acquire(l(a1), l(a2), l(a3), l(a4),
				l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12),
				l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20))
				or { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8),
				l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16),
				l(a17), l(a18), l(a19), l(a20) }
			for _,v in ipairs(interfaces) do
				if not v.class or not inherits(v, Interface) then
					AceOO:error("Mixins can inherit only from interfaces")
				end
			end
			local num = table.getn(interfaces)
			for i = 1, num do
				local v = interfaces[i]
				interfaces[i] = nil
				interfaces[v] = true
			end
			table.setn(interfaces, 0)
			for interface in pairs(interfaces) do
				traverseInterfaces(interface, interfaces)
			end
			for interface in pairs(interfaces) do
				for field,kind in pairs(interface.interface) do
					if kind ~= "nil" then
						local good = false
						for bit in pairs(export) do
							if bit == field then
								good = true
								break
							end
						end
						if not good then
							AceOO:error("Mixin does not fully accommodate field %q", field)
						end
					end
				end
			end
		end
		self.super = Mixin.prototype
		Mixin.super.prototype.init(self)
		self.export = export
		self.interfaces = interfaces
	end
end

-- @class Interface
-- @brief A class to create interfaces, which contain contracts that classes
--			which inherit from this must comply with.
--
-- @object Interface prototype
-- @brief	The prototype that interface objects must adhere to.
--
-- @method Interface prototype init
-- @brief	Initialize the mixin object.
-- @param	interface	The interface we contract (the hash of fields forced).
-- @param	(a1..a20)	Superinterfaces
do
	Interface = Class()
	function Interface:ToString()
		if self.GetLibraryVersion then
			return (self:GetLibraryVersion())
		else
			return 'Instance'
		end
	end
	function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
		Interface.super.prototype.init(self)
		AceOO:argCheck(interface, 2, "table")
		for k,v in pairs(interface) do
			if type(k) ~= "string" then
				AceOO:error("All keys to argument #2 must be numbers.")
			elseif type(v) ~= "string" then
				AceOO:error("All values to argument #2 must be strings.")
			elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then
				AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".')
			end
		end
		local l = getlibrary
		a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20)
		if a1 then
			self.superinterfaces = Compost and Compost:Acquire(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) or {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20}
			for k,v in ipairs(self.superinterfaces) do
				if not inherits(v, Interface) or not v.class then
					AceOO:error('Cannot provide a non-Interface to inherit from')
				end
			end
			local num = table.getn(self.superinterfaces)
			for i = 1, num do
				local v = self.superinterfaces[i]
				self.superinterfaces[i] = nil
				self.superinterfaces[v] = true
			end
			table.setn(self.superinterfaces, 0)
		end
		self.interface = interface
	end
end

-- @function Classpool
-- @brief	Obtain a read only class from our pool of classes, indexed by the
--		 superclass and mixins.
-- @param	sc		 The superclass of the class we want.
-- @param	(m1..m20)	Mixins of the class we want's objects.
-- @return A read only class from the class pool.
local Classpool
do
	local pool = setmetatable({}, {__mode = 'v'})
	local function newindex(k, v)
		AceOO:error('Attempt to modify a read-only class.')
	end
	local function protonewindex(k, v)
		AceOO:error('Attempt to modify a read-only class prototype.')
	end
	local function ts(bit)
		if type(bit) ~= "table" then
			return tostring(bit)
		elseif getmetatable(bit) and bit.__tostring then
			return tostring(bit)
		elseif type(bit.GetLibraryVersion) == "function" then
			return bit:GetLibraryVersion()
		else
			return tostring(bit)
		end
	end
	local t
	local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
		m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
		if not t then t = {} end
		if sc then if sc.uid then table.insert(t, sc.uid) else AceOO:error("%s is not an appropriate class/mixin", ts(sc)) end
		if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end
		if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end
		if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end
		if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end
		if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end
		if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end
		if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end
		if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end
		if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end
		if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end
		if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end
		if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end
		if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end
		if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end
		if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end
		if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end
		if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end
		if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end
		if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end
		if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end
		end end end end end end end end end end end end end end end end end end end end end
		table.sort(t)
		local uid = table.concat(t, '')
		for k in pairs(t) do t[k] = nil end
		table.setn(t, 0)
		return uid
	end
	local classmeta
	function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
		m10, m11, m12, m13, m14, m15, m16,
		m17, m18, m19, m20)
		local l = getlibrary
		sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20)
		if sc and sc.class then
			sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19
		end
		sc = sc or Class
		local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20)
		if not pool[key] then
			local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9,
			m10, m11, m12, m13, m14, m15, m16, m17,
			m18, m19, m20)
			if not classmeta then
				classmeta = {}
				local mt = getmetatable(class)
				for k,v in pairs(mt) do
					classmeta[k] = v
				end
				classmeta.__newindex = newindex
			end
			-- Prevent the user from adding methods to this class.
			-- NOTE: I'm not preventing modifications of existing class members,
			-- but it's likely that only a truly malicious user will be doing so.
			class.sealed = true
			setmetatable(class, classmeta)
			getmetatable(class.prototype).__newindex = protonewindex
			pool[key] = class
		end
		return pool[key]
	end
end

AceOO.Factory = Factory
AceOO.Object = Object
AceOO.Class = Class
AceOO.Mixin = Mixin
AceOO.Interface = Interface
AceOO.Classpool = Classpool
AceOO.inherits = inherits

-- Library handling bits

local function activate(self, oldLib, oldDeactivate)
	AceOO = self
	Factory = self.Factory
	Object = self.Object
	Class = self.Class
	ClassFactory.prototype = Class
	Mixin = self.Mixin
	Interface = self.Interface
	Classpool = self.Classpool
	
	if oldLib then
		self.classes = oldLib.classes
	end
	if not self.classes then
		self.classes = setmetatable({}, {__mode="k"})
	else
		for class in pairs(self.classes) do
			class.new = class_new
		end
	end
	
	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end

local function external(self, major, instance)
	if major == "Compost-2.0" then
		Compost = instance
	end
end

AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
AceOO = AceLibrary(MAJOR_VERSION)
