local constants = {
	rune_mine_interval = 90;
	-- how often a powered forge rolls for new runes

	rune_cache_max = 4;
	-- how many runes a runeforge can hold at a time
	
	rune_grades = {'Fragile', 'Shoddy', 'Ordinary', 'Pristine'};
	-- how many grades of rune quality/power there are
}
sorcery.register.runes.foreach('sorcery:generate',{},function(name,rune)
	local id = 'sorcery:rune_' .. name
	rune.image = rune.image or string.format('sorcery_rune_%s.png',name)
	rune.item = id
	minetest.register_craftitem(id, {
		description = sorcery.lib.color(rune.tone):readable():fmt(rune.name .. ' Rune');
		short_description = rune.name .. ' Rune';
		inventory_image = rune.image;
		stack_max = 1;
		groups = {
			sorcery_rune = 1;
			not_in_creative_inventory = 1;
		};
		_proto = { id = name, data = rune; };
	})
end)

local rune_set = function(stack,r)
	local m = stack:get_meta()
	local def = stack:get_definition()._proto.data
	local grade
	if r.grade then grade = r.grade
	elseif m:contains('rune_grade') then grade = m:get_int('rune_grade') end

	local qpfx = constants.rune_grades[grade]
	local title = sorcery.lib.color(def.tone):readable():fmt(string.format('%s %s Rune',qpfx,def.name))

	m:set_int('rune_grade',grade)
	m:set_string('description',title)
end

sorcery.amulet = {}
sorcery.amulet.setrune = function(stack,rune)
	local m = stack:get_meta()
	if rune then
		local rp = rune:get_definition()._proto
		local rg = rune:get_meta():get_int('rune_grade')
		m:set_string('amulet_rune', rp.id)
		m:set_int('amulet_rune_grade', rg)
		local spell = sorcery.amulet.getspell(stack)
		if not spell then return nil end

		local name = string.format('Amulet of %s', spell.name)

		m:set_string('description', sorcery.lib.ui.tooltip {
			title = name;
			color = spell.tone;
			desc = spell.desc;
		})
	else
		m:set_string('description','')
		m:set_string('amulet_rune','')
		m:set_string('amulet_rune_grade','')
	end
	return stack
end

sorcery.amulet.getrune = function(stack)
	local m = stack:get_meta()
	if not m:contains('amulet_rune') then return nil end
	local rune = m:get_string('amulet_rune')
	local grade = m:get_int('amulet_rune_grade')
	local rs = ItemStack(sorcery.data.runes[rune].item)
	rune_set(rs, {grade = grade})
	return rs
end

sorcery.amulet.getspell = function(stack)
	local m = stack:get_meta()
	local proto = stack:get_definition()._sorcery.amulet
	local rune = m:get_string('amulet_rune')
	local rd = sorcery.data.runes[rune]
	local spell = rd.amulets[proto.base]
	if not spell then return nil end
	local title,desc,cast = spell.name, spell.desc, spell.cast

	if proto.frame and spell.frame and spell.frame[proto.frame] then
		local sp = spell.frame[proto.frame]
		title = sp.name or title
		desc = sp.desc or desc
		cast = sp.desc or cast
	end
	
	return {
		rune = rune;
		spell = spell;
		name = title;
		desc = desc;
		cast = cast;
		tone = sorcery.lib.color(rd.tone);
	}
end


local runeforge_update = function(pos,time)
	local m = minetest.get_meta(pos)
	local i = m:get_inventory()
	local l = sorcery.ley.netcaps(pos,time or 1)

	local pow_min = l.self.powerdraw >= l.self.minpower
	local pow_max = l.self.powerdraw >= l.self.maxpower

	if time and pow_min then -- roll for runes
		local rolls = math.floor(time/constants.rune_mine_interval)
		local newrunes = {}
		for _=1,rolls do
			local choices = {}
			for name,rune in pairs(sorcery.data.runes) do
				if rune.minpower*time <= l.self.powerdraw and math.random(rune.rarity) == 1 then
					local n = ItemStack(rune.item)
					choices[#choices + 1] = n
				end
			end
			if #choices > 0 then newrunes[#newrunes + 1] = choices[math.random(#choices)] end
		end

		print('rolled for runes, got', dump(newrunes))
		for _,r in pairs(newrunes) do
			if i:room_for_item('cache',r) then
				local qual = math.random(#constants.rune_grades)
				rune_set(r,{grade = qual})
				i:add_item('cache',r)
			end
		end
	end

	local spec = string.format([[
		formspec_version[3] size[10.25,8] real_coordinates[true]
		list[context;cache;%f,0.25;%u,1;]
		list[context;amulet;3.40,1.50;1,1;]
		list[context;active;5.90,1.50;1,1;]
		list[current_player;main;0.25,3;8,4;]

		image[0.25,0.50;1,1;sorcery_statlamp_%s.png]
	]], (10.5 - constants.rune_cache_max*1.25)/2, constants.rune_cache_max,
	    pow_max and 'green' or (pow_min and 'yellow') or 'off')
	
	m:set_string('formspec',spec)
	return true
end

local rfbox = {
	type = 'fixed';
	fixed = {
		-0.5, -0.5, -0.5;
		 0.5,  0.1,  0.5;
	};
};
minetest.register_node('sorcery:runeforge', {
	description = 'Rune Forge';
	drawtype = 'mesh';
	mesh = 'sorcery-runeforge.obj';
	sunlight_propagates = true;
	paramtype = 'light';
	paramtype2 = 'facedir';
	selection_box = rfbox;
	collision_box = rfbox;
	groups = {
		choppy = 2;
		oddly_breakable_by_hand = 2;
		sorcery_magitech = 1;
		sorcery_tech = 1;
		sorcery_ley_device = 1;
	};
	tiles = {
		'default_diamond_block.png';
		'default_tin_block.png';
		'sorcery_metal_iridium_shiny.png';
		'sorcery_metal_vidrium_shiny.png';
		'default_copper_block.png';
	};
	_sorcery = {
		ley = {
			mode = 'consume';
			affinity = {'praxic'};
			power = function(pos,time)
				local max,min = 0
				for _,r in pairs(sorcery.data.runes) do
					if r.minpower > max then max = r.minpower end
					if min == nil or r.minpower < min then min = r.minpower end
				end
				return min*time,max*time
			end;
		};
		on_leychange = runeforge_update;
		recipe = {
			note = 'Periodically creates runes when sufficiently powered and can be used to imbue them into an amulet, giving it a powerful magical effect';
		};
	};
	on_construct = function(pos)
		local m = minetest.get_meta(pos)
		local i = m:get_inventory()
		i:set_size('cache',constants.rune_cache_max)
		i:set_size('amulet',1)
		i:set_size('active',1)
		m:set_string('infotext','Rune Forge')
		runeforge_update(pos)
		minetest.get_node_timer(pos):start(constants.rune_mine_interval)
	end;
	after_dig_node = sorcery.lib.node.purge_only {'amulet'};
	on_timer = runeforge_update;
	on_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
		local inv = minetest.get_meta(pos):get_inventory()
		if fl == 'active' then
			inv:set_stack('amulet',1,sorcery.amulet.setrune(inv:get_stack('amulet',1)))
		elseif tl == 'active' then
			inv:set_stack('amulet',1,sorcery.amulet.setrune(inv:get_stack('amulet',1), inv:get_stack(tl,ti)))
		end
	end;
	on_metadata_inventory_put = function(pos, list, idx, stack, user)
		if list == 'amulet' then
			local inv = minetest.get_meta(pos):get_inventory()
			inv:set_stack('active',1,ItemStack(sorcery.amulet.getrune(stack)))
		end
	end;
	on_metadata_inventory_take = function(pos, list, idx, stack, user)
		if list == 'amulet' then
			minetest.get_meta(pos):get_inventory():set_stack('active',1,ItemStack())
		end
	end;
	allow_metadata_inventory_put = function(pos,list,idx,stack,user)
		if list == 'amulet' then
			if minetest.get_item_group(stack:get_name(), 'sorcery_amulet') ~= 0 then
				return 1
			end
		end
		return 0
	end;
	allow_metadata_inventory_take = function(pos,list,idx,stack,user)
		if list == 'amulet' then return 1 end
		return 0
	end;
	allow_metadata_inventory_move = function(pos, fl,fi, tl,ti, count, user)
		if fl == 'cache' then
			if tl == 'cache' then return 1 end
			if tl == 'active' then
				local inv = minetest.get_meta(pos):get_inventory()
				if not inv:is_empty('amulet') then
					local amulet = inv:get_stack('amulet',1)
					local rune = inv:get_stack(fl,fi)
					if sorcery.data.runes[rune:get_definition()._proto.id].amulets[amulet:get_definition()._sorcery.amulet.base] then
						return 1
					end
				end
			end
		end
		if fl == 'active' then
			if tl == 'cache' then return 1 end
		end
		return 0
	end;
})

do local m = sorcery.data.metals
	-- temporary recipe until a fancier multi-part crafting path can be come up with
	-- TODO: better than this
	minetest.register_craft {
		output = 'sorcery:runeforge';
		recipe = {
			{'default:copper_ingot',m.vidrium.parts.block,'default:copper_ingot'};
			{'default:diamond',m.iridium.parts.ingot,'default:diamond'};
			{'default:tin_ingot','sorcery:core_syncretic','default:tin_ingot'};
		};
	}
end
