-----------------------------------
--Controller for the Inventory UI
-----------------------------------

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

local Class = require("lib_class")

local InventoryController = Class()

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

--- Called by the class constructor
--- @return nil
function InventoryController:init()
	self.Document = nil ---@type Document the RML document
	self.Category = nil ---@type string the current inventory category
	self.FirstRun = false --- @type boolean whether this is the first frame the inventory is being displayed
	self.SelectedEntry = nil ---@type string the currently selected entry
	self.SelectedSection = nil ---@type number the currently selected section

	--Translatable strings
	--- @type table<string, table<string, number>>
	self.Strings = {
		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},
		NoData = {"No Data Loaded", 11571}
	}

	ScpuiSystem.data.memory.model_rendering = nil

end

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

	if not Backstock then
		ba.error("Backstock 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("inventory_title"):SetClass("h2", true)
	self.Document:GetElementById("inventory_count"):SetClass("h1", true)
	self.Document:GetElementById("req_text"):SetClass("h1", true)

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

	--If Requisitions is empty then hide the button completely
	local reqs_available = #Backstock.current.requisitions.ships + #Backstock.current.requisitions.primaries + #Backstock.current.requisitions.secondaries
	Md.print("INVENTORY detected num requisitions at " .. reqs_available)
	if reqs_available == 0 then
		Md.print("INVENTORY is hiding the Requisitions button!")
		self.Document:GetElementById("top_panel_a"):SetClass("hidden", true)
	end

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

	self:change_section(ScpuiSystem.InventorySection)

end

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

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

	if section ~= self.SelectedSection then

		ScpuiSystem.InventorySection = section

		local item_list = nil
		local save_list = nil

		if section == self.SECTION_SHIPS then
			item_list = Backstock.default.ships
			save_list = Backstock.current.ships
			self.Category = "ships"
		elseif section == self.SECTION_PRIMARIES then
			item_list = Backstock.default.primaries
			save_list = Backstock.current.primaries
			self.Category = "primaries"
		elseif section == self.SECTION_SECONDARIES then
			item_list = Backstock.default.secondaries
			save_list = Backstock.current.secondaries
			self.Category = "secondaries"
		end

		Md.print("INVENTORY setting selected data type to " .. self.Category)

		assert(item_list, "No item list found!")
		assert(save_list, "No save list found!")

		if self.SelectedEntry then
			self:clearEntry()
		end

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

		--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 locked. In that case, auto select the next one instead.
			for i, v in ipairs(save_list) do
				if save_list[i].unlocked and save_list[i].remaining > 0 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

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


	end

end

--- Create a list item for the inventory list
--- @param entry string the name of the item to create
--- @return Element li_el the list item element
function InventoryController:createEntryListItem(entry)

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

	local display_name = entry

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

	--Find default item data
	local default_index = Utils.getTableIndex(Backstock.default[self.Category], entry)
	---@type backstock_item
	local default_data = Backstock.default[self.Category][default_index]

	--Find save item data
	local save_index = Utils.getTableIndex(Backstock.current[self.Category], entry)
	local save_data = Backstock.current[self.Category][save_index]

	local amount_remaining = save_data.remaining or default_data.default

	li_el.inner_rml = "<span class=\"inventorylist_left\">" .. display_name .. "</span><span class=\"inventorylist_right\">" .. amount_remaining .. "</span>"
	li_el.id = entry

	li_el:SetClass("inventorylist_element", true)
	li_el:SetClass("button_1", true)
	li_el:AddEventListener("click", function(_, _, _)
		self:selectEntry(default_data.key)
	end)

	default_data.key = li_el.id

	return li_el
end

--- Create all the list items for a given inventory category
--- @param section backstock_item[] the inventory category to create
--- @return nil
function InventoryController:createEntries(section)

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

	ScpuiSystem:clearEntries(list_el)

	for i, v in ipairs(section) do
		--Find save item data
		local save_index = Utils.getTableIndex(Backstock.current[self.Category], v.name)
		local save_data = Backstock.current[self.Category][save_index]

		if save_data.unlocked and save_data.remaining > 0 then
			Md.print("INVENTORY Adding entry " .. i .. ": " .. v.name)
			list_el:AppendChild(self:createEntryListItem(v.name))
		end
	end
end

--- Set the current entry as not selected
--- @return nil
function InventoryController:clearEntry()
	self.Document:GetElementById(self.SelectedEntry):SetPseudoClass("checked", false)
	self.SelectedEntry = nil
end

--- Clear all data from the inventory display
--- @return nil
function InventoryController:clearData()
	ScpuiSystem.data.memory.model_rendering = nil
	self.Document:GetElementById("inventory_title").inner_rml = "<p>" .. Utils.xstr(self.Strings.NoData) .. "</p>"
	self.Document:GetElementById("inventory_count").inner_rml = "<p></p>"
	self.Document:GetElementById("inventory_desc").inner_rml = "<p></p>"
end

--- Select an entry in the inventory
--- @param key string the name of the entry to select
--- @return nil
function InventoryController:selectEntry(key)

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

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


	if key ~= self.SelectedEntry then

		ScpuiSystem.data.memory.model_rendering = {}
		ScpuiSystem.data.memory.model_rendering.RotationSpeed = 40

		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("INVENTORY getting id for " .. key)
		local this_entry = self.Document:GetElementById(key)
		self.SelectedEntry = key
		this_entry:SetPseudoClass("checked", true)

		local amount_remaining = save_data.remaining or default_data.default

		self.Document:GetElementById("inventory_title").inner_rml = "<p>" .. default_data.name .. "</p>"
		self.Document:GetElementById("inventory_count").inner_rml = "<p>" .. Utils.xstr(self.Strings.Remaining).. ": " .. amount_remaining .. "</p>"

		--Decide if item is a weapon or a ship
		if self.Category == "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("inventory_view")
					self.FirstRun = true
				end, async.OnFrameExecutor)
			else
				ScpuiSystem.data.memory.model_rendering.Class = key
				ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("inventory_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("inventory_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("inventory_view")
					self.FirstRun = true
				end, async.OnFrameExecutor)
			else
				ScpuiSystem.data.memory.model_rendering.Class = key
				ScpuiSystem.data.memory.model_rendering.Element = self.Document:GetElementById("inventory_view")
			end

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

			--Handle custom data
			local stats = Topics.weapons.stats:send(this_weapon_class)
			local this_weapon_created = 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.Category == "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_created .. ".",
				"<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("inventory_desc").inner_rml = table.concat(rml_parts)

		end

	end


end

--- Draw the current entry model every frame
--- @return nil
function InventoryController:drawModel()

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

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

		if self.Category == "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 model_element == nil then return end

		local x = ScpuiSystem:getAbsoluteLeft(model_element) + 25
		local y = ScpuiSystem:getAbsoluteTop(model_element) - model_element.offset_top - 7 --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

--- Leaves the inventory and returns to the main menu
--- @return nil
function InventoryController:quit()
    ba.postGameEvent(ba.GameEvents["GS_EVENT_MAIN_MENU"])
    self.Document:Close()
end

--- Called by the RML document to exit the inventory
--- @param element Element The element that triggered the event
--- @return nil
function InventoryController:exit(element)
    ui.playElementSound(element, "click", "success")
	self:quit()
end

--- Called by the RML document to open the requisitions menu
--- @param element Element The element that triggered the event
--- @return nil
function InventoryController:requisitions_clicked(element)

    ui.playElementSound(element, "click", "success")
	ScpuiSystem:beginSubstate("Requisitions")
	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 InventoryController:global_keydown(element, event)
    if event.parameters.key_identifier == rocket.key_identifier.ESCAPE then
        event:StopPropagation()
		self:quit()
    end
end

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

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

return InventoryController
