-----------------------------------
--Controller for the Scan Reader UI
-----------------------------------

local Topics = require("lib_ui_topics")

local Class = require("lib_class")

local ScanReaderController = Class()

ScanReaderController.STATE_ONE = 1
ScanReaderController.STATE_TWO = 2
ScanReaderController.STATE_THREE = 3
ScanReaderController.STATE_FOUR = 4

--- Called by the class constructor
--- @return nil
function ScanReaderController:init()
	self.Document = nil ---@type Document the RML document
	self.Visible_List = {} --- @type scan_reader_scan[] The list of visible entries
	self.SelectedEntry = nil --- @type scan_reader_scan The currently selected entry
	self.SelectedIndex = nil --- @type number The index of the currently selected entry
	self.Audio = nil --- @type audio_stream The currently playing audio handle
	self.HelpShown = false --- @type boolean Whether the help text is shown
end

--Iterate over all the ships, weapons, and intel but only grab the necessary data
function ScanReaderController:LoadData()
	require('scanreader_utils')

	local scans = scanreader_utils:getScans()
	if #scans > 0 then
		self:createScanEntries(scans)
	end
end

--- Called by the RML document
--- @param document Document
function ScanReaderController:initialize(document)
    self.Document = document

	---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("scan_reader_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("tech_btn_1"):SetPseudoClass("checked", false)
	self.Document:GetElementById("tech_btn_2"):SetPseudoClass("checked", false)
	self.Document:GetElementById("tech_btn_3"):SetPseudoClass("checked", false)
	self.Document:GetElementById("tech_btn_4"):SetPseudoClass("checked", true)

	Topics.techroom.initialize:send(self)

	self.Document:GetElementById("play_cont"):SetClass("hidden", true)
	self.Document:GetElementById("stop_cont"):SetClass("hidden", true)

	--Get all the data fresh each time in case there are changes
	self:LoadData()

	self.SelectedEntry = nil

	if #self.Visible_List > 0 then
		self:selectScanEntry(self.Visible_List[1])
	end

end

--- Called by the RML document to change tech room states
--- @param state number The new state. Should be one of the STATE_ enumerations
--- @return nil
function ScanReaderController:change_tech_state(state)

	if state == self.STATE_ONE then
		Topics.techroom.btn1Action:send()
	end
	if state == self.STATE_TWO then
		Topics.techroom.btn2Action:send()
	end
	if state == self.STATE_THREE then
		Topics.techroom.btn3Action:send()
	end
	if state == self.STATE_FOUR then
		--This is where we are already, so don't do anything
		--topics.techroom.btn4Action:send()
	end

end

--- Create a list item element for a scan entry
--- @param entry scan_reader_scan The scan entry
--- @param index number The index of the entry
--- @return Element el The list item element
function ScanReaderController:createEntryListItem(entry, index)

	local li_el = self.Document:CreateElement("li")
	li_el:SetClass("list_element", true)

	local display_name = ba.XSTR(entry.Name, entry.Xstr)
	entry.DisplayName = display_name

	local name = "<span>" .. display_name .. "</span>"
	local new = "<span style=\"color:red;margin-left:10;\">" .. ba.XSTR("NEW!", 9294) .. "</span>"

	local display = name

	if entry.Seen == false then
		display = display .. new
	end

	li_el.inner_rml = display
	li_el.id = entry.Name

	entry.Key = li_el.id
	entry.Index = index

	table.insert(self.Visible_List, entry)

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

	return li_el
end

--- Create the list of scan entries
--- @param list scan_reader_scan[] The list of scan entries
--- @return nil
function ScanReaderController:createScanEntries(list)

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

	ScpuiSystem:clearEntries(list_names_el)

	for i, v in ipairs(list) do
		list_names_el:AppendChild(self:createEntryListItem(v, i))
	end
end

--- Select a scan entry
--- @param entry scan_reader_scan The scan entry to select
--- @return nil
function ScanReaderController:selectScanEntry(entry)

	if entry == nil then
		return
	end

	if (self.SelectedEntry == nil) or (entry.Key ~= self.SelectedEntry.Key) then
		self.Document:GetElementById(entry.Key):SetPseudoClass("checked", true)

		self.SelectedIndex = entry.Index

		local text_el = self.Document:GetElementById("scan_view")
		ScpuiSystem:clearEntries(text_el)

		if self.SelectedEntry then
			local old_entry = self.Document:GetElementById(self.SelectedEntry.Key)
			if old_entry then
				old_entry:SetPseudoClass("checked", false)
				if self.SelectedEntry.Seen == false then
					--Set as seen and save
					require('scanreader_utils')
					scanreader_utils:setSeen(self.SelectedEntry.Name)
					--Make sure we only remove "NEW!"
					if old_entry.last_child.inner_rml == "NEW!" then
						old_entry:RemoveChild(old_entry.last_child)
					end
				end
			end
		end

		local thisEntry = self.Document:GetElementById(entry.Key)
		self.SelectedEntry = entry
		thisEntry:SetPseudoClass("checked", true)

		self:stop_audio()

		--Set the description text
		text_el.inner_rml = entry.Text or ''
		text_el.scroll_top = 0
	end


end

--- Set an entry in the list as not selected
--- @return nil
function ScanReaderController:clearEntry()

	self.Document:GetElementById(self.SelectedEntry.Key):SetPseudoClass("checked", false)
	self.SelectedEntry = nil

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 ScanReaderController:global_keydown(element, event)
    if event.parameters.key_identifier == rocket.key_identifier.ESCAPE then
        event:StopPropagation()

        ba.postGameEvent(ba.GameEvents["GS_EVENT_MAIN_MENU"])
	elseif event.parameters.key_identifier == rocket.key_identifier.UP and event.parameters.ctrl_key == 1 then
		self:change_tech_state(self.STATE_TWO)
	elseif event.parameters.key_identifier == rocket.key_identifier.DOWN and event.parameters.ctrl_key == 1 then
		self:change_tech_state(self.STATE_FOUR)
	elseif event.parameters.key_identifier == rocket.key_identifier.UP and event.parameters.shift_key == 1 then
		self:scrollList(self.Document:GetElementById("scan_list"), 0)
	elseif event.parameters.key_identifier == rocket.key_identifier.DOWN and event.parameters.shift_key == 1 then
		self:scrollList(self.Document:GetElementById("scan_list"), 1)
	elseif event.parameters.key_identifier == rocket.key_identifier.UP then
		self:scrollText(self.Document:GetElementById("scan_view"), 0)
	elseif event.parameters.key_identifier == rocket.key_identifier.DOWN then
		self:scrollText(self.Document:GetElementById("scan_view"), 1)
	elseif event.parameters.key_identifier == rocket.key_identifier.LEFT then
		self:select_prev(self.Document:GetElementById("scan_view"))
	elseif event.parameters.key_identifier == rocket.key_identifier.RIGHT then
		self:select_next(self.Document:GetElementById("scan_view"))
	elseif event.parameters.key_identifier == rocket.key_identifier.RETURN then
		--self:commit_pressed(element)
	elseif event.parameters.key_identifier == rocket.key_identifier.F1 then
		self:help_clicked(element)
	elseif event.parameters.key_identifier == rocket.key_identifier.F2 then
		self:options_button_clicked(element)
	end
end

--- Scroll the list element
--- @param element Element The list element
--- @param direction number The direction to scroll (0 = up, 1 = down)
--- @return nil
function ScanReaderController:scrollList(element, direction)
	if direction == 0 then
		element.scroll_top = element.scroll_top - 15
	else
		element.scroll_top = element.scroll_top + 15
	end
end

--- Scroll the text element
--- @param element Element The text element
--- @param direction number The direction to scroll (0 = up, 1 = down)
--- @return nil
function ScanReaderController:scrollText(element, direction)
	if direction == 0 then
		element.scroll_top = (element.scroll_top - 5)
	else
		element.scroll_top = (element.scroll_top + 5)
	end
end

--- Called by the RML to select the next scan
--- @param element Element The button element
--- @return nil
function ScanReaderController:select_next(element)
	if #self.Visible_List == 0 then return end

    local num = #self.Visible_List

	if self.SelectedIndex == num then
		ui.playElementSound(element, "click", "error")
	else
		local count = 1
		while self.Visible_List[self.SelectedIndex + count] ~= nil do
			count = count + 1
		end

		if (self.SelectedIndex + count) > num then
			ui.playElementSound(element, "click", "error")
		else
			self:selectScanEntry(self.Visible_List[self.SelectedIndex + count])
		end
	end
end

--- Called by the RML to select the previous scan
--- @param element Element The button element
--- @return nil
function ScanReaderController:select_prev(element)
	if #self.Visible_List == 0 then return end

	if self.SelectedIndex == 1 then
		ui.playElementSound(element, "click", "error")
	else
		local count = 1
		while self.Visible_List[self.SelectedIndex - count] ~= nil do
			count = count + 1
		end

		if (self.SelectedIndex - count) < 1 then
			ui.playElementSound(element, "click", "error")
		else
			self:selectScanEntry(self.Visible_List[self.SelectedIndex - count])
		end
	end
end

--- Wait for a sound to finish playing
--- @param sound audio_stream The sound to wait for
--- @return promise
function ScanReaderController:waitForSound(sound)
	return async.run(function()
		while sound ~= nil and sound:isPlaying() do
			async.await(async.yield())
		end
	end, async.OnFrameExecutor)
end

--- Called by the RML to play the audio for the selected scan
--- @return nil
function ScanReaderController:play_audio()
	if self.SelectedEntry == nil then return end

	if self.Audio ~= nil then
		self:stop_audio()
	end

	self.Audio = ad.openAudioStream(self.SelectedEntry.Audio, AUDIOSTREAM_VOICE)
	self.Audio:play()

	self.Document:GetElementById("play_cont"):SetClass("hidden", true)
	self.Document:GetElementById("stop_cont"):SetClass("hidden", false)

	async.run(function()
		--Wait for sound to be finished playing
		async.await(ScanReaderController:waitForSound(self.Audio))
		self:stop_audio()
	end, async.OnFrameExecutor)
end

--- Called by the RML to stop the audio for the selected scan
--- @return nil
function ScanReaderController:stop_audio()
	if self.Audio ~= nil and self.Audio:isValid() then
		self.Audio:stop()
	end

	if self.SelectedEntry ~= nil then
		if self.SelectedEntry.Audio ~= "<none>" then
			self.Document:GetElementById("play_cont"):SetClass("hidden", false)
		else
			self.Document:GetElementById("play_cont"):SetClass("hidden", true)
		end

		self.Document:GetElementById("stop_cont"):SetClass("hidden", true)
	end

	self.Audio = nil
end

--- Called by the RML to leave the scan reader
--- @param element Element The button element
--- @return nil
function ScanReaderController:accept_pressed(element)
    ui.playElementSound(element, "click", "success")
    ba.postGameEvent(ba.GameEvents["GS_EVENT_MAIN_MENU"])
end

--- Called by the RML to open the options menu
--- @param element Element The button element
--- @return nil
function ScanReaderController:options_button_clicked(element)
    ui.playElementSound(element, "click", "success")
    ba.postGameEvent(ba.GameEvents["GS_EVENT_OPTIONS_MENU"])
end

--- Called by the RML to show the help text
--- @param element Element The button element
--- @return nil
function ScanReaderController:help_clicked(element)
    ui.playElementSound(element, "click", "success")

	self.HelpShown  = not self.HelpShown

    local help_texts = self.Document:GetElementsByClassName("tooltip")
    for _, v in ipairs(help_texts) do
        v:SetPseudoClass("shown", self.HelpShown)
    end
end

--- Run all the frame functions
--- @return nil
function ScanReaderController:unload()
	self.SelectedEntry = nil
	self:stop_audio()
end

return ScanReaderController
