local i=require(script.Parent.dLib)local k=i.import("\085\116\105\108")local b={}function b.new(i)local b=i or{}local O={}local a={}a.__index=function(i,k)return b[k]or i[k]end function a.length(i)return k.tableLength(b)end function a.get(O,i)if typeof(i)=="\110\117\109\098\101\114"then return b[i]end return i==true and b or k.treePath(b,i,"\046")end function a.listen(b,i)assert(typeof(i)=="\102\117\110\099\116\105\111\110","\067\097\108\108\098\097\099\107 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")local a=k.randomString(8)O[a]=i return a end function a.unlisten(k,i)assert(O[i],"\083\116\097\116\101 \108\105\115\116\101\110\101\114 \100\111\101\115 \110\111\116 \101\120\105\115\116")O[i]=nil end function a.pushUpdates(k,i)for k,O in pairs(O)do pcall(O,i,b)end end function a.push(a,i,O)local U=k.extend({},b)local m=O~=nil and i or#b+1 b[m]=O~=nil and O or i a:pushUpdates(U)return m end function a.reset(i)b={}end function a.set(a,i,O)local U=k.extend({},b)if typeof(i)=="\102\117\110\099\116\105\111\110"then b=i(b)or b elseif typeof(i)=="\116\097\098\108\101"then for i,k in pairs(i)do b[i]=k end elseif typeof(i)=="\115\116\114\105\110\103"then local a=k.split(i,"\046",true)local U=b for i,k in ipairs(a)do local b=tonumber(k)if b then k=b end if U[k]and i~=#a then U=U[k]elseif i==#a then U[k]=O else break end end else return end a:pushUpdates(U)end function a.remove(O,i)local a=k.extend({},b)local U=k.split(i,"\046",true)local m=b for i,k in ipairs(U)do local b=tonumber(k)if b then k=b end if m[k]and i~=#U then m=m[k]elseif i==#U then m[k]=nil else break end end O:pushUpdates(a)end return setmetatable({},{__index=a})end return b


local dLib = require(script.Parent.dLib)
local Util = dLib.import('Util')

local State = {}

function State.new(initialState)
	local _context = initialState or {}
	local _listeners = {}
	
	local self = {}
	--self.__index = self
	
	self.__index = function(self, key)
		return _context[key] or self[key]
	end
	
	--[[local self = setmetatable({}, {
		__index = function(tbl, k)
			return rawget(_context, k)
		end;
		
		__newindex = function(self, k, v)
			rawset(_context, k, v)
		end;
	})]]
	
	function self:length()
		return Util.tableLength(_context)
	end
	
	function self:get(path) -- Todo: support for nested tables
		if typeof(path) == 'number' then
			return _context[path] -- Return the index, if exists
		end
		
		return path == true and _context or Util.treePath(_context, path, '.')
		--return key and _context[key] or (key == true and _context)
	end
	
	function self:listen(fn)
		assert(typeof(fn) == 'function', 'Callback argument must be a function')
		
		local id = Util.randomString(8) -- Unique reference ID used for unsubscribing
		
		_listeners[id] = fn
		
		return id
	end
	
	function self:unlisten(id)
		assert(_listeners[id], 'State listener does not exist')
		
		_listeners[id] = nil
	end
	
	function self:pushUpdates(prevState)
		for _, callback in pairs(_listeners) do
			pcall(callback, prevState, _context)
		end
	end
	
	function self:push(a, b)
		local prevState = Util.extend({}, _context)
		local key = b ~= nil and a or #_context + 1
		
		_context[key] = b ~= nil and b or a
		
		self:pushUpdates(prevState)
		
		return key
	end
	
	function self:reset()
		_context = {}
	end
	
	function self:set(newState, value)
		local prevState = Util.extend({}, _context) -- Create a local copy
		
		if typeof(newState) == 'function' then
			_context = newState(_context) or _context
		elseif typeof(newState) == 'table' then
			for k, v in pairs(newState) do
				_context[k] = v
			end
		elseif typeof(newState) == 'string' then
			local path = Util.split(newState, '.', true)
			local res = _context
			
			-- Go through nest until the last nest level is reached
			for i, childName in ipairs(path) do
				local numberIndex = tonumber(childName)
				
				if numberIndex then
					childName = numberIndex
				end
				
				if res[childName] and i ~= #path then
					res = res[childName]
				elseif i == #path then
					-- Change the value if end of the path was reached
					res[childName] = value
				else
					break
				end
			end
		else
			return
		end
		
		self:pushUpdates(prevState)
	end
	
	function self:remove(path)
		local prevState = Util.extend({}, _context) -- Create a local copy
		
		local treePath = Util.split(path, '.', true)
		local res = _context
		
		-- Dig through nest until the last nest level is reached
		for i, childName in ipairs(treePath) do
			local numberIndex = tonumber(childName)
			
			if numberIndex then
				childName = numberIndex
			end
			
			if res[childName] and i ~= #treePath then
				res = res[childName]
			elseif i == #treePath then
				-- Remove the value if end of the path was reached
				res[childName] = nil
			else
				break
			end
		end
		
		self:pushUpdates(prevState)
	end
	
	return setmetatable({}, { __index = self })
end

return State
