local k=import("\046\046\047\097\115\115\105\103\110")local L=import("\046\046\047\073\110\115\116\097\110\099\101\080\114\111\112\101\114\116\121")local r=import("\046\046\047\083\105\103\110\097\108")local T=import("\046\046\047\116\121\112\101\075\101\121")local function a(k)local L=getmetatable(k)return L and L.instance~=nil end local D={}D.options={creatable=false}D.name="\073\110\115\116\097\110\099\101"D.properties={}D.properties.Name=L.normal({getDefault=function(k)return(getmetatable(k)).class.name end})D.properties.ClassName=L.readOnly({getDefault=function(k)return(getmetatable(k)).class.name end})D.properties.AncestryChanged=L.readOnly({getDefault=function()return r.new()end})D.properties.Changed=L.readOnly({getDefault=function()return r.new()end})D.properties.ChildAdded=L.readOnly({getDefault=r.new})D.properties.ChildRemoved=L.readOnly({getDefault=r.new})D.properties.Parent=L.normal({set=function(k,L,r)local T=(getmetatable(k)).instance if T.destroyed then error("\065\116\116\101\109\112\116 \116\111 \115\101\116 \112\097\114\101\110\116 \097\102\116\101\114 \098\101\105\110\103 \100\101\115\116\114\111\121\101\100\033")end if T.properties.Parent==r then return end if r~=nil and not a(r)then error(string.format("\067\097\110\'\116 \115\101\116 \080\097\114\101\110\116 \116\111 \037\113\059 \080\097\114\101\110\116 \109\117\115\116 \098\101 \097\110 \073\110\115\116\097\110\099\101\033"),tostring(r))end if T.properties.Parent~=nil then(getmetatable(T.properties.Parent)).instance.children[k]=nil T.properties.Parent.ChildRemoved:Fire(k)end T.properties.Parent=r if r~=nil then(getmetatable(r)).instance.children[k]=true r.ChildAdded:Fire(k)end k:_PropagateAncestryChanged(k,r)end})D.prototype={}function D.prototype.ClearAllChildren(k)local L=(getmetatable(k)).instance.children for k in pairs(L)do k:Destroy()end end function D.prototype.FindFirstAncestor(L,k)local r=L.Parent while r do if r.Name==k then return r end r=r.Parent end end function D.prototype.FindFirstAncestorOfClass(L,k)local r=L.Parent while r do if r.ClassName==k then return r end r=r.Parent end end function D.prototype.FindFirstAncestorWhichIsA(L,k)local r=L.Parent while r do if r:IsA(k)then return r end r=r.Parent end end function D.prototype.FindFirstChild(L,k)local r=(getmetatable(L)).instance.children for L in pairs(r)do if L.Name==k then return L end end return nil end function D.prototype.FindFirstChildOfClass(L,k)local r=(getmetatable(L)).instance.children for L in pairs(r)do if L.ClassName==k then return L end end return nil end function D.prototype.FindFirstChildWhichIsA(L,k)local r=(getmetatable(L)).instance.children for L in pairs(r)do if L:IsA(k)then return L end end return nil end function D.prototype.GetChildren(k)local L=(getmetatable(k)).instance.children local r={}for k in pairs(L)do table.insert(r,k)end return r end function D.prototype.GetDescendants(k)local L={}local r={}local T=k while T do local k=T:GetChildren()for k,T in pairs(k)do r[#r+1]=T L[#L+1]=T end T=L[#L]L[#L]=nil end return r end function D.prototype.IsA(L,k)local r=(getmetatable(L)).class while r~=nil do if r.name==k then return true end r=r.super end return false end function D.prototype.IsDescendantOf(L,k)local r=L repeat r=r.Parent until r==nil or r==k return r==k end function D.prototype.Destroy(k)k:ClearAllChildren()if k.Parent~=nil then k.Parent=nil end k:_DisconnectAllChangedListeners();(getmetatable(k)).instance.destroyed=true end function D.prototype.GetPropertyChangedSignal(L,k)local T=(getmetatable(L)).class.properties local a=(getmetatable(L)).instance.propertySignals local D=a[k]if not D then assert(T[k],k.." \105\115 \110\111\116 \097 \118\097\108\105\100 \112\114\111\112\101\114\116\121 \110\097\109\101\046")D=r.new()a[k]=D end return D end function D.prototype.GetFullName(k)local L=k.Name local r=k.Parent while r and(getmetatable(r)).class.name~="\068\097\116\097\077\111\100\101\108"do L=r.Name..("\046"..L)r=r.Parent end return L end function D.prototype.WaitForChild(r,k,L)return r:FindFirstChild(k)end function D.prototype._DisconnectAllChangedListeners(k)local L=(getmetatable(k)).instance.propertySignals for k,L in pairs(L)do L:_DisconnectAllListeners()end k.Changed:_DisconnectAllListeners()end function D.prototype._PropagateAncestryChanged(r,k,L)r.AncestryChanged:Fire(k,L)local T=(getmetatable(r)).instance.children for r in pairs(T)do r:_PropagateAncestryChanged(k,L)end end D.metatable={}D.metatable[T]="\073\110\115\116\097\110\099\101"function D.metatable.__index(k,L)local r=(getmetatable(k)).class if r.properties[L]then return r.properties[L].get(k,L)end if r.prototype[L]then return r.prototype[L]end local T=k:FindFirstChild(L)if T then return T end error(string.format("\037\113 \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \037\115",tostring(L),k.ClassName),2)end function D.metatable.__newindex(k,L,r)local T=(getmetatable(k)).class if T.properties[L]then T.properties[L].set(k,L,r)k.Changed:Fire(L)local a=(getmetatable(k)).instance.propertySignals[L]if a then a:Fire()end return end error(string.format("\037\113 \105\115 \110\111\116 \097 \118\097\108\105\100 \109\101\109\098\101\114 \111\102 \037\115",tostring(L),k.ClassName),2)end function D.metatable.__tostring(k)return k.Name end function D.new(L,...)local r={destroyed=false,properties={};propertySignals={},children={}}local T=newproxy(true)k(getmetatable(T),L.metatable);(getmetatable(T)).instance=r;(getmetatable(T)).class=L for k,L in pairs(L.properties)do r.properties[k]=L.getDefault(T)end L:init(T,...)return T end function D.init(L,k,...) end function D.extend(T,L,r)assert(type(L)=="\115\116\114\105\110\103","\069\120\112\101\099\116\101\100 \115\116\114\105\110\103 \'\110\097\109\101\' \097\115 \097\114\103\117\109\101\110\116 \035\049\046")assert(type(r)=="\116\097\098\108\101"or r==nil,"\069\120\112\101\099\116\101\100 \111\112\116\105\111\110\097\108 \116\097\098\108\101 \'\111\112\116\105\111\110\115\' \097\115 \097\114\103\117\109\101\110\116 \035\050\046")local a=k({},T)a.name=L a.super=T a.properties=k({},T.properties)a.prototype=k({},T.prototype)a.metatable=k({},T.metatable)a.options=k({},T.options,r or{})return a end return D
--[[
	Provides a base implementation for all Instances in Lemur.

	When adding a new instance, you can define:
	* properties, using helpers in InstanceProperty
	* prototype, used for defining methods and static values
	* init, called by the class's constructor
]]

local assign = import("../assign")
local InstanceProperty = import("../InstanceProperty")
local Signal = import("../Signal")
local typeKey = import("../typeKey")

local function isInstance(value)
	local metatable = getmetatable(value)

	return metatable and metatable.instance ~= nil
end

local BaseInstance = {}

BaseInstance.options = {
	creatable = false,
}

BaseInstance.name = "Instance"

BaseInstance.properties = {}

BaseInstance.properties.Name = InstanceProperty.normal({
	getDefault = function(self)
		return getmetatable(self).class.name
	end,
})

BaseInstance.properties.ClassName = InstanceProperty.readOnly({
	getDefault = function(self)
		return getmetatable(self).class.name
	end,
})

BaseInstance.properties.AncestryChanged = InstanceProperty.readOnly({
	getDefault = function()
		return Signal.new()
	end,
})

BaseInstance.properties.Changed = InstanceProperty.readOnly({
	getDefault = function()
		return Signal.new()
	end,
})

BaseInstance.properties.ChildAdded = InstanceProperty.readOnly({
	getDefault = Signal.new
})

BaseInstance.properties.ChildRemoved = InstanceProperty.readOnly({
	getDefault = Signal.new
})

BaseInstance.properties.Parent = InstanceProperty.normal({
	set = function(self, key, value)
		local instance = getmetatable(self).instance

		if instance.destroyed then
			error("Attempt to set parent after being destroyed!")
		end

		if instance.properties.Parent == value then
			return
		end

		if value ~= nil and not isInstance(value) then
			error(string.format("Can't set Parent to %q; Parent must be an Instance!"), tostring(value))
		end

		if instance.properties.Parent ~= nil then
			getmetatable(instance.properties.Parent).instance.children[self] = nil
			instance.properties.Parent.ChildRemoved:Fire(self)
		end

		instance.properties.Parent = value

		if value ~= nil then
			getmetatable(value).instance.children[self] = true
			value.ChildAdded:Fire(self)
		end

		self:_PropagateAncestryChanged(self, value)
	end,
})

BaseInstance.prototype = {}

function BaseInstance.prototype:ClearAllChildren()
	local children = getmetatable(self).instance.children

	for child in pairs(children) do
		child:Destroy()
	end
end

function BaseInstance.prototype:FindFirstAncestor(name)
	local level = self.Parent

	while level do
		if level.Name == name then
			return level
		end

		level = level.Parent
	end
end

function BaseInstance.prototype:FindFirstAncestorOfClass(name)
	local level = self.Parent

	while level do
		if level.ClassName == name then
			return level
		end

		level = level.Parent
	end
end

function BaseInstance.prototype:FindFirstAncestorWhichIsA(className)
	local level = self.Parent

	while level do
		if level:IsA(className) then
			return level
		end

		level = level.Parent
	end
end

function BaseInstance.prototype:FindFirstChild(name)
	local children = getmetatable(self).instance.children

	-- Search for existing children
	-- This is a set stored by child instead of by name, since names are not unique.
	for child in pairs(children) do
		if child.Name == name then
			return child
		end
	end

	return nil
end

function BaseInstance.prototype:FindFirstChildOfClass(className)
	local children = getmetatable(self).instance.children

	-- Search for existing children
	-- This is a set stored by child instead of by name, since names are not unique.
	for child in pairs(children) do
		if child.ClassName == className then
			return child
		end
	end

	return nil
end

function BaseInstance.prototype:FindFirstChildWhichIsA(className)
	local children = getmetatable(self).instance.children

	-- Search for existing children
	-- This is a set stored by child instead of by name, since names are not unique.
	for child in pairs(children) do
		if child:IsA(className) then
			return child
		end
	end

	return nil
end

function BaseInstance.prototype:GetChildren()
	local children = getmetatable(self).instance.children
	local result = {}

	for child in pairs(children) do
		table.insert(result, child)
	end

	return result
end

function BaseInstance.prototype:GetDescendants()
	local stack = {}
	local descendants = {}
	local current = self

	while current do
		local children = current:GetChildren()

		for _, child in pairs(children) do
			descendants[#descendants + 1] = child
			stack[#stack + 1] = child
		end

		current = stack[#stack]
		stack[#stack] = nil
	end

	return descendants
end

function BaseInstance.prototype:IsA(className)
	local currentClass = getmetatable(self).class

	while currentClass ~= nil do
		if currentClass.name == className then
			return true
		end

		currentClass = currentClass.super
	end

	return false
end

function BaseInstance.prototype:IsDescendantOf(object)
	local parent = self

	repeat
		parent = parent.Parent
	until parent == nil or parent == object

	return parent == object
end

function BaseInstance.prototype:Destroy()
	self:ClearAllChildren()

	if self.Parent ~= nil then
		self.Parent = nil
	end

	self:_DisconnectAllChangedListeners()

	getmetatable(self).instance.destroyed = true
end

function BaseInstance.prototype:GetPropertyChangedSignal(key)
	local properties = getmetatable(self).class.properties
	local propertySignals = getmetatable(self).instance.propertySignals

	local listener = propertySignals[key]

	if not listener then
		assert(properties[key], key .. " is not a valid property name.")

		listener = Signal.new()
		propertySignals[key] = listener
	end

	return listener
end

function BaseInstance.prototype:GetFullName()
	local name = self.Name
	local level = self.Parent

	while level and getmetatable(level).class.name ~= "DataModel" do
		name = level.Name .. "." .. name
		level = level.Parent
	end

	return name
end

function BaseInstance.prototype:WaitForChild(name, delay)
	return self:FindFirstChild(name)
end

function BaseInstance.prototype:_DisconnectAllChangedListeners()
	local propertySignals = getmetatable(self).instance.propertySignals

	for _, signal in pairs(propertySignals) do
		signal:_DisconnectAllListeners()
	end

	self.Changed:_DisconnectAllListeners()
end

function BaseInstance.prototype:_PropagateAncestryChanged(instance, parent)
	self.AncestryChanged:Fire(instance, parent)

	local children = getmetatable(self).instance.children

	for child in pairs(children) do
		child:_PropagateAncestryChanged(instance, parent)
	end
end

BaseInstance.metatable = {}
BaseInstance.metatable[typeKey] = "Instance"

function BaseInstance.metatable.__index(self, key)
	local class = getmetatable(self).class

	if class.properties[key] then
		return class.properties[key].get(self, key)
	end

	if class.prototype[key] then
		return class.prototype[key]
	end

	local object = self:FindFirstChild(key)
	if object then
		return object
	end

	error(string.format("%q is not a valid member of %s", tostring(key), self.ClassName), 2)
end

function BaseInstance.metatable.__newindex(self, key, value)
	local class = getmetatable(self).class

	if class.properties[key] then
		class.properties[key].set(self, key, value)

		self.Changed:Fire(key)

		local propertyChangedSignal = getmetatable(self).instance.propertySignals[key]

		if propertyChangedSignal then
			propertyChangedSignal:Fire()
		end

		return
	end

	error(string.format("%q is not a valid member of %s", tostring(key), self.ClassName), 2)
end

function BaseInstance.metatable:__tostring()
	return self.Name
end

function BaseInstance:new(...)
	local internalInstance = {
		destroyed = false,
		properties = {},
		propertySignals = {},
		children = {},
	}

	local instance = newproxy(true)

	-- Because userdata have a fixed metatable, merge values onto it.
	assign(getmetatable(instance), self.metatable)
	getmetatable(instance).instance = internalInstance
	getmetatable(instance).class = self

	for key, property in pairs(self.properties) do
		internalInstance.properties[key] = property.getDefault(instance)
	end

	self:init(instance, ...)

	return instance
end

function BaseInstance:init(instance, ...)
end

--[[
	Create a new instance class with the given name.
]]
function BaseInstance:extend(name, options)
	assert(type(name) == "string", "Expected string 'name' as argument #1.")
	assert(type(options) == "table" or options == nil, "Expected optional table 'options' as argument #2.")

	local newClass = assign({}, self)

	newClass.name = name
	newClass.super = self

	newClass.properties = assign({}, self.properties)
	newClass.prototype = assign({}, self.prototype)
	newClass.metatable = assign({}, self.metatable)
	newClass.options = assign({}, self.options, options or {})

	return newClass
end

return BaseInstance