-- IndustrialTest
-- Copyright (C) 2025 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 Creates hypertext table with rows
-- \param rows table
-- \param withTitleRow bool
-- \param description (optional) string
-- \returns string
local function createTable(rows,withTitleRow,description)
	local columnWidths={}
	for _,row in ipairs(rows) do
		for i,cell in ipairs(row) do
			-- Add 3 here because of cell padding (2 spaces at each side of column text) and right cell separator
			columnWidths[i]=math.max(columnWidths[i] or 0,string.len(cell)+3)
		end
	end

	-- Begin with width of 1 because of left edge
	local tableWidth=1
	for _,width in ipairs(columnWidths) do
		tableWidth=tableWidth+width
	end

	local horizontalEdge=string.rep("-",tableWidth).."\n"
	local hypertextRows={horizontalEdge}
	for i,row in ipairs(rows) do
		local hypertextRow={"|"}
		for o,cell in ipairs(row) do
			local cellWidth=string.len(cell)+3
			local rightPadding=string.rep(" ",columnWidths[o]-cellWidth)
			table.insert(hypertextRow,string.format(" %s%s |",cell,rightPadding))
		end
		table.insert(hypertextRow,"\n")
		table.insert(hypertextRows,table.concat(hypertextRow,""))
		-- Insert horizontal edge if title row is present
		if withTitleRow and i==1 then
			table.insert(hypertextRows,horizontalEdge)
		end
	end
	table.insert(hypertextRows,horizontalEdge)

	return "<mono>"..table.concat(hypertextRows,"")..(description or "").."</mono>\n"
end

-- \brief Creates table with machine information while providing few shortcuts for common fields
-- \param fields table
-- \param machineName string
-- \returns string
local function createMachineInformationTable(fields,machineName)
	local fieldLabels={
		inputVoltage="Input voltage level",
		outputVoltage="Output voltage level",
		recipe="Recipe type",
		powerCapacity="Power capacity",
		opPower="Power per operation",
		powerPerFuel="Power per fuel unit",
		efficiency="Default efficiency"
	}

	local rows={}
	for _,field in ipairs(fields) do
		local label=fieldLabels[field.name] or field.name
		table.insert(rows,{
			label,
			field.value
		})
	end

	return createTable(rows,false,string.format("Figure 1. Machine information for %s",machineName))
end

local pages={
	{
		name="introduction",
		title=S("Introduction"),
		icon=industrialtest.elementKeys.paper,
		content={
			[[
				<big>||Introduction||</big>
				<left>
					||IndustrialTest implements finite power functionality which can be used to perform various tasks using generators and consumers. Such features are not a new thing in Luanti, as there are older mods which do that already. This mod, however, proves to be more flexible.||
				</left>
				<img name="industrialtest_guide_introduction.png" width="{{IMAGE_WIDTH}}">
			]]
		}
	},

	{
		name="electricalNetwork",
		title=S("Electrical network"),
		icon="industrialtest:insulated_copper_cable",
		content={
			[[
				<big>||Electrical network||</big>
				<left>
					||In order to attach electricity consumers to power sources (like generators, batteries, further just called generators) electrical network is used. Consumers can get connected either directly to generators or through various cables. Each generator can output certain voltage (this is actually amperage, however since this mod doesn't simulate electricity accurately, it's called voltage), the same situation is for consumers as they can only handle up to certain voltage.||
					||Figure 1 shows voltage levels present in this mod.||
					||If voltage is too high for cable it will melt without dropping anything while consumer will explode also without dropping anything and possibly destroying terrain around. Power capacity is measured in EU (Energy Units) and the voltage is EU/update.||
					||Picture 1 shows environment after consumer explosion.||
				</left>
			]],
			createTable({
				{
					"Voltage level",
					"EU/update",
					"Cable"
				},
				{
					"LV",
					"600",
					"Tin"
				},
				{
					"MV",
					"2400",
					"Copper"
				},
				{
					"HV",
					"10200",
					"Gold"
				},
				{
					"EV",
					"40800",
					"Iron"
				},
				{
					"IV",
					"163800",
					"Glass Fibre"
				}
			},true,S("Figure 1. Voltage levels and respective cables")),
			[[
				<img name="industrialtest_guide_explosion.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Environment after consumer explosion||</left>
			]]
		}
	},

	{
		name="cableFormer",
		title=S("Cable Former"),
		icon="industrialtest:cable_former",
		content={
			[[
				<big>||Cable Former||</big>
				<left>
					||Cable Former can be used to produce cables with higher material efficiency. It can, however, only produce cables from metals so for example <item name="industrialtest:glass_fibre_cable" height="{{ITEM_HEIGHT}}"> <b>Glass Fibre Cable</b> can't be produced in it.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="LV"
				},
				{
					name="recipe",
					value="Cable forming"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.CableFormer.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.CableFormer.opPower)
				},
				{
					name="efficiency",
					value=tostring(1/industrialtest.CableFormer.efficiency)
				}
			},S("Cable Former")),
			[[
				<img name="industrialtest_guide_cable_former.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Cable Former running||</left>
			]]
		}
	},

	{
		name="canningMachine",
		title=S("Canning Machine"),
		icon="industrialtest:canning_machine",
		content={
			[[
				<big>||Canning Machine||</big>
				<left>
					||Canning Machine is used to move certain fluid from one item to another. The common usage is to refill fuel in <item name="industrialtest:jetpack_v" height="{{ITEM_HEIGHT}}">  <b>Jetpack</b> or <item name="industrialtest:fuel_can" height="{{ITEM_HEIGHT}}"> <b>Fuel Can</b>.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="LV"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.CanningMachine.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.CanningMachine._opPower)
				},
				{
					name="Canning completion time",
					value=string.format("%d seconds",industrialtest.CanningMachine._canningTime)
				}
			},S("Canning Machine")),
			[[
				<img name="industrialtest_guide_canning_machine.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Canning Machine refilling <item name="industrialtest:fuel_can" height="{{ITEM_HEIGHT}}"> <b>Fuel Can</b> with fuel from <item name="industrialtest:coalfuel_cell" height="{{ITEM_HEIGHT}}"> <b>Coalfuel Cell</b>||</left>
			]]
		}
	},

	{
		name="compressor",
		title=S("Compressor"),
		icon="industrialtest:compressor",
		content={
			[[
				<big>||Compressor||</big>
				<left>
					||Compressor as the name suggests is used to compress various materials. Mostly required to produce items for crafting other items.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="LV"
				},
				{
					name="recipe",
					value="Compressing"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.Compressor.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.Compressor.opPower)
				},
				{
					name="efficiency",
					value=tostring(1/industrialtest.Compressor.efficiency)
				}
			},S("Compressor")),
			[[
				<img name="industrialtest_guide_compressor.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Compressor compressing <item name="industrialtest:plantball" height="{{ITEM_HEIGHT}}"> <b>Plantball</b> into <item name="industrialtest:compressed_plantball" height="{{ITEM_HEIGHT}}"> <b>Compressed Plantball</b>||</left>
			]]
		}
	},

	{
		name="electricFurnace",
		title=S("Electric Furnace"),
		icon="industrialtest:electric_furnace",
		content={
			[[
				<big>||Electric Furnace||</big>
				<left>
					||Electric Furnace like regular furnace can be used to smelt items. The difference is that Electric Furnace is slightly faster even without any upgrades and, well, it's electric so it doesn't require fuel.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="LV"
				},
				{
					name="recipe",
					value="Smelting"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.ElectricFurnace.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.ElectricFurnace.opPower)
				},
				{
					name="efficiency",
					value=tostring(1/industrialtest.ElectricFurnace.efficiency)
				}
			},S("Electric Furnace")),
			[[
				<img name="industrialtest_guide_electric_furnace.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Electric Furnace smelting <item name="industrialtest:iron_dust" height="{{ITEM_HEIGHT}}"> <b>Iron Dust</b> into <item name="{{elementKeyIronIngot}}" height="{{ITEM_HEIGHT}}"> <b>Iron Ingot</b>||</left>
			]]
		}
	},

	{
		name="extractor",
		title=S("Extractor"),
		icon="industrialtest:extractor",
		content={
			[[
				<big>||Extractor||</big>
				<left>
					||Extractor is used to separate materials from within other materials, often when it's not possible by other means.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="LV"
				},
				{
					name="recipe",
					value="Extracting"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.Extractor.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.Extractor.opPower)
				},
				{
					name="efficiency",
					value=tostring(1/industrialtest.Extractor.efficiency)
				}
			},S("Extractor")),
			[[
				<img name="industrialtest_guide_extractor.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Extractor extracting <item name="{{elementKeyRubber}}" height="{{ITEM_HEIGHT}}"> <b>Rubber</b> from <item name="{{elementKeyRubberWood}}" height="{{ITEM_HEIGHT}}"> <b>Rubber Wood</b>||</left>
			]]
		}
	},

	{
		name="generator",
		title=S("Generator"),
		icon="industrialtest:generator",
		content={
			[[
				<big>||Generator||</big>
				<left>
					||Generator produces energy from various fuels which can be used for smelting. It is the most basic way of powering electric machines and also the simplest to obtain.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="outputVoltage",
					value="LV"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.Generator.capacity)
				},
				{
					name="powerPerFuel",
					value="200 EU"
				}
			},S("Generator")),
			[[
				<img name="industrialtest_guide_generator.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Generator producing power from <item name="{{elementKeyCoal}}" height="{{ITEM_HEIGHT}}"> <b>Coal</b>||</left>
			]]
		}
	},

	{
		name="geothermalGenerator",
		title=S("Geothermal Generator"),
		icon="industrialtest:geothermal_generator",
		content={
			[[
				<big>||Geothermal Generator||</big>
				<left>
					||Geothermal Generator produces energy using heat from fluids like lava. Hot fluids can be transferred into Geothermal Generator for example by using <item name="industrialtest:empty_cell" height="{{ITEM_HEIGHT}}"> <b>Empty Cell</b> or <item name="{{elementKeyBucket}}" height="{{ITEM_HEIGHT}}"> <b>Empty Bucket</b>.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="outputVoltage",
					value="LV"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.GeothermalGenerator.capacity)
				}
			},S("Geothermal Generator")),
			[[
				<img name="industrialtest_guide_geothermal_generator.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Geothermal Generator producing power from <b>Lava</b>||</left>
			]]
		}
	},

	{
		name="inductionFurnace",
		title=S("Induction Furnace"),
		icon="industrialtest:induction_furnace",
		content={
			[[
				<big>||Induction Furnace||</big>
				<left>
					||Induction Furnace like Electric Furnace uses electricity to smelt items. The difference between those is that Induction Furnace when running builds up heat internally over time. This property results in Induction Furnace smelting items faster the longer it's running. Once smelting is finished Induction Furnace begins to slowly lose heat. The heat level is indicated on the left of formspec.||
					||Furthermore Induction Furnace allows to smelt 2 different items at once increasing efficiency even more.||
				</left>
			]],
			createMachineInformationTable({
				{
					name="inputVoltage",
					value="MV"
				},
				{
					name="recipe",
					value="Smelting"
				},
				{
					name="powerCapacity",
					value=string.format("%d EU",industrialtest.InductionFurnace.capacity)
				},
				{
					name="opPower",
					value=string.format("%d EU",industrialtest.InductionFurnace._opPower)
				},
				{
					name="efficiency",
					value=tostring(1/industrialtest.InductionFurnace._efficiency)
				}
			},S("Induction Furnace")),
			[[
				<img name="industrialtest_guide_induction_furnace.png" width="{{IMAGE_WIDTH}}">
				<left>||Picture 1. Induction Furnace smelting <item name="industrialtest:copper_dust" height="{{ITEM_HEIGHT}}"> <b>Copper Dust</b> and <item name="industrialtest:iron_dust" height="{{ITEM_HEIGHT}}"> <b>Iron Dust</b> into <item name="{{elementKeyCopperIngot}}" height="{{ITEM_HEIGHT}}"> <b>Copper Ingot</b> and <item name="{{elementKeyIronIngot}}" height="{{ITEM_HEIGHT}}"> <b>Iron Ingot</b>.||</left>
			]]
		}
	}
}

local function preprocessHypertext(content,vars)
	-- Find and replace strings which should be translated, this pattern matches any string that doesn't include | which is contained within || at both ends
	content=string.gsub(content,"||([^|]+)||",function(sourceString)
		return S(sourceString)
	end)

	for key,value in pairs(vars) do
		content=string.gsub(content,string.format("{{%s}}",key),value)
	end

	return content
end

local function getGuideFormspec(playerName,pageName)
	local formspec={
		"formspec_version[4]",
		"size[15,10.8]",
		"label[0.1,0.2;"..S("IndustrialTest Guide").."]",
		--"scrollbaroptions[]",
		"scrollbar[3.6,0.4;0.5,10.3;vertical;scrollbarList;0]",
		"scroll_container[0.1,0.4;4,10.3;scrollbarList;vertical]"
	}

	-- Contents sidebar
	local PAGE_BUTTON_HEIGHT=0.7
	for i,page in ipairs(pages) do
		table.insert(formspec,string.format("container[0,%f]",(i-1)*PAGE_BUTTON_HEIGHT))
		table.insert(formspec,string.format("item_image[0,0;%f,%f;%s]",PAGE_BUTTON_HEIGHT,PAGE_BUTTON_HEIGHT,page.icon))
		table.insert(formspec,string.format("button[%f,0;%f,%f;%s;%s]",PAGE_BUTTON_HEIGHT+0.05,3.45-PAGE_BUTTON_HEIGHT,PAGE_BUTTON_HEIGHT,page.name,page.title))
		table.insert(formspec,"container_end[]")
	end
	table.insert(formspec,"scroll_container_end[]")

	-- Introduction page is shown by default
	pageName=(pageName or "introduction")

	for _,page in ipairs(pages) do
		if page.name==pageName then
			local content=preprocessHypertext(table.concat(page.content,""),{
				IMAGE_WIDTH=550,
				ITEM_HEIGHT=16,
				elementKeyBucket=industrialtest.elementKeys.bucket,
				elementKeyCoal=industrialtest.elementKeys.coal,
				elementKeyCopperIngot=industrialtest.elementKeys.copperIngot,
				elementKeyIronIngot=industrialtest.elementKeys.ironIngot,
				elementKeyRubber=industrialtest.elementKeys.rubber,
				elementKeyRubberWood=industrialtest.elementKeys.rubberWood
			})
			table.insert(formspec,string.format("hypertext[4.2,0.4;10.7,10.3;content;%s]",content))
			break
		end
	end

	return table.concat(formspec,"")
end

local function showGuide(playerName,page)
	minetest.show_formspec(playerName,"industrialtest:guide",getGuideFormspec(playerName,page))
	return true
end

local function handleGuideFields(player,formname,fields)
	if formname~="industrialtest:guide" then
		return
	end

	for _,page in ipairs(pages) do
		if fields[page.name] then
			minetest.close_formspec(player:get_player_name(),formname)
			showGuide(player:get_player_name(),page.name)
			break
		end
	end
end
minetest.register_on_player_receive_fields(handleGuideFields)

minetest.register_chatcommand("industrialtest_guide",{
	description=S("Shows graphical guide for content and features provided by IndustrialTest"),
	func=function(playerName)
		showGuide(playerName)
	end
})
