-- IndustrialTest
-- Copyright (C) 2023 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")

-- Common functions
local function onConstruct(pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	inv:set_size("charged",1)
	inv:set_size("src",1)
	inv:set_size("dst",1)
	meta:set_float("fluidAmount",0)
	meta:set_string("fluid","")
end

local function getFormspec(self,pos)
	local parentFormspec=industrialtest.ActivatedElectricMachine.getFormspec(self,pos)
	local meta=minetest.get_meta(pos)
	local fluidPercent=meta:get_float("fluidAmount")/100
	local powerPercent=meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")
	local fluid=meta:get_string("fluid")
	local fuel=self.getFuel(fluid)
	local tile=(fuel and fuel.texture or "industrialtest_gui_fluid_bg.png")
	local formspec={
		industrialtest.internal.getItemSlotBg(2,1.8,1,1),
		"list[context;src;2,1.8;1,1]",
		(fluidPercent>0 and "image[2,3;1,1;industrialtest_gui_fluid_bg.png^[lowpart:"..fluidPercent..":"..tile.."]" or "image[2,3;1,1;industrialtest_gui_fluid_bg.png]"),
		industrialtest.internal.getItemSlotBg(2,4.2,1,1),
		"list[context;dst;2,4.2;1,1]",
		industrialtest.internal.getItemSlotBg(6,3,1,1),
		"list[context;charged;6,3;1,1]",
		self.createPowerIndicatorWidget(powerPercent,9,1),
		"listring[context;src]",
		"listring[context;dst]"
	}
	return parentFormspec..table.concat(formspec,"")
end

local function allowMetadataInventoryMove(pos,fromList,fromIndex,toList,toIndex,count)
	if toList=="dst" then
		return 0
	end
	return count
end

local function allowMetadataInventoryPut(pos,listname,index,stack,player)
	if listname=="dst" then
		return 0
	end
	return stack:get_count()
end

local function takeFuelFromItem(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local fluidSlot=inv:get_stack("src",1)
	local fluid=meta:get_string("fluid")
	local fluidAmount=meta:get_float("fluidAmount")

	if fluidSlot:is_empty() or fluidAmount>9000 then
		return false
	end

	local fuel=self.getFuelByItem(fluidSlot:get_name())
	if fuel and (fuel.name==fluid or fluid=="") then
		local leftover=false
		local leftoverAddingSucceeded=false
		for _,item in ipairs(fuel.storageItems) do
			if item.name==fluidSlot:get_name() and item.leftover then
				local leftoverItemstack=ItemStack(item.leftover)
				if inv:room_for_item("dst",leftoverItemstack) then
					inv:add_item("dst",leftoverItemstack)
					leftoverAddingSucceeded=true
				end
				leftover=true
			end
		end
		if not leftover or leftoverAddingSucceeded then
			fluidSlot:take_item()
			inv:set_stack("src",1,fluidSlot)
			meta:set_string("fluid",fuel.name)
			meta:set_float("fluidAmount",fluidAmount+1000)
			self:updateFormspec(pos)
			return true
		end
	end

	return false
end

local function generate(self,pos,elapsed)
	local meta=minetest.get_meta(pos)
	local fluidAmount=meta:get_float("fluidAmount")

	if fluidAmount>0 and not industrialtest.api.isFullyCharged(meta) then
		local fluidUsed=math.min(fluidAmount,50)
		meta:set_float("fluidAmount",fluidAmount-fluidUsed*elapsed)
		local toAdd=math.ceil(self.getFuel(meta:get_string("fluid")).calorificValue*elapsed*fluidUsed/50)
		industrialtest.api.addPower(meta,toAdd)
		return true
	end

	return false
end

industrialtest.GeothermalGenerator=industrialtest.internal.derive(industrialtest.ActivatedElectricMachine,{
	name="industrialtest:geothermal_generator",
	description=S("Geothermal Generator"),
	tiles={
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png^industrialtest_geothermal_generator_front.png"
	},
	sounds="metal",
	facedir=true,
	storageLists={
		"src",
		"dst",
		"charged"
	},
	active={
		tiles={
			"industrialtest_machine_block.png",
			"industrialtest_machine_block.png",
			"industrialtest_machine_block.png",
			"industrialtest_machine_block.png",
			"industrialtest_machine_block.png",
			"industrialtest_machine_block.png^industrialtest_geothermal_generator_front_active.png"
		},
		lightSource=10
	},
	powerLists={
		{
			list="charged",
			direction="o"
		}
	},
	requiresWrench=true,
	hasPowerOutput=true,
	capacity=7000,
	flow=industrialtest.api.lvPowerFlow,
	ioConfig="oooooo",
	getFormspec=getFormspec,
	getFuel=industrialtest.api.getGeothermalGeneratorFuel,
	getFuelByItem=industrialtest.api.getGeothermalGeneratorFuelByItem
})

function industrialtest.GeothermalGenerator.onConstruct(self,pos)
	onConstruct(pos)
	industrialtest.ActivatedElectricMachine.onConstruct(self,pos)
end

function industrialtest.GeothermalGenerator.update(self,pos,elapsed,meta,inv)
	local srcSlot=inv:get_stack("src",1)
	local dstSlot=inv:get_stack("dst",1)
	local shouldUpdateFormspec=takeFuelFromItem(self,pos)
	return (not srcSlot:is_empty() and dstSlot:get_free_space()>0 and not industrialtest.api.isFluidStorageFull(meta)),shouldUpdateFormspec
end

function industrialtest.GeothermalGenerator.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	return math.min(allowMetadataInventoryMove(pos,fromList,fromIndex,toList,toIndex,count),industrialtest.ActivatedElectricMachine.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count))
end

function industrialtest.GeothermalGenerator.allowMetadataInventoryPut(self,pos,listname,index,stack,player)
	return math.min(allowMetadataInventoryPut(pos,listname,index,stack,player),industrialtest.ActivatedElectricMachine.allowMetadataInventoryPut(self,pos,listname,index,stack,player))
end

function industrialtest.GeothermalGenerator.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	if toList=="src" and takeFuelFromItem(self,pos) then
		self:trigger(pos)
	end
	industrialtest.ActivatedElectricMachine.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
end

function industrialtest.GeothermalGenerator.onMetadataInventoryPut(self,pos,listname,index,stack)
	if listname=="src" and takeFuelFromItem(self,pos) then
		self:trigger(pos)
	end
	industrialtest.ActivatedElectricMachine.onMetadataInventoryPut(self,pos,listname,index,stack)
end

function industrialtest.GeothermalGenerator.shouldActivate(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcSlot=inv:get_stack("src",1)
	local dstSlot=inv:get_stack("dst",1)
	local fluidAmount=meta:get_float("fluidAmount")

	return (fluidAmount>0 or (not srcSlot:is_empty() and dstSlot:get_free_space()>0)) and not industrialtest.api.isFullyCharged(meta)
end

function industrialtest.GeothermalGenerator.shouldDeactivate(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcSlot=inv:get_stack("src",1)
	local dstSlot=inv:get_stack("dst",1)
	local fluidAmount=meta:get_float("fluidAmount")

	return (fluidAmount<=0 and (srcSlot:is_empty() or dstSlot:get_free_space()==0)) or industrialtest.api.isFullyCharged(meta)
end

function industrialtest.GeothermalGenerator.activeUpdate(self,pos,elapsed)
	local shouldUpdateFormspec=false

	if takeFuelFromItem(self,pos) then
		shouldUpdateFormspec=true
	end

	if generate(self,pos,elapsed) then
		shouldUpdateFormspec=true
	end

	return shouldUpdateFormspec
end

industrialtest.GeothermalGenerator:register()

minetest.register_craft({
	type="shaped",
	output="industrialtest:geothermal_generator",
	recipe={
		{industrialtest.elementKeys.glass,"industrialtest:empty_cell",industrialtest.elementKeys.glass},
		{industrialtest.elementKeys.glass,"industrialtest:empty_cell",industrialtest.elementKeys.glass},
		{"industrialtest:refined_iron_ingot","industrialtest:generator","industrialtest:refined_iron_ingot"}
	}
})

industrialtest.WaterMill=industrialtest.internal.derive(industrialtest.ElectricMachine,{
	name="industrialtest:water_mill",
	description=S("Water Mill"),
	tiles={
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png",
		"industrialtest_machine_block.png^industrialtest_water_mill_side.png",
		"industrialtest_machine_block.png^industrialtest_water_mill_side.png",
		"industrialtest_machine_block.png^industrialtest_water_mill_side.png",
		"industrialtest_machine_block.png^industrialtest_water_mill_side.png"
	},
	sounds="metal",
	storageLists={
		"src",
		"dst",
		"charged"
	},
	powerLists={
		{
			list="charged",
			direction="o"
		}
	},
	requiresWrench=true,
	hasPowerOutput=true,
	capacity=7000,
	flow=industrialtest.api.lvPowerFlow,
	ioConfig="oooooo",
	getFormspec=getFormspec,
	getFuel=industrialtest.api.getWaterMillFuel,
	getFuelByItem=industrialtest.api.getWaterMillFuelByItem
})

function industrialtest.WaterMill.onConstruct(self,pos)
	onConstruct(pos)
	industrialtest.ElectricMachine.onConstruct(self,pos)
end

function industrialtest.WaterMill.canUpdate(self,pos)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local srcSlot=inv:get_stack("src",1)
	local dstSlot=inv:get_stack("dst",1)
	local fluidAmount=meta:get_float("fluidAmount")

	return (fluidAmount>0 or (not srcSlot:is_empty() and dstSlot:get_free_space()>0)) and not industrialtest.api.isFullyCharged(meta)
end

function industrialtest.WaterMill.update(self,pos,elapsed)
	local shouldUpdateFormspec=false
	local shouldRerunTimer=false

	if takeFuelFromItem(self,pos) then
		shouldUpdateFormspec=true
		shouldRerunTimer=true
	end

	if generate(self,pos,elapsed) then
		shouldUpdateFormspec=true
		shouldRerunTimer=true
	end

	return shouldRerunTimer,shouldUpdateFormspec
end

function industrialtest.WaterMill.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	return math.min(allowMetadataInventoryMove(pos,fromList,fromIndex,toList,toIndex,count),industrialtest.ActivatedElectricMachine.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count))
end

function industrialtest.WaterMill.allowMetadataInventoryPut(self,pos,listname,index,stack,player)
	return math.min(allowMetadataInventoryPut(pos,listname,index,stack,player),industrialtest.ActivatedElectricMachine.allowMetadataInventoryPut(self,pos,listname,index,stack,player))
end

function industrialtest.WaterMill.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	if toList=="src" and takeFuelFromItem(self,pos) then
		self:trigger(pos)
	end
	industrialtest.ElectricMachine.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
end

function industrialtest.WaterMill.onMetadataInventoryPut(self,pos,listname,index,stack)
	if listname=="src" and takeFuelFromItem(self,pos) then
		self:trigger(pos)
	end
	industrialtest.ElectricMachine.onMetadataInventoryPut(self,pos,listname,index,stack)
end

function industrialtest.WaterMill.action(self,pos)
	local meta=minetest.get_meta(pos)
	local powerToAdd=0
	local neighbourPositions={
		vector.offset(pos,-1,0,0),
		vector.offset(pos,1,0,0),
		vector.offset(pos,0,-1,0),
		vector.offset(pos,0,1,0),
		vector.offset(pos,0,0,-1),
		vector.offset(pos,0,0,1)
	}
	for _,value in ipairs(neighbourPositions) do
		local node=minetest.get_node_or_nil(value)
		if node then
			local fuel=industrialtest.api.getWaterMillFuel(node.name)
			if fuel then
				powerToAdd=powerToAdd+fuel.calorificValue*0.2
			end
		end
	end
	if industrialtest.api.addPower(meta,powerToAdd)>0 then
		self:updateFormspec(pos)
		self:trigger(pos)
	end
end

industrialtest.WaterMill:register()

local neighbors={}
for key,_ in pairs(industrialtest.api.waterMillFuels) do
	table.insert(neighbors,key)
end
minetest.register_abm({
	label="Water Mill generating",
	nodenames={"industrialtest:water_mill"},
	neighbors=neighbors,
	interval=industrialtest.config.updateDelay,
	chance=1,
	action=function(pos)
		industrialtest.WaterMill:action(pos)
	end
})

minetest.register_craft({
	type="shaped",
	output="industrialtest:water_mill",
	recipe={
		{"",industrialtest.elementKeys.stick,""},
		{industrialtest.elementKeys.stick,"industrialtest:generator",industrialtest.elementKeys.stick},
		{"",industrialtest.elementKeys.stick,""}
	}
})
