--[[

	TechAge
	=======

	Copyright (C) 2019-2020 Joachim Stolberg

	AGPL v3
	See LICENSE.txt for more information

	Cooking routines for furnace

]]--

-- for lazy programmers
local P = minetest.string_to_pos
local M = minetest.get_meta
local S = techage.S

local range = techage.in_range

local Recipes = {}     -- registered recipes {output = {recipe, ...},}
local Ingredients = {} -- {{input = output},
local KeyList = {}     -- index to Recipes key translation

techage.furnace = {}


local function all_ingredients_available(output, ingr)
	if Recipes[output] then
		for idx,recipe in ipairs(Recipes[output]) do
			local not_in_list = false
			for _,item in ipairs(recipe.input) do
				if not techage.in_list(ingr, item) then
					not_in_list = true
				end
			end
			if not_in_list == false then
				return idx -- list number of the recipe
			end
		end
	end
end

-- Return a list with all outputs of the given list of ingredients
local function get_recipes(ingr)
	if #ingr > 0 then
		local tbl = {}
		for _,item in ipairs(ingr) do
			if Ingredients[item] then
				for _,output in ipairs(Ingredients[item]) do
					if all_ingredients_available(output, ingr) then
						techage.add_to_set(tbl, output)
					end
				end
			end
		end
		return tbl
	else
		return KeyList
	end
end

function techage.furnace.get_ingredients(pos)
	local inv = M(pos):get_inventory()
	local tbl = {}
	for _,stack in ipairs(inv:get_list('src')) do
		if stack:get_name() ~= "" then
			tbl[#tbl+1] = stack:get_name()
		end
	end
	return tbl
end

local function remove_item_from_list(list, item)
	for _,stack in ipairs(list) do
		if stack:get_name() == item then
			stack:set_count(stack:get_count() - 1)
			return true
		end
	end
	return false
end

-- move recipe src items to output inventory
local function process(inv, recipe, output)
	-- check dst inv
	local stack = ItemStack(output)
	stack:set_count(recipe.number)
	if not inv:room_for_item("dst", stack) then
		return techage.BLOCKED
	end
	-- handle waste
	if recipe.waste then
		if not inv:room_for_item("dst", ItemStack(recipe.waste)) then
			return techage.BLOCKED
		end
	end
	-- remove items
	local list = inv:get_list("src")
	for _,item in ipairs(recipe.input) do
		if not remove_item_from_list(list, item) then
			return techage.STANDBY
		end
	end
	-- store changes on scr
	inv:set_list("src", list)
	-- add output to dst
	inv:add_item("dst", stack)
	-- add waste to dst
	if recipe.waste then
		local leftover = inv:add_item("dst", ItemStack(recipe.waste))
		if leftover:get_count() > 0 then
			inv:add_item("src", leftover)
			return techage.BLOCKED
		end
	end
	return techage.RUNNING
end

function techage.furnace.check_if_worth_to_wakeup(pos, nvm)
	local inv = M(pos):get_inventory()
	if not nvm.output or not nvm.num_recipe then
		return false
	end
	local recipe = Recipes[nvm.output] and Recipes[nvm.output][nvm.num_recipe]
	if not recipe then
		return false
	end
	-- check dst inv
	local stack = ItemStack(nvm.output)
	stack:set_count(recipe.number)
	if not inv:room_for_item("dst", stack) then
		return false
	end
	-- check src inv
	local list = inv:get_list("src")
	for _,item in ipairs(recipe.input) do
		if not remove_item_from_list(list, item) then
			return false
		end
	end
	return true
end

function techage.furnace.smelting(pos, nvm, elapsed)
	local inv = M(pos):get_inventory()
	local state = techage.RUNNING
	if inv and not inv:is_empty("src") then
		if not nvm.output or not nvm.num_recipe then
			return techage.FAULT, "recipe error"
		end
		local recipe = Recipes[nvm.output] and Recipes[nvm.output][nvm.num_recipe]
		if not recipe then
			return techage.FAULT, "recipe error"
		end

		elapsed = elapsed + (nvm.leftover or 0)
		while elapsed >= recipe.time do
			state = process(inv, recipe, nvm.output)
			if state ~= techage.RUNNING then
				return state
			end
			elapsed = elapsed - recipe.time
		end
		nvm.leftover = elapsed
		if recipe.time >= 10 then
			nvm.item_percent = math.min(math.floor((nvm.leftover * 100.0) / recipe.time), 100)
		else
			nvm.item_percent = 100
		end
		return state
	end
	return techage.STANDBY
end

function techage.furnace.get_output(nvm, ingr, idx)
	local tbl = get_recipes(ingr)
	idx = range(idx, 1, #tbl)
	nvm.output = tbl[idx] or tbl[1]
	nvm.num_recipe = all_ingredients_available(nvm.output, ingr)
	return nvm.output
end

function techage.furnace.get_num_recipes(ingr)
	return #get_recipes(ingr)
end

function techage.furnace.reset_cooking(nvm)
	nvm.leftover = 0
	nvm.item_percent = 0
end


techage.recipes.register_craft_type("ta3_melting", {
	description = S("TA3 Melting"),
	icon = "techage_concrete.png^techage_appl_furnace.png^techage_frame_ta3.png",
	width = 2,
	height = 2,
})

function techage.furnace.register_recipe(recipe)
	local words = string.split(recipe.output, " ")
	local output = words[1]
	local number = tonumber(words[2] or 1)
	table.insert(KeyList, output)
	--print(recipe.output, dump(recipe.recipe))
	if not Recipes[output] then
		Recipes[output] = {}
	end
	table.insert(Recipes[output], {
		input = recipe.recipe,
		waste = recipe.waste,
		number = number,
		time = math.max((recipe.time or 3) * number, 2),
	})
	for _,item in ipairs(recipe.recipe) do
		if Ingredients[item] then
			techage.add_to_set(Ingredients[item], output)
		else
			Ingredients[item] = {output}
		end
	end

	recipe.items = recipe.recipe
	recipe.type = "ta3_melting"
	techage.recipes.register_craft(recipe)
end