-- IndustrialTest
-- Copyright (C) 2024 mrkubax10

-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.

-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.

-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

local S=minetest.get_translator("industrialtest")
industrialtest.InductionFurnace=table.copy(industrialtest.ActivatedElectricMachine)
industrialtest.internal.unpackTableInto(industrialtest.InductionFurnace,{
	name="industrialtest:induction_furnace",
	description=S("Induction Furnace"),
	tiles={
		"industrialtest_advanced_machine_block.png",
		"industrialtest_advanced_machine_block.png",
		"industrialtest_advanced_machine_block.png",
		"industrialtest_advanced_machine_block.png",
		"industrialtest_advanced_machine_block.png",
		"industrialtest_advanced_machine_block.png^industrialtest_electric_furnace_front.png"
	},
	sounds="metal",
	requiresWrench=true,
	facedir=true,
	storageLists={
		"src",
		"dst",
		"upgrades",
		"powerStorage"
	},
	powerLists={
		{
			list="powerStorage",
			direction="i"
		}
	},
	active={
		tiles={
			"industrialtest_advanced_machine_block.png",
			"industrialtest_advanced_machine_block.png",
			"industrialtest_advanced_machine_block.png",
			"industrialtest_advanced_machine_block.png",
			"industrialtest_advanced_machine_block.png",
			"industrialtest_advanced_machine_block.png^industrialtest_electric_furnace_front_active.png"
		}
	},
	capacity=industrialtest.api.mvPowerFlow*2,
	flow=industrialtest.api.mvPowerFlow,
	hasPowerInput=true,
	ioConfig="iiiiii",
	_opPower=60,
	_efficiency=0.5
})

function industrialtest.InductionFurnace.onConstruct(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	inv:set_size("src",2)
	inv:set_size("dst",2)
	inv:set_size("powerStorage",1)
	inv:set_size("upgrades",4)
	meta:set_int("heat",0)
	meta:set_float("srcTime",0)
	industrialtest.ActivatedElectricMachine.onConstruct(self,pos)
end

function industrialtest.InductionFurnace.getFormspec(self,pos)
	local parentFormspec=industrialtest.ActivatedElectricMachine.getFormspec(self,pos)
	local meta=minetest.get_meta(pos)
	local powerPercent=meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")*100
	local maxSrcTime=meta:get_float("maxSrcTime")
	local srcPercent=maxSrcTime>0 and meta:get_float("srcTime")/maxSrcTime*100 or 0
	local heat=meta:get_int("heat")
	local formspec={
		industrialtest.internal.getItemSlotBg(3.7,1.8,2,1),
		"list[context;src;3.7,1.8;2,1]",
		(powerPercent>0 and "image[3.7,2.8;1,1;industrialtest_gui_electricity_bg.png^[lowpart:"..powerPercent..":industrialtest_gui_electricity_fg.png]"
		 or "image[3.7,2.8;1,1;industrialtest_gui_electricity_bg.png]"),
		industrialtest.internal.getItemSlotBg(3.7,3.9,1,1),
		"list[context;powerStorage;3.7,3.9;1,1]",
		(srcPercent>0 and "image[4.9,2.8;1,1;gui_furnace_arrow_bg.png^[lowpart:"..srcPercent..":gui_furnace_arrow_fg.png^[transformR270]"
		 or "image[4.9,2.8;1,1;gui_furnace_arrow_bg.png^[transformR270]"),
		industrialtest.internal.getItemSlotBg(6,2.8,2,1),
		"list[context;dst;6,2.8;2,1;]",
		industrialtest.internal.getItemSlotBg(9,0.9,1,4),
		"list[context;upgrades;9,0.9;1,4]",
		"label[0.5,2.8;"..minetest.formspec_escape(S("Heat: @1 %",heat)).."]",
		"listring[context;src]",
		"listring[context;dst]"
    }
	return parentFormspec..table.concat(formspec,"")
end

function industrialtest.InductionFurnace.update(self,pos,elapsed,meta,inv)
	local srcList=inv:get_list("src")
	local heat=meta:get_int("heat")
	local shouldRerunTimer=false
	local shouldUpdateFormspec=false

	if heat>0 then
		meta:set_int("heat",math.max(heat-math.max(2*elapsed,1),0))
		shouldRerunTimer=true
		shouldUpdateFormspec=true
	end

	return shouldRerunTimer,shouldUpdateFormspec
end

function industrialtest.InductionFurnace.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	if toList=="dst" then
		return 0
	end
	return industrialtest.ActivatedElectricMachine.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
end

function industrialtest.InductionFurnace.allowMetadataInventoryPut(self,pos,listname,index,stack)
	if listname=="dst" then
		return 0
	end
	return industrialtest.ActivatedElectricMachine.allowMetadataInventoryPut(self,pos,listname,index,stack)
end

function industrialtest.InductionFurnace.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	industrialtest.ActivatedElectricMachine.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	if fromList=="src" or toList=="src" then
		self:calculateMaxSrcTime(pos)
	end
	if fromList=="src" and self.isInputEmpty(pos) then
		local meta=minetest.get_meta(pos)
		meta:set_float("srcTime",-1)
		meta:set_float("maxSrcTime",0)
		self:updateFormspec(pos)
	elseif toList=="src" or (fromList=="dst" and not self.isOutputFull(pos)) then
		self:triggerIfNeeded(pos)
	end
end

function industrialtest.InductionFurnace.onMetadataInventoryPut(self,pos,listname,index,stack,player)
	industrialtest.ActivatedElectricMachine.onMetadataInventoryPut(self,pos,listname,index,stack,player)
	if listname=="src" then
		self:calculateMaxSrcTime(pos)
		self:triggerIfNeeded(pos)
	end
end

function industrialtest.InductionFurnace.onMetadataInventoryTake(self,pos,listname,index,stack)
	if listname=="src" then
		self:calculateMaxSrcTime(pos)
	elseif listname=="dst" and not self.isOutputFull(pos) then
		self:triggerIfNeeded(pos)
	end
end

function industrialtest.InductionFurnace.shouldActivate(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcList=inv:get_list("src")

	for _,slot in ipairs(srcList) do
		if not slot:is_empty() then
			local result,after=minetest.get_craft_result({
				method="cooking",
				width=1,
				items={slot}
			})
			if result.time>0 and inv:room_for_item("dst",result.item) then
				return meta:get_int("industrialtest.powerAmount")>=self._opPower
			end
		end
	end

	return false
end

function industrialtest.InductionFurnace.shouldDeactivate(self,pos)
	return not self:shouldActivate(pos)
end

function industrialtest.InductionFurnace.activeUpdate(self,pos,elapsed,meta,inv)
	local srcList=inv:get_list("src")
	local powerAmount=meta:get_int("industrialtest.powerAmount")
	local srcTime=meta:get_float("srcTime")
	local maxSrcTime=meta:get_float("maxSrcTime")
	local heat=meta:get_int("heat")
	local speed=industrialtest.api.getMachineSpeed(meta)
	local requiredPower=elapsed*self._opPower*speed

	local results={}
	for _,slot in ipairs(srcList) do
		if slot:is_empty() then
			table.insert(results,false)
		else
			local result,after=minetest.get_craft_result({
				method="cooking",
				width=1,
				items={slot}
			})
			if result.time>0 and inv:room_for_item("dst",result.item) then
				table.insert(results,result.item)
			else
				table.insert(results,false)
			end
		end
	end

	srcTime=srcTime+elapsed*(1+heat/200)
	if srcTime>=maxSrcTime then
		for i,result in ipairs(results) do
			if result then
				local multiplier=math.min(srcList[i]:get_count(),speed)
				local prevCount=result:get_count()
				result:set_count(result:get_count()*multiplier)
				local leftover=inv:add_item("dst",result)
				srcList[i]:take_item(multiplier-leftover:get_count()/prevCount)
				inv:set_stack("src",i,srcList[i])
			end
		end
		srcTime=0
	end
	meta:set_float("srcTime",srcTime)

	if heat<100 then
		meta:set_int("heat",math.min(100,heat+speed))
	end

	industrialtest.api.addPower(meta,-requiredPower)

	return true
end

function industrialtest.InductionFurnace.calculateMaxSrcTime(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcList=inv:get_list("src")

	local maxSrcTime=0
	for _,slot in ipairs(srcList) do
		local result,_=minetest.get_craft_result({
			method="cooking",
			width=1,
			items={slot}
		})
		maxSrcTime=math.max(maxSrcTime,result.time*self._efficiency)
	end
	meta:set_float("maxSrcTime",maxSrcTime)
end

function industrialtest.InductionFurnace.isInputEmpty(pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcList=inv:get_list("src")

	for _,slot in ipairs(srcList) do
		if not slot:is_empty() then
			return false
		end
	end

	return true
end

function industrialtest.InductionFurnace.isOutputFull(pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local dstList=inv:get_list("dst")

	for _,slot in ipairs(dstList) do
		if slot:get_free_space()>0 then
			return false
		end
	end

	return true
end

industrialtest.InductionFurnace:register()

minetest.register_craft({
	type="shaped",
	output="industrialtest:induction_furnace",
	recipe={
		{industrialtest.elementKeys.copperIngot,industrialtest.elementKeys.copperIngot,industrialtest.elementKeys.copperIngot},
		{industrialtest.elementKeys.copperIngot,"industrialtest:electric_furnace",industrialtest.elementKeys.copperIngot},
		{industrialtest.elementKeys.copperIngot,"industrialtest:advanced_machine_block",industrialtest.elementKeys.copperIngot}
	}
})
