-- 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")

-- \brief Adds power storage to metadata
-- \param capacity How much EU item/node can store
-- \param flow How much EU can flow in or out item/node per industrialtest.updateDelay
-- \param ioConfig Input/Output configuration in following side order: -X, +X, -Y, +Y, -Z, +Z
--                 a - bidirectional, i - input, o - output
-- \returns nil
function industrialtest.api.addPowerStorage(meta,capacity,flow,ioConfig)
	meta:set_int("industrialtest.powerCapacity",capacity)
	meta:set_int("industrialtest.powerFlow",flow)
	meta:set_int("industrialtest.powerAmount",0)
	meta:set_string("industrialtest.ioConfig",ioConfig)
end

-- \brief Checks if metadata contains power storage
-- \param meta MetaDataRef which should be checked
-- \returns true if metadata contains power storage, false otherwise
function industrialtest.api.hasPowerStorage(meta)
	local values={"industrialtest.powerCapacity","industrialtest.powerFlow","industrialtest.powerAmount","industrialtest.ioConfig"}
	for _,value in ipairs(values) do
		if not meta:contains(value) then
			return false
		end
	end
	return true
end

-- \brief Changes node's power IO config. Function doesn't check if meta actually contains power storage.
-- \param meta MetaDataRef of node which power IO config should be changed
-- \param side Side number. See industrialtest.api.addPowerStorage to check order.
-- \param mode Side mode. See industrialtest.api.addPowerStorage for possible values.
-- \returns nil
function industrialtest.api.changeIoConfig(meta,side,mode)
	local ioConfig=meta:get_string("industrialtest.ioConfig")
	ioConfig=string.sub(ioConfig,1,side-1)..mode..string.sub(ioConfig,side+1)
	meta:set_string("industrialtest.ioConfig",ioConfig)
end

-- \brief Checks if provided side is power input
-- \param meta MetaDataRef of node
-- \param side Side number. See industrialtest.api.addPowerStorage to check order.
-- \returns true if provided side is power input, false otherwise
function industrialtest.api.isPowerInput(meta,side)
	local ioConfig=meta:get_string("industrialtest.ioConfig")
	local mode=string.sub(ioConfig,side,side)
	return (mode=="i" or mode=="a")
end

-- \brief Checks if provided side is power output
-- \param meta MetaDataRef of node
-- \param side Side number. See industrialtest.api.addPowerStorage to check order.
-- \returns true if provided side is power output, false otherwise
function industrialtest.api.isPowerOutput(meta,side)
	local ioConfig=meta:get_string("industrialtest.ioConfig")
	local mode=string.sub(ioConfig,side,side)
	return (mode=="o" or mode=="a")
end

-- \brief Checks if power storage is fully charged
-- \param meta MetaDataRef which should be checked
-- \returns true if power storage is fully charged, false otherwise
function industrialtest.api.isFullyCharged(meta)
	return meta:get_int("industrialtest.powerAmount")>=meta:get_int("industrialtest.powerCapacity")
end

-- \brief Adds power to power storage. Function doesn't check if meta contains power storage so you must be sure that it does.
-- \param meta MetaDataRef to which power should be added
-- \param amount Amount of power to add
-- \returns How much of power was actually added
function industrialtest.api.addPower(meta,amount)
	local powerAmount=meta:get_int("industrialtest.powerAmount")
	local powerCapacity=meta:get_int("industrialtest.powerCapacity")
	local prevPowerAmount=powerAmount
	powerAmount=industrialtest.internal.clamp(powerAmount+amount,0,powerCapacity)
	meta:set_int("industrialtest.powerAmount",powerAmount)
	return powerAmount-prevPowerAmount
end

-- \brief Adds power to destination metadata while subtracting it from source metadata
-- \Param srcMeta MetaDataRef from which take power
-- \param destMeta MetaDataRef to which add power
-- \returns How much of power was actually transferred
function industrialtest.api.transferPower(srcMeta,destMeta,amount)
	local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount)
	if currentFlow==0 then
		return 0
	end
	local actualFlow=industrialtest.api.addPower(destMeta,currentFlow)
	srcMeta:set_int("industrialtest.powerAmount",srcMeta:get_int("industrialtest.powerAmount")-actualFlow)
	return actualFlow
end

-- \brief Updates itemstack description to show current power storage information, additionally updates item wear bar.
--        Function doesn't check if itemstack contains power storage so you should be sure that it does before calling this function
-- \param itemstack ItemStack which should be updated
-- \returns nil
function industrialtest.api.updateItemPowerText(itemstack)
	local meta=itemstack:get_meta()
	local def=minetest.registered_tools[itemstack:get_name()]
	local desc=meta:contains("industrialtest.descriptionOverride") and meta:get_string("industrialtest.descriptionOverride") or def.description
	meta:set_string("description",S("@1\n@2 / @3 EU",desc,meta:get_int("industrialtest.powerAmount"),meta:get_int("industrialtest.powerCapacity")))
	itemstack:set_wear(65535-meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")*65534)
end

-- \brief Adds power storage to item depending on it's definition
-- \param itemstack ItemStack to which item storage should be added
-- \returns true if power storage was successfully added, false otherwise
function industrialtest.api.preparePowerStorageItem(itemstack)
	local meta=itemstack:get_meta()
	local def=minetest.registered_tools[itemstack:get_name()]
	if industrialtest.api.hasPowerStorage(meta) or not def or not def._industrialtest_powerStorage or not def._industrialtest_powerCapacity or not def._industrialtest_powerFlow then
		return false
	end
	industrialtest.api.addPowerStorage(meta,def._industrialtest_powerCapacity,def._industrialtest_powerFlow,"n/a")
	industrialtest.api.updateItemPowerText(itemstack)
	return true
end

-- \brief Adds power to itemstack. Function checks if itemstack has power storage.
-- \param itemstack ItemStack to which add power
-- \param amount How much power to add
-- \returns Amount of power added
function industrialtest.api.addPowerToItem(itemstack,amount)
	local meta=itemstack:get_meta()
	if not industrialtest.api.hasPowerStorage(meta) then
		return 0
	end
	local added=industrialtest.api.addPower(meta,amount)
	industrialtest.api.updateItemPowerText(itemstack)
	return added
end

-- \brief Adds power to destination itemstack while subtracting it from source metadata
-- \param srcMeta MetaDataRef from which take power
-- \param itemstack ItemStack to which add power
-- \param amount number
-- \returns How much of power was actually transferred
function industrialtest.api.transferPowerToItem(srcMeta,itemstack,amount)
	local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount)
	if currentFlow==0 then
		return 0
	end
	local actualFlow=industrialtest.api.addPowerToItem(itemstack,currentFlow)
	srcMeta:set_int("industrialtest.powerAmount",srcMeta:get_int("industrialtest.powerAmount")-actualFlow)
	return actualFlow
end

-- \brief Adds power to destination metadata while subtracting it from source itemstack
-- \param srcItemstack ItemStack from which subtract power
-- \param meta MetaDataRef to which add power
-- \param amount How much power should be transferred
-- \returns How much of power was actually transferred
function industrialtest.api.transferPowerFromItem(srcItemstack,meta,amount)
	local srcMeta=srcItemstack:get_meta()
	local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount)
	if currentFlow==0 then
		return 0
	end
	local actualFlow=industrialtest.api.addPower(meta,currentFlow)
	industrialtest.api.addPowerToItem(srcItemstack,-actualFlow)
	return actualFlow
end