-----------------------------------
--Controller for the Requisitions UI
-----------------------------------

local AsyncUtil = require("lib_async")
local Dialogs = require("lib_dialogs")
local Md = require("debugFunctions")
local Topics = require("lib_ui_topics")
local Utils = require("lib_utils")

local Class = require("lib_class")

local RequisitionsController = Class()

RequisitionsController.SECTION_SHIPS = 1 -- Ships
RequisitionsController.SECTION_PRIMARIES = 2 -- Primaries
RequisitionsController.SECTION_SECONDARIES = 3 -- Secondaries

--- Called by the class constructor
--- @return nil
function RequisitionsController:init()
	self.Document = nil ---@type Document the RML document
	self.SelectedData = nil ---@type string the current data type

	--Requisitions Globals
	self.InputActive = false ---@type boolean if the requisition input field is active
	self.InputElement = nil ---@type ElementFormControlInput the requisition input field
	self.SubmittedValue = 0 ---@type number the value submitted by the requisition input field
	self.CurrentSelection = nil ---@type backstock_requisition_item the current requisition selection
	self.CurrentSection = nil ---@type number the current section. Should be one of the SECTION_* enumerations
	self.CurrentFaction = nil ---@type string the current faction
	self.CurrentKey = nil ---@type string the current item key

	self.RecentSection = nil ---@type number the last section visited. Should be one of the SECTION_* enumerations
	self.RecentFaction = nil ---@type string the last faction visited
	self.RecentKey = nil ---@type string the last item key visited

	self.Visible_Factions = {} ---@type string[] a list of factions to display

	self.TotalCost = nil ---@type number the total cost of the requisition

	self.FirstRun = false ---@type boolean if this is the first frame of the requisitions ui or not

	--Translatable strings
	--- @type table<string, table<string, number>>
	self.Strings = {
		Available = {"Available", 11578},
		Owned = {"Owned", 11579},
		Cost = {"Cost", 11580},
		Selection = {"Total", 11581},
		CommandPoints = {"CP", 9258},
		PerItem = {"Per Item", 11583},
		Remaining = {"Remaining", 11547},
		FactionIntro = {"Typically flown by the", 11548},
		Type = {"Ship Type", 11549},
		Maneuver = {"Maneuverability Rating", 11550},
		Armor = {"Armor Rating", 11551},
		Manufacturer = {"Manufactured by", 11552},
		Afterburn = {"Max Afterburner Fuel", 11553},
		Countermeasures = {"Countermeasure Capacity", 11554},
		Power = {"Power Generator Capacity", 11555},
		PowerSuffix = {"mw", 11556},
		CreatedByIntro = {"Designed and Manufactured by", 11557},
		Seeking = {"Weapon Type", 11558},
		Range = {"Range", 11457},
		Speed = {"Speed", 11560},
		Damage = {"Damage Yield", 11561},
		DPS = {"Damage Per Second", 11562},
		ArmorFactor = {"Armor Damage", 11563},
		ShieldFactor = {"Shield Damage", 11564},
		SubsystemFactor = {"Subsystem Damage", 11565},
		CargoSize = {"Bank Capacity Used Per Unit", 11566},
		SpeedSuffix = {"m/s", 11567},
		DamageSuffix = {"KEb", 11568},
		DPSSuffix = {"KEb/s", 11569},
		RangeSuffix = {"m", 11570},
		NoReqs = {"No Requisitions Available", 11608},
		Okay = {"Okay", 10819},
		NeededCP = {"Total cost for the requested items is", 11610},
		CurrentCP = {"You only have", 11611},

		FailureText = {"Not enough Command Points available!", 11612},
		FailureTitle = {"REQUISITION FAILURE", 11613},
		SuccessText1 = {"Sucessfully requisitioned", 11614},
		SuccessText2 = {"Total cost", 11615},
		SuccessText3 = {"Amount available is now reflected in Inventory", 11616},
		SuccessText4 = {"Command Points remaining", 11617},
		SuccessTitle = {"REQUISITION SUCCESS", 11618},
		EmptyReturn = {"Please enter a non-zero number into the text box and then submit.", 11619}
	}
	self.OkayKeypress = string.sub(Utils.xstr(self.Strings.Okay), 1, 1) --Get the first letter of the Okay string for keypress detection

	ScpuiSystem.data.memory.model_rendering = nil

end

--- Called by the RML document
--- @param document Document
function RequisitionsController:initialize(document)
    self.Document = document
	ScpuiSystem.RequisitionsContext = self --- Reference for cross frame access

	if not Backstock then
		ba.error("Backstock is not loaded! Inventory will not work!")
	end

	if not Factions then
		ba.error("Factions is not loaded! Inventory will not work!")
	end

	---Load background choice
	self.Document:GetElementById("main_background"):SetClass(ScpuiSystem:getBackgroundClass(), true)

	--For BtA: Set the class custom. This is a thing because Journal needs to be modular to SCPUI as well as BtA.
	--So eventually this will need a separate file to add it's own hooks into ui_topics that can then be referenced.
	--But for now we just brute force it.
	self.Document:GetElementById("main_background"):SetClass("inventory_bg", true)

	---Load the desired font size from the save file
	self.Document:GetElementById("main_background"):SetClass(("base_font" .. ScpuiSystem:getFontPixelSize()), true)
	self.Document:GetElementById("requisitions_title"):SetClass("h2", true)
	self.Document:GetElementById("requisitions_counts_wrapper"):SetClass("h1", true)
	self.Document:GetElementById("requisitions_cost"):SetClass("p1", true)
	self.Document:GetElementById("requisitions_requiredcp"):SetClass("s2", true)
	self.Document:GetElementById("requisition_amount_input"):SetClass("p1", true)
	self.Document:GetElementById("requisitions_playercp"):SetClass("p1", true)
	self.Document:GetElementById("submit_cont"):SetClass("p1", true)

	--Meant to remove focus from the input box until the player clicks on it.
	self.InputElement = Element.As.ElementFormControlInput(self.Document:GetElementById("requisition_amount_input"))
	async.run(function()
        async.await(AsyncUtil.wait_for(0.1))
		self.InputElement:Blur()
    end, async.OnFrameExecutor)

	self.InputElement.max_length = 5

	--Always reload data to reflect the latest changes
	self:loadData()

	if ScpuiSystem.InventorySection == nil then
		ScpuiSystem.InventorySection = 1
	end

	self:change_section(ScpuiSystem.InventorySection)

	self.SelectedFaction = nil
	if self.Visible_Factions[1] then
		self:changeFaction(self.Visible_Factions[1])
	end

end

--- Load default and current backstock values if available
--- @return nil
function RequisitionsController:loadData()
	Backstock:LoadDataQuick()

	self.Document:GetElementById("requisitions_requiredcp").inner_rml = "<p>" .. Utils.xstr(self.Strings.Selection) .. ": " .. "0" .. " " .. Utils.xstr(self.Strings.CommandPoints) .. "</p>"
	self.Document:GetElementById("requisitions_playercp").inner_rml = "<p>" .. Utils.xstr(self.Strings.CommandPoints) .. " " .. Utils.xstr(self.Strings.Remaining) .. ": " .. Backstock.current.playerCP .. "</p>"
end

--- Refresh the requisitions lists based on the current section and faction
--- @return nil
function RequisitionsController:refreshLists()

	local new_key = nil
	local new_faction = nil
	local new_section = self.RecentSection

	---@type backstock_requisition_item[]
	local refresh_list = Backstock.current.requisitions[self.SelectedData]

	--The CheckExists function only checks for val.name, but we need val.faction so let's do it manually.
	local faction_exists = false
	for i, v in ipairs(refresh_list) do
		if v.faction == self.RecentFaction then
			faction_exists = true
		end
	end

	--First check if the current faction still exists
	if faction_exists then
		new_faction = self.RecentFaction
	else
		if refresh_list[1] then
			new_faction = refresh_list[1].faction
		end
	end


	--The CheckExists function only checks for val.name, but we need val.id so let's do it manually.
	local key_exists = nil
	for i, v in ipairs(refresh_list) do
		if refresh_list[i].name == self.RecentKey and refresh_list[i].faction == new_faction then
			key_exists = true
		end
	end

	--Now check if the current item key still exists
	if key_exists then
		new_key = self.RecentKey
	else
		if refresh_list[1] then
			for i, v in ipairs(refresh_list) do
				if refresh_list[i].faction == new_faction then
					new_key = refresh_list[i].name
					break
				end
			end
		end
	end

	Md.print("REQUISITIONS is refreshing to " .. new_section .. " " .. tostring(new_faction) .. " " .. tostring(new_key))

	--Actually go to the refreshed selections
	self:change_section(new_section)
	if new_faction then
		self:changeFaction(new_faction)
		if new_key then
			self:selectEntry(new_key)
		end
	end

end

--- Called by the RML document to change the section
--- @param section number the new section. Should be one of the SECTION_* enumerations
--- @return nil
function RequisitionsController:change_section(section)

	if section then--~= self.SelectedSection then

		local faction_list = nil

		self.CurrentSection = section

		if section == self.SECTION_SHIPS then
			faction_list = Backstock.current.requisitions.ships
			self.SelectedData = "ships"
		elseif section == self.SECTION_PRIMARIES then
			faction_list = Backstock.current.requisitions.primaries
			self.SelectedData = "primaries"
		elseif section == self.SECTION_SECONDARIES then
			faction_list = Backstock.current.requisitions.secondaries
			self.SelectedData = "secondaries"
		end

		assert(faction_list, "Faction list was nil!")

		ScpuiSystem.InventorySection = section

		Md.print("REQUISTIONS setting selected data type to " .. self.SelectedData)

		--If we had an old section on, remove the active class
		if self.SelectedSection then
			local old_bullet = self.Document:GetElementById("btn_"..self.SelectedSection)
			old_bullet:SetPseudoClass("checked", false)
		end

		self.SelectedSection = section
		self.SelectedFaction = nil

		--Only create factions if there are any to create
		if faction_list[1] then
			self:createFactionsList(faction_list)
		else
			local list_el = self.Document:GetElementById("list_logos_ul")
			ScpuiSystem:clearEntries(list_el)
			self:clearData()
		end

		if self.Visible_Factions[1] then
			self:changeFaction(self.Visible_Factions[1])
		end

		local new_bullet = self.Document:GetElementById("btn_"..self.SelectedSection)
		new_bullet:SetPseudoClass("checked", true)


	end

end

--- Changes the currently selected faction and updates the visible lists
--- @param faction string the new faction
--- @return nil
function RequisitionsController:changeFaction(faction)

	if faction ~= self.SelectedFaction then

		local item_list = nil

		self.CurrentFaction = faction

		if self.SelectedData == "ships" then
			item_list = Backstock.current.requisitions.ships
		elseif self.SelectedData == "primaries" then
			item_list = Backstock.current.requisitions.primaries
		elseif self.SelectedData == "secondaries" then
			item_list = Backstock.current.requisitions.secondaries
		end

		assert(item_list, "Item list was nil!")

		if self.SelectedEntry then
			self:clearEntry()
		end

		--If we had an old faction on, remove the active class
		if self.SelectedFaction then
			local old_logo = self.Document:GetElementById(self.SelectedFaction)
			old_logo:SetPseudoClass("checked", false)
		end

		self.SelectedFaction = faction

		--Only create entries if there are any to create
		if item_list[1] then
			self:createEntries(item_list)
			--It's possible for the top entry in the index to be for a different faction. In that case, auto select the next one instead.
			for i, v in ipairs(item_list) do
				if item_list[i].faction == self.SelectedFaction then
					self:selectEntry(item_list[i].name)
					break
				end
			end
		else
			local list_el = self.Document:GetElementById("list_items_ul")
			ScpuiSystem:clearEntries(list_el)
			self:clearData()
		end

		-- check for a valid value since some sections can be empty
		if item_list[1] then
			Md.print("REQUISITIONS showing list from " .. self.SelectedFaction)
			local new_logo = self.Document:GetElementById(self.SelectedFaction)
			new_logo:SetPseudoClass("checked", true)
		end


	end

end

--- Creates an element for a faction list entry
--- @param entry string the faction acronym
--- @return Element li_el the new element
function RequisitionsController:createFactionListItem(entry)

	local li_el = self.Document:CreateElement("li")

	local faction = Factions:getFactionByAcronym(entry)


	li_el.inner_rml = "<img src=\"" .. faction.Logo .. " class=\"psuedo_img\"></img>" .. faction.DisplayName .. ""
	li_el.id = entry

	li_el:SetClass("requisitionslogo_element", true)
	li_el:SetClass("button_1", true)
	li_el:SetClass("req_logo", true)
	li_el:SetClass("req_logo", true)
	li_el:AddEventListener("click", function(_, _, _)
		self:changeFaction(entry)
	end)

	return li_el
end

--- Creates the faction list
--- @param list backstock_requisition_item[] a list of items to get factions from
--- @return nil
function RequisitionsController:createFactionsList(list)

	local list_el = self.Document:GetElementById("list_logos_ul")

	ScpuiSystem:clearEntries(list_el)

	self.Visible_Factions = {}
	local faction_count = 0

	--List which factions to create
	for i, v in ipairs(list) do
		if not self:checkForFaction(self.Visible_Factions, v.faction) then
			self.Visible_Factions[faction_count + 1] = v.faction
			Md.print("REQUISITIONS Found faction " .. i .. ": " .. v.faction)
			faction_count = faction_count + 1
		end
	end

	for i, v in ipairs(self.Visible_Factions) do
		Md.print("REQUISITIONS Adding faction " .. i .. ": " .. v)
		list_el:AppendChild(self:createFactionListItem(v))
	end
end

--- Checks if a faction is in the list
--- @param tab string[] the list to check
--- @param val string the faction to check for
--- @return boolean result true if the faction is in the list
function RequisitionsController:checkForFaction(tab, val)
	for i, v in ipairs(tab) do
		if v == val then
			return true
		end
	end

	return false
end

--- Creates an element for a requisition list entry
--- @param entry backstock_requisition_item the requisition item
--- @param index number the index of the item in the list
--- @return Element li_el the new element
function RequisitionsController:createEntryListItem(entry, index)

	local li_el = self.Document:CreateElement("li")

	local display_name = entry.name

	--For weapons let's use the tech title for the list instead
	if self.SelectedData ~= "ships" then
		display_name = tb.WeaponClasses[entry.name].Title
	else
		display_name = tb.ShipClasses[entry.name].AltName
	end

	local amount_remaining = entry.amount

	li_el.inner_rml = "<span class=\"requisitionslist_left\">" .. display_name .. "</span><span class=\"requisitionslist_right\">" .. amount_remaining .. "</span>"
	li_el.id = entry.name

	li_el:SetClass("requisitionslist_element", true)
	li_el:SetClass("button_1", true)
	li_el:AddEventListener("click", function(_, _, _)
		self:selectEntry(Backstock.current.requisitions[self.SelectedData][index].key)
	end)

	Backstock.current.requisitions[self.SelectedData][index].key = li_el.id

	return li_el
end

--- Creates the requisitions list of items
--- @param local_list backstock_requisition_item[] a list of items to create
--- @return nil
function RequisitionsController:createEntries(local_list)

	local list_el = self.Document:GetElementById("list_items_ul")

	ScpuiSystem:clearEntries(list_el)
	self.Document:GetElementById("requisition_amount_input"):SetClass("hidden", false)
	self.Document:GetElementById("submit_cont"):SetClass("hidden", false)


	for i, v in ipairs(local_list) do
		if v.faction == self.SelectedFaction then
			Md.print("INVENTORY Adding item " .. i .. ": " .. v.name .. " for faction " .. v.faction)
			list_el:AppendChild(self:createEntryListItem(v, i))
		end
	end
end

--- Sets the currently selected entry as not active or checked
--- @return nil
function RequisitionsController:clearEntry()
	self.Document:GetElementById(self.SelectedEntry):SetPseudoClass("checked", false)
	self.SelectedEntry = nil
end

--- Clears all data from the requisitions view
--- @return nil
function RequisitionsController:clearData()
	ScpuiSystem.data.memory.model_rendering = nil
	local ani_wrapper_el = self.Document:GetElementById("requisitions_view")
	ani_wrapper_el:RemoveChild(ani_wrapper_el.first_child)
	self.Document:GetElementById("requisitions_title").inner_rml = "<p>" .. Utils.xstr(self.Strings.NoReqs) .. "</p>"
	self.Document:GetElementById("requisitions_owned").inner_rml = "<p></p>"
	self.Document:GetElementById("requisitions_available").inner_rml = "<p></p>"
	self.Document:GetElementById("requisitions_desc").inner_rml = "<p></p>"
	--self.Document:GetElementById("requisitions_requiredcp"):SetClass("hidden", true)
	self.Document:GetElementById("requisition_amount_input"):SetClass("hidden", true)
	self.Document:GetElementById("submit_cont"):SetClass("hidden", true)
end

--- Selects an entry in the requisitions list
--- @param key string the key of the item to select
--- @return nil
function RequisitionsController:selectEntry(key)

	--Find default item data
	local default_index = Utils.getTableIndex(Backstock.default[self.SelectedData], key)
	---@type backstock_requisition_item
	local default_data = Backstock.default[self.SelectedData][default_index]
	Md.print("REQUISITIONS found " .. key .. " in default table " .. self.SelectedData .. " at index " .. default_index)

	--Find save item data
	local save_index = Utils.getTableIndex(Backstock.current[self.SelectedData], key)
	local save_data = Backstock.current[self.SelectedData][save_index]
	Md.print("REQUISITIONS found " .. key .. " in save table " .. self.SelectedData .. " at index " .. save_index)

	--Find requisitions item data
	local req_index = Utils.getTableIndex(Backstock.current.requisitions[self.SelectedData], key)
	local req_data = Backstock.current.requisitions[self.SelectedData][req_index]
	Md.print("REQUISITIONS found " .. key .. " in requisitions table " .. self.SelectedData .. " at index " .. req_index)

	--Save current selection in case player submits a requisition!
	self.CurrentSelection = req_data

	if self.SelectedData == "ships" then
		local this_ship_class = tb.ShipClasses[self.CurrentSelection.name]
		self.CurrentSelection.displayName = this_ship_class.AltName
	else
		local this_weapon_class = tb.WeaponClasses[self.CurrentSelection.name]
		self.CurrentSelection.displayName = this_weapon_class.AltName
	end

	if key ~= self.SelectedEntry and default_index ~= "" then

		ScpuiSystem.data.memory.model_rendering = {}
		ScpuiSystem.data.memory.model_rendering.Draw = true
		ScpuiSystem.data.memory.model_rendering.RotationSpeed = 40
		local ani_wrapper_el = self.Document:GetElementById("requisitions_view")
		ani_wrapper_el:RemoveChild(ani_wrapper_el.first_child)

		if self.SelectedEntry then
			local old_entry = self.Document:GetElementById(self.SelectedEntry)
			if old_entry then old_entry:SetPseudoClass("checked", false) end
		end

		Md.print("REQUISITIONS getting id for " .. key)
		local this_entry = self.Document:GetElementById(key)
		self.SelectedEntry = key
		this_entry:SetPseudoClass("checked", true)

		self.CurrentKey = key

		local amount_remaining = Backstock.current.requisitions[self.SelectedData][req_index].amount
		local amount_owned = Backstock.current[self.SelectedData][save_index].remaining or 0
		local item_cost = Backstock.current.requisitions[self.SelectedData][req_index].cost

		self.Document:GetElementById("requisitions_title").inner_rml = "<p>" .. self.CurrentSelection.displayName .. "</p>"
		self.Document:GetElementById("requisitions_owned").inner_rml = "<p>" .. Utils.xstr(self.Strings.Owned) .. ": " .. amount_owned .. "</p>"
		self.Document:GetElementById("requisitions_available").inner_rml = "<p>" .. Utils.xstr(self.Strings.Available) .. ": " .. amount_remaining .. "</p>"
		self.Document:GetElementById("requisitions_cost").inner_rml = "<p>" .. Utils.xstr(self.Strings.Cost) .. ": " .. item_cost .. " " .. Utils.xstr(self.Strings.CommandPoints) .. "<br></br>" .. Utils.xstr(self.Strings.PerItem) .. "</p>"

		--Decide if item is a weapon or a ship
		if self.SelectedData == "ships" then
			local this_ship_class = tb.ShipClasses[key]

			if self.FirstRun == false then
				async.run(function()
					async.await(AsyncUtil.wait_for(0.001))
					ScpuiSystem.data.memory.model_rendering.Class = key
					ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("requisitions_view")
					self.FirstRun = true
				end, async.OnFrameExecutor)
			else
				ScpuiSystem.data.memory.model_rendering.Class = key
				ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("requisitions_view")
			end

			--Let's convert PowerOutput to something interesting
			local power = math.ceil(this_ship_class.PowerOutput * 10) * 100
			local this_ship_power = power .. Utils.xstr(self.Strings.PowerSuffix)
			local this_ship_tech_desc = Topics.ships.description:send(this_ship_class)

			-- Build the RML string step-by-step
			local rml_parts = {
				"<p>",
				this_ship_class.Description,
				"<br></br><br></br>",
				Utils.xstr(self.Strings.FactionIntro) .. " " .. default_data.faction .. ".",
				"<br></br><br></br>",
				Utils.xstr(self.Strings.Type) .. ": <span class=\"white\">" .. this_ship_class.TypeString .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Maneuver) .. ": <span class=\"white\">" .. this_ship_class.ManeuverabilityString .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Armor) .. ": <span class=\"white\">" .. this_ship_class.ArmorString .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Manufacturer) .. ": <span class=\"white\">" .. this_ship_class.ManufacturerString .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Afterburn) .. ": <span class=\"white\">" .. this_ship_class.AfterburnerFuelMax .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Countermeasures) .. ": <span class=\"white\">" .. this_ship_class.CountermeasuresMax .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Power) .. ": <span class=\"white\">" .. this_ship_power .. "</span>",
				"<br></br><br></br>",
				"<div class=\"hr\"></div>",
				this_ship_tech_desc,
				"</p>"
			}

			-- Concatenate all parts into the final RML string
			self.Document:GetElementById("requisitions_desc").inner_rml = table.concat(rml_parts)

		else
			local this_weapon_class = tb.WeaponClasses[key]

			if self.FirstRun == false then
				async.run(function()
					async.await(AsyncUtil.wait_for(0.001))
					ScpuiSystem.data.memory.model_rendering.Class = key
					ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("requisitions_view")
					self.FirstRun = true
				end, async.OnFrameExecutor)
			else
				ScpuiSystem.data.memory.model_rendering.Class = key
				ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("requisitions_view")
			end

			--For weapons lets use the Tech Title instead
			self.Document:GetElementById("requisitions_title").inner_rml = "<p>" .. this_weapon_class.Title .. "</p>"

			--Handle custom data
			local stats = Topics.weapons.stats:send(this_weapon_class)
			local this_weapon_creator = default_data.creator
			local this_weapon_type = default_data.category
			local this_weapon_range = math.floor(stats.Range) .. " " .. Utils.xstr(self.Strings.RangeSuffix)
			local this_weapon_speed = stats.Velocity .. " " .. Utils.xstr(self.Strings.SpeedSuffix)
			local this_weapon_armor = math.floor(stats.HullDamage / this_weapon_class.FireWait) .. " " .. Utils.xstr(self.Strings.DPSSuffix)
			local this_weapon_shield = math.floor(stats.ShieldDamage / this_weapon_class.FireWait) .. " " .. Utils.xstr(self.Strings.DPSSuffix)
			local this_weapon_subsystem = math.floor(stats.SubsystemDamage / this_weapon_class.FireWait) .. " " .. Utils.xstr(self.Strings.DPSSuffix)
			local this_weapon_cargo_size = "" --Only display this for secondaries
			local this_weapon_tech_desc = Topics.weapons.description:send(this_weapon_class)

			--Handle data only for Secondaries
			if self.SelectedData == "secondaries" then
				this_weapon_cargo_size = Utils.xstr(self.Strings.CargoSize) .. ": <span class=\"white\">" .. this_weapon_class.CargoSize .. "</span>"
			end

			-- Build the RML string step-by-step
			local rml_parts = {
				"<p>",
				this_weapon_class.Title,
				"<br></br><br></br>",
				Utils.xstr(self.Strings.CreatedByIntro) .. " " .. this_weapon_creator .. ".",
				"<br></br><br></br>",
				Utils.xstr(self.Strings.Seeking) .. ": <span class=\"white\">" .. this_weapon_type .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Range) .. ": <span class=\"white\">" .. this_weapon_range .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.Speed) .. ": <span class=\"white\">" .. this_weapon_speed .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.ArmorFactor) .. ": <span class=\"white\">" .. this_weapon_armor .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.ShieldFactor) .. ": <span class=\"white\">" .. this_weapon_shield .. "</span>",
				"<br></br>",
				Utils.xstr(self.Strings.SubsystemFactor) .. ": <span class=\"white\">" .. this_weapon_subsystem .. "</span>",
				"<br></br>",
				this_weapon_cargo_size,
				"<br></br>",
				"<div class=\"hr\"></div>",
				this_weapon_tech_desc,
				"</p>"
			}

			-- Concatenate all parts into the final RML string
			self.Document:GetElementById("requisitions_desc").inner_rml = table.concat(rml_parts)

		end

	end

end

--- Draw the model for the current requisition every frame
--- @return nil
function RequisitionsController:drawModel()

	if ScpuiSystem.data.memory.model_rendering and ScpuiSystem.data.Substate == "Requisitions" and ScpuiSystem.data.memory.model_rendering.Draw == true then  --Haaaaaaacks
		local this_object_class = nil

		if ScpuiSystem.data.memory.model_rendering.Class == nil then return end

		if ScpuiSystem.dialogActive == true then
			return
		end

		if self.SelectedData == "ships" then
			this_object_class = tb.ShipClasses[ScpuiSystem.data.memory.model_rendering.Class]
		else
			this_object_class = tb.WeaponClasses[ScpuiSystem.data.memory.model_rendering.Class]
		end

		ScpuiSystem.data.memory.model_rendering.RotationSpeed = ScpuiSystem.data.memory.model_rendering.RotationSpeed + (7 * ba.getRealFrametime())

		if ScpuiSystem.data.memory.model_rendering.RotationSpeed >= 100 then
			ScpuiSystem.data.memory.model_rendering.RotationSpeed = ScpuiSystem.data.memory.model_rendering.RotationSpeed - 100
		end

		local model_element = ScpuiSystem.data.memory.model_rendering.Element

		if not model_element then return end

		local x = ScpuiSystem:getAbsoluteLeft(model_element) + 25
		local y = ScpuiSystem:getAbsoluteTop(model_element) - model_element.offset_top - 40 --Does not include modelView.offset_top because that element's padding is set for anims also subtracts 7px for funsies
		local w = model_element.offset_width
		local h = model_element.offset_height

		this_object_class:renderTechModel(x, y, x + w, y + h, ScpuiSystem.data.memory.model_rendering.RotationSpeed, -15, 0, 1.1)
	end

end

--- This really just serves to concatentate a bunch of things for styling and xstrs; makes the script more readable
--- @param success boolean if the requisition was a success
--- @param amount number the amount requisitioned
--- @return string finalString the formatted string
function RequisitionsController:generateDialogText(success, amount)

	local span_open_1 = "<span class=\"h1\" style=\"color:grey;margin-left:10%;\">"
	local span_open_2 = "<span class=\"h2\" style=\"margin-left:5%;\">"
	local span_open_3 = "<span class=\"h1\" style=\"color:grey;margin-left:5%;\">"
	local span_open_4 = "<span class=\"h1\" style=\"color:red;margin-left:5%;\">"
	local span_close = "</span>"

	local final_string = ""

	if success then
		--Add number requisitions and how much it cose
		local this_string = span_open_2 .. Utils.xstr(self.Strings.SuccessText1) .. " " .. tostring(amount) .. " " .. self.CurrentSelection.displayName
		if amount > 1 then
			this_string = this_string .. "s" .. span_close
		else
			this_string = this_string .. span_close
		end
		--Add line break and total cost
		this_string = this_string .. "<br></br>" .. span_open_1 .. Utils.xstr(self.Strings.SuccessText2) .. ": " .. self.TotalCost .. " " .. Utils.xstr(self.Strings.CommandPoints) .. span_close

		--Add line break and explainer
		this_string = this_string .. "<br></br>" .. span_open_1 .. Utils.xstr(self.Strings.SuccessText3) .. span_close

		--Add line break and total Command Points remaining
		this_string = this_string .. "<br></br><br></br><br></br>" .. span_open_3 .. Utils.xstr(self.Strings.SuccessText4) .. ": " .. Backstock.current.playerCP .. span_close

		final_string = this_string
	else
		--Add warning that requisition failed
		local this_string = "<br></br><br></br>" .. span_open_4 .. Utils.xstr(self.Strings.FailureText) .. span_close

		--Add Command Points needed
		this_string = this_string .. "<br></br><br></br>" .. span_open_1 .. Utils.xstr(self.Strings.NeededCP) .. " " .. self.TotalCost .. " " .. Utils.xstr(self.Strings.CommandPoints) .. span_close

		--Add Command Points remaining
		this_string = this_string .. "<br></br>" .. span_open_1 .. Utils.xstr(self.Strings.CurrentCP) .. " " .. Backstock.current.playerCP .. " " .. Utils.xstr(self.Strings.CommandPoints) .. span_close

		final_string = this_string
	end
	return final_string
end


--- Check if the submitted requisition is possible based on amounts and cost
--- @param num_items number the amount to requisition
--- @return nil
function RequisitionsController:checkRequisitionCost(num_items)

	--Continue if submit contains a value, otherwise error prompt
	if num_items > 0 then
		local item_cost = self.CurrentSelection.cost

		--If submitted value is larger than what's available, just offer everything available.
		if num_items > self.CurrentSelection.amount then
			num_items = self.CurrentSelection.amount
		end

		--Calculate total cost of the Requisition
		self.TotalCost = num_items * item_cost
		if self.TotalCost > Backstock.current.playerCP then
			Md.print("REQUISITIONS player didn't have enough CP. Needed " .. self.TotalCost .. ", had " .. Backstock.current.playerCP)
			self.SubmittedValue = 0
			self.InputElement:SetAttribute("value", "")
			--Send a failure popup
			local text = self:generateDialogText(false, num_items)
			local title = "<span style=\"font-size:1.8em;color:#AD7F7F;\">" .. Utils.xstr(self.Strings.FailureTitle) .. "</span>"
			self:showDialog(text, title)
			return
		else
			self.SubmittedValue = 0
			self.InputElement:SetAttribute("value", "")
			self:submitRequisition(num_items, self.TotalCost)
		end

	else
		Md.print("REQUISITIONS player submitted an empty value")
		--Send a failure popup
		local text = "<br></br><br></br><span class=\"h1\" style=\"color:red;margin-left:5%;\">" .. Utils.xstr(self.Strings.EmptyReturn) .. "</span>"
		local title = "<span class=\"h1\" style=\"color:#AD7F7F;\">" .. Utils.xstr(self.Strings.FailureTitle) .. "</span>"
		self:showDialog(text, title)
	end

end

--- Here is where we actually subtract the submition from the loaded requisition and then reload the document.
--- @param final_amount number the amount to requisition
--- @param final_cost number the cost of the requisition
--- @return nil
function RequisitionsController:submitRequisition(final_amount, final_cost)

	self.CurrentSelection.amount = self.CurrentSelection.amount - final_amount
	Backstock.current.playerCP = Backstock.current.playerCP - final_cost

	local inventory_table = Backstock.current[self.SelectedData]

	assert(inventory_table, "Inventory table was nil!")

	if self:checkExistsInTable(inventory_table, self.CurrentSelection.name) then
		for i, v in ipairs(inventory_table) do
			if v.name == self.CurrentSelection.name then
				v.remaining = v.remaining + final_amount
			end
		end
	else
		local index = #inventory_table
		Backstock.current[self.SelectedData][index] = {
			name = self.CurrentSelection.name,
			amount = final_amount,
			unlocked = true
		}
	end
	--Send a success popup
	local text = self:generateDialogText(true, final_amount)
	local title = "<span style=\"font-size:1.8em;color:#85AD7F;\">" .. Utils.xstr(self.Strings.SuccessTitle) .. "</span>"
	self:showDialog(text, title)

	self.RecentSection = self.CurrentSection
	self.RecentFaction = self.CurrentFaction
	self.RecentKey = self.CurrentKey

	self:saveChanges()

	--Set var to indicate saved loadouts must not be used
	Backstock.updated = true

	self:loadData()
	--self:ChangeSection(self.currentSection)
	self:refreshLists()

end

--- Save the requisition changes to the save file
--- @return nil
function RequisitionsController:saveChanges()

	Md.print("REQUISITIONS saving data...")

	--Clean the table
	Backstock:CleanTable(Backstock.current.requisitions.ships)
	Backstock:CleanTable(Backstock.current.requisitions.primaries)
	Backstock:CleanTable(Backstock.current.requisitions.secondaries)

	Backstock:SaveChanges()

end

--- Show a dialog box with the given text and title
--- @param text string the text to display
--- @param title string the title to display
--- @return nil
function RequisitionsController:showDialog(text, title)
	--Create a simple dialog box with the text and title

	ScpuiSystem.data.memory.model_rendering.Draw = false
	ScpuiSystem.dialogActive = true

	local dialog = Dialogs.new()
		dialog:title(title)
		dialog:text(text)
		dialog:button(Dialogs.BUTTON_TYPE_POSITIVE, Utils.xstr(self.Strings.Okay), true, self.OkayKeypress)
		dialog:escape(true)
		dialog:show(self.Document.context)
		:continueWith(function()
			if ScpuiSystem.data.memory.model_rendering then
				ScpuiSystem.data.memory.model_rendering.Draw = true
			end
			ScpuiSystem.dialogActive = nil
		end)
	-- Route input to our context until the user dismisses the dialog box.
	ui.enableInput(self.Document.context)
end

--- Check if a value is in a table
--- @param tab backstock_item[]|backstock_requisition the table to check
--- @param val any the value to check for
--- @return boolean result true if the value is in the table
function RequisitionsController:checkExistsInTable(tab, val)

	for i, v in ipairs(tab) do
		if v.name == val then
			return true
		end
	end
	return false

end

--- Called by the RML when the input element loses focus
--- @return nil
function RequisitionsController:input_focus_lost()
	self.InputActive = false
end

--- Called by the RML when the submit button is pressed
--- @return nil
function RequisitionsController:submit_pressed()
	if self.SubmittedValue then
		self:checkRequisitionCost(self.SubmittedValue)
	end
end

--- Called by the RML when the input element accepts a keypress
--- @param event Event the event that was triggered
--- @return nil
function RequisitionsController:input_change(event)

	--remove all whitespace
	local string_value = event.parameters.value:gsub("^%s*(.-)%s*$", "%1")
	--sorry, no dots either!
	string_value = string.gsub(string_value, "%.", "")

	--If RETURN was not pressed then make sure the input is a number with no spaces. Else save the value, clear the field, and continue.
	if event.parameters.linebreak ~= 1 then
		local value = tonumber(string_value)
		if type(value) == "number" then
			self.InputElement:SetAttribute("value", string_value)
		else
			--remove the trailing character because it was not a number
			string_value = string_value:sub(1, -2)
			self.InputElement:SetAttribute("value", string_value)
		end

		local val = self.InputElement:GetAttribute("value")

		if val == "" then
			val = 0
		else
			val = tonumber(val)
		end

		self.SubmittedValue = val
	else
		local submit_id = self.Document:GetElementById("submit_btn")
		ui.playElementSound(submit_id, "click")
		Md.print("REQUISITIONS got submition for " .. string_value .. " " .. self.CurrentSelection.name .. "s from faction " .. self.CurrentSelection.faction)
		self:checkRequisitionCost(self.SubmittedValue)
	end

	self:updateCost()

end

--- Update the total cost element
--- @return nil
function RequisitionsController:updateCost()
	--Now adjust the visible math
	local val = self.InputElement:GetAttribute("value")
	if val == "" then
		val = 0
	else
		val = tonumber(val)
	end
	local visible_value = val * self.CurrentSelection.cost
	self.Document:GetElementById("requisitions_requiredcp").inner_rml = "<p>" .. Utils.xstr(self.Strings.Selection) .. ": " .. visible_value .. " " .. Utils.xstr(self.Strings.CommandPoints) .. "</p>"
end

--- Called by the RML when the exit button is pressed
--- @return nil
function RequisitionsController:exit(element)
    ui.playElementSound(element, "click", "success")
	ScpuiSystem:beginSubstate("Inventory")
	self.Document:Close()
end

--- Global keydown function handles all keypresses
--- @param element Element The main document element
--- @param event Event The event that was triggered
--- @return nil
function RequisitionsController:global_keydown(element, event)
    if event.parameters.key_identifier == rocket.key_identifier.ESCAPE then
        event:StopPropagation()
		ScpuiSystem:beginSubstate("Inventory")
		self.Document:Close()
    end
end

--- Run all the frame functions
--- @return nil
function RequisitionsController:unload()
	ScpuiSystem.RequisitionsContext = nil
    ScpuiSystem:freeAllModels()
end

engine.addHook("On Frame", function()
	if ScpuiSystem.RequisitionsContext then
		if ScpuiSystem.data.Render == true then
			ScpuiSystem.RequisitionsContext:drawModel()
		end
	end
end, {State="GS_STATE_SCRIPTING"}, function()
	return false
end)

return RequisitionsController
