-- 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/>.

industrialtest.Machine={}

function industrialtest.Machine.onConstruct(self,pos)
	local meta=minetest.get_meta(pos)
	if not self.withoutFormspec then
		meta:set_string("formspec",self:getFormspec(pos))
	end
	self:trigger(pos)
end

function industrialtest.Machine.onDestruct(self)
	-- dummy function
end

function industrialtest.Machine.afterPlaceNode(self,pos,placer,itemstack,pointed)
	-- dummy function
end

function industrialtest.Machine.afterDigNode(self,pos,oldnode,oldmetadata,digger)
	-- dummy function
end

function industrialtest.Machine.onDig(self,pos,node,digger)
	-- dummy function
	return minetest.node_dig(pos,node,digger)
end

function industrialtest.Machine.getFormspec(self,pos)
	local formspec
	if industrialtest.mtgAvailable then
		formspec={
			"formspec_version[4]",
			"size[10.8,12]",
			"label[0.5,0.5;"..self.description.."]",
			"list[current_player;main;0.5,6.25;8,1]",
			"list[current_player;main;0.5,7.5;8,3;8]",
			"listring[current_player;main]"
		}
	elseif industrialtest.mclAvailable then
		formspec={
			"formspec_version[4]",
			"size[12,12.5]",
			"label[0.5,0.5;"..self.description.."]",
			mcl_formspec.get_itemslot_bg_v4(0.5,7,9,3),
			"list[current_player;main;0.5,7;9,3;9]",
			mcl_formspec.get_itemslot_bg_v4(0.5,11,9,1),
			"list[current_player;main;0.5,11;9,1]",
			"listring[current_player;main]"
		}
	end
	return table.concat(formspec,"")
end

function industrialtest.Machine.updateFormspec(self,pos)
	if self.withoutFormspec then
		return
	end

	local meta=minetest.get_meta(pos)
	meta:set_string("formspec",self:getFormspec(pos))
end

function industrialtest.Machine.canUpdate(self,pos)
	return false
end

function industrialtest.Machine.trigger(self,pos)
	local timer=minetest.get_node_timer(pos)
	if not timer:is_started() then
		timer:start(industrialtest.config.updateDelay)
	end
end

function industrialtest.Machine.triggerIfNeeded(self,pos)
	if self:canUpdate(pos) then
		self:trigger(pos)
	end
end

function industrialtest.Machine.onTimer(self,pos,elapsed)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local shouldRerunTimer=false
	local shouldUpdateFormspec=false

	if self.update then
		shouldRerunTimer,shouldUpdateFormspec=self:update(pos,elapsed,meta,inv)
	end

	if shouldUpdateFormspec then
		self:updateFormspec(pos)
	end

	return shouldRerunTimer
end

function industrialtest.Machine.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local movedItemStack=inv:get_stack(fromList,1)

	if toList=="upgrades" then
		return self.allowMoveToUpgradeSlot(pos,toIndex,movedItemStack)
	end

	return count
end

function industrialtest.Machine.allowMetadataInventoryPut(self,pos,listname,index,stack,player)
	if listname=="upgrades" then
		return self.allowMoveToUpgradeSlot(pos,index,stack)
	end

	return stack:get_count()
end

function industrialtest.Machine.allowMetadataInventoryTake(self,pos,listname,index,stack,player)
	return stack:get_count()
end

function industrialtest.Machine.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
	if toList=="upgrades" then
		local meta=minetest.get_meta(pos)
		local inv=meta:get_inventory()
		local itemstack=inv:get_stack(fromList,fromIndex)
		local def=itemstack:get_definition()
		if def and def._industrialtest_self and def._industrialtest_self.apply then
			def._industrialtest_self:apply(pos)
		end
	elseif fromList=="upgrades" then
		local meta=minetest.get_meta(pos)
		local inv=meta:get_inventory()
		local itemstack=inv:get_stack(fromList,fromIndex)
		local def=itemstack:get_definition()
		if def and def._industrialtest_self and def._industrialtest_self.remove then
			def._industrialtest_self:remove(pos)
		end
	end
end

function industrialtest.Machine.onMetadataInventoryPut(self,pos,listname,index,stack)
	if listname=="upgrades" then
		local def=stack:get_definition()
		if def and def._industrialtest_self and def._industrialtest_self.apply then
			def._industrialtest_self:apply(pos)
		end
	end
end

function industrialtest.Machine.onMetadataInventoryTake(self,pos,listname,index,stack)
	if listname=="upgrades" then
		local def=stack:get_definition()
		if def and def._industrialtest_self and def._industrialtest_self.remove then
			def._industrialtest_self:remove(pos)
		end
	end
end

function industrialtest.Machine.onReceiveFields(self,pos,formname,fields)
	-- dummy function
end

local function mclAfterDigNode(pos,oldmeta,lists)
	-- Taken from https://git.minetest.land/MineClone2/MineClone2/src/branch/master/mods/ITEMS/mcl_furnaces/init.lua#L538
	local meta=minetest.get_meta(pos)
	local meta2=meta
	meta:from_table(oldmeta)
	local inv=meta:get_inventory()
	for _,listname in ipairs(lists) do
		local stack=inv:get_stack(listname,1)
		if not stack:is_empty() then
			local p = {x=pos.x+math.random(0, 10)/10-0.5, y=pos.y, z=pos.z+math.random(0, 10)/10-0.5}
			minetest.add_item(p, stack)
		end
	end
	meta:from_table(meta2:to_table())
end

function industrialtest.Machine.createDefinitionTable(self)
	local def={
		description=self.description,
		tiles=self.tiles,
		on_construct=function(pos)
			self:onConstruct(pos)
		end,
		on_destruct=function(pos)
			self:onDestruct(pos)
		end,
		after_place_node=function(pos,placer,itemstack,pointed)
			self:afterPlaceNode(pos,placer,itemstack,pointed)
		end,
		after_dig_node=function(pos,oldnode,oldmetadata,digger)
			self:afterDigNode(pos,oldnode,oldmetadata,digger)
		end,
		on_dig=function(pos,node,digger)
			return self:onDig(pos,node,digger)
		end,
		on_timer=function(pos,elapsed)
			return self:onTimer(pos,elapsed)
		end,
		allow_metadata_inventory_move=function(pos,fromList,fromIndex,toList,toIndex,count)
			return self:allowMetadataInventoryMove(pos,fromList,fromIndex,toList,toIndex,count)
		end,
		allow_metadata_inventory_put=function(pos,listname,index,stack,player)
			return self:allowMetadataInventoryPut(pos,listname,index,stack,player)
		end,
		allow_metadata_inventory_take=function(pos,listname,index,stack,player)
			return self:allowMetadataInventoryTake(pos,listname,index,stack,player)
		end,
		on_metadata_inventory_put=function(pos,listname,index,stack,player)
			self:onMetadataInventoryPut(pos,listname,index,stack)
		end,
		on_metadata_inventory_move=function(pos,fromList,fromIndex,toList,toIndex,count)
			self:onMetadataInventoryMove(pos,fromList,fromIndex,toList,toIndex)
		end,
		on_metadata_inventory_take=function(pos,listname,index,stack,player)
			self:onMetadataInventoryTake(pos,listname,index,stack)
		end,
		on_receive_fields=function(pos,formname,fields)
			self:onReceiveFields(pos,formname,fields)
		end,
		_industrialtest_self=self
	}

	if industrialtest.mtgAvailable then
		def.groups={cracky=2}
		if self.sounds=="metal" then
			def.sounds=default.node_sound_metal_defaults()
		elseif self.sounds=="wood" then
			def.sounds=default.node_sound_wood_defaults()
		end
		def.can_dig=function(pos)
			local meta=minetest.get_meta(pos)
			local inv=meta:get_inventory()
			for _,value in ipairs(self.storageLists) do
				if inv:get_stack(value,1):get_count()>0 then
					return false
				end
			end
			return true
		end
	elseif industrialtest.mclAvailable then
		def.after_dig_node=function(pos,oldnode,oldmeta)
			mclAfterDigNode(pos,oldmeta,self.storageLists)
		end
		if self.sounds=="metal" then
			def.sounds=mcl_sounds.node_sound_metal_defaults()
		elseif sounds=="wood" then
			def.sounds=mcl_sounds.node_sound_wood_defaults()
		end
		def.groups={
			pickaxey=1,
			container=2
		}
		def._mcl_blast_resistance=3.5
		def._mcl_hardness=3.9
		def._mcl_hoppers_on_try_pull=function(pos, hop_pos, hop_inv, hop_list)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			local stack = inv:get_stack("dst", 1)
			if not stack:is_empty() and hop_inv:room_for_item(hop_list, stack) then
				return inv, "dst", 1
			end
			return nil, nil, nil
		end
		def._mcl_hoppers_on_try_push=function(pos, hop_pos, hop_inv, hop_list)
			local meta = minetest.get_meta(pos)
			local inv = meta:get_inventory()
			return inv, "src", mcl_util.select_stack(hop_inv, hop_list, inv, "src")
		end
		def._mcl_hoppers_on_after_push=function(pos)
			self:triggerIfNeeded(pos)
		end
	end

	def.groups._industrialtest_wrenchUnmountable=1
	if self.requiresWrench then
		def.drop="industrialtest:machine_block"
	end

	if self.facedir then
		def.paramtype2="facedir"
		def.legacy_facedir_simple=true
	end

	if self.hasPowerInput then
		def.groups._industrialtest_hasPowerInput=1
	end
	if self.hasPowerOutput then
		def.groups._industrialtest_hasPowerOutput=1
	end

	return def
end

function industrialtest.Machine.register(self)
	local def=self:createDefinitionTable()
	minetest.register_node(self.name,def)
	industrialtest.api.addTag(self.name,"usesTimer")
end

function industrialtest.Machine.allowMoveToUpgradeSlot(pos,toIndex,itemstack)
	local def=itemstack:get_definition()
	if not def or not def.groups or not def.groups._industrialtest_machineUpgrade then
		return 0
	end
	local meta=minetest.get_meta(pos)
	local inv=meta:get_inventory()
	local targetSlot=inv:get_stack("upgrades",toIndex)
	if not targetSlot:is_empty() then
		return 0
	end
	return itemstack:get_count()
end
