-- marshal provides low-level data marshalling capabilities, but
-- for various reasons it's handy to have a higher-level interface
-- that allows us to register and retrieve data structures from
-- node or item metadata. this library provides that interface.

-- objstore consists of a single function that returns a structure
-- store. this structure-store should be saved to a variable, and
-- called upon to register or modify attributes. every node or item
-- that contains metadata contains a list of attached structures,
-- which themselves contain a list of attached records. this allows
-- structures to be extended at will without disrupting existing
-- metadata storage.

-- WARNING: this is likely incompatible with the StorageRef backend,
-- which uses json -- need to figure out how to make the marshalling
-- format compatible

local lib = sorcery.lib
local m = lib.marshal

local recpack, recunpack = m.transcoder {
	recs = m.g.array(m.g.struct {
		id = m.t.str;
		data = m.t.blob;
	});
}
return function() {
	return {
		structs = {};
		register = function(self,id)
			local struc = {
				id = id;
				records = {};
				register_record = function(self,def)
					if not records[def.id] then
						records[def.id] = def
						return true
					else
						return false
					end
				end;
				_encode = function(self,data)
					local obj = {}
					for k,v in pairs(records) do
						local t = m.transcoder(v.fields)
						obj[#obj + 1] = {
							id = k;
							data = t(data[k]);
						}
					end
					return recpack(obj)
				end;
				_decode = function(self,str)
				end;
				_meta = function(self,store)
					return {
						store = store;
						_getall = function(iself)
							return self:_decode(lib.str.meta_dearmor(iself.store:get_string('objstore:'..id),true))
						end;
						_save = function(iself,obj)
							iself.store:set_string('objstore:'..id, lib.str.meta_armor(self:_encode(obj)))
						end;
						get = function(self,rec)
							local recs = recunpack(lib.str.meta_dearmor(self.store:get_string('objstore:'..id),true))
							for _,r in pairs(recs) do
								if r.id == rec then
									return r
								end
							end
							return nil
						end;
						merge = function(self,obj)
							-- obj should be a table of structure {
							--		record_id = { record_field = value; }
							-- }, e.g.:
							--   sorcery.store.enchantment.merge {
							--		spells = {
							--			[4] = { id = 'dowse, boost = 15 }
							--		}
							--	}
							local recs = self:_getall()
							for rec,flds in pairs(obj) do
								for k,v in pairs(flds) do
									if not recs[rec] then recs[rec] = {} end
									recs[rec][k] = v
								end
							end
							self:_save(recs)
						end;
					}
				end;
				item = function(self,stack)
					return self:_meta(stack:get_meta())
				end;
				node = function(self,pos)
					return self:_meta(minetest.get_meta(pos))
				end;
				user = function(self,u)
					if type(u) == 'string' then
						u = minetest.get_player_by_name(u)
					end
					return self:_meta(u:get_meta())
				end;
			}
			self.structs[id] = struc;
			return struc;
		end;
	}
}
