--- Personnel Entry Override
--- @class personnel_entry_override
--- @field Key number The key of the override that should match a BtA Progression value
--- @field Biography string? The biography of the person
--- @field Locked boolean? Whether the person is locked
--- @field Callsign string? The callsign of the person
--- @field Rank string? The rank of the person
--- @field Position string? The position or job of the person
--- @field Posting string? The posting of the person
--- @field Status string? The status of the person, such as "Deceased" or "Missing in Action"
--- @field Commendations string? The commendations of the person
--- @field Height number? The height of the person
--- @field Weight number? The weight of the
--- @field Gender string? The gender of the person
--- @field Species string? The species of the person
--- @field Affiliation string? The affiliation of the person. Should be a faction acronym

--- Personnel Entry
--- @class personnel_entry
--- @field Name string The name of the person
--- @field File string The file name of the person's image
--- @field Biography string The biography of the person
--- @field Locked boolean Whether the person is locked
--- @field Callsign string? The callsign of the person
--- @field Rank string? The rank of the person
--- @field Position string? The position or job of the person
--- @field Posting string? The posting of the person
--- @field Status string? The status of the person, such as "Deceased" or "Missing in Action"
--- @field Commendations string? The commendations of the person
--- @field Height number? The height of the person
--- @field Weight number? The weight of the
--- @field Gender string? The gender of the person
--- @field Species string? The species of the person
--- @field Affiliation string? The affiliation of the person. Should be a faction acronym
--- @field Progression personnel_entry_override[]? The progression data of the person

Personnel = {
	entries = {},
}

--- Parse the personnel table
--- @param data string The file to parse
--- @return nil
function Personnel:parsePersonnel(data)

	parse.readFileText(data, "data/tables")

	parse.requiredString("#PERSONNEL")

	while parse.optionalString("$Name:") do
		--- @type personnel_entry
		local entry = {
			Name = "",
			File = "",
			Biography = "",
			Locked = false,
		}

		entry.Name = parse.getString()

		--Warn if character already exists
		for _, v in pairs(Personnel.entries) do
			if v.Name == entry.Name then
				ba.error("Character with Name '" .. entry.Name .. "' already exists. This needs to be fixed!")
			end
		end

		parse.requiredString("$Filename:")
		entry.File = parse.getString()

		parse.requiredString("$Biography:")
		entry.Biography = parse.getString()

		if parse.optionalString("+Locked:") then
			entry.Locked = parse.getBoolean()
		else
			entry.Locked = false
		end

		if parse.optionalString("+Callsign:") then
			entry.Callsign = parse.getString()
		end

		if parse.optionalString("+Rank:") then
			entry.Rank = parse.getString()
		end

		if parse.optionalString("+Position:") then
			entry.Position = parse.getString()
		end

		if parse.optionalString("+Posting:") then
			entry.Posting = parse.getString()
		end

		if parse.optionalString("+Status:") then
			entry.Status = parse.getString()
		end

		if parse.optionalString("+Commendations:") then
			local commendations = ""

			while parse.optionalString("+Entry:") do
				commendations = commendations .. parse.getString() .. ", "
			end

			-- Remove trailing ", " if present
			if commendations:sub(-2) == ", " then
				commendations = commendations:sub(1, -3)
			end

			entry.Commendations = commendations
		end

		if parse.optionalString("+Height:") then
			entry.Height = parse.getFloat()
		end

		if parse.optionalString("+Weight:") then
			entry.Weight = parse.getFloat()
		end

		if parse.optionalString("+Gender:") then
			entry.Gender = parse.getString()
		end

		if parse.optionalString("+Species:") then
			entry.Species = parse.getString()
		end

		if parse.optionalString("+Affiliation:") then
			entry.Affiliation = parse.getString() --Deliberately just save the acronym for now. We can resolve it when we load the UI because Factions may not be loaded yet
		end

		entry.Progression = {}
		while parse.optionalString("$Progression:") do
			--- @type personnel_entry_override
			local e = {
				Key = 0,
			}

			local BtA = require("btafunctions")
			e.Key = BtA:getGameProgress(parse.getString())

			if parse.optionalString("$Biography:") then
				e.Biography = parse.getString()
			end

			if parse.optionalString("+Locked:") then
				e.Locked = parse.getBoolean()
			end

			if parse.optionalString("+Callsign:") then
				e.Callsign = parse.getString()
			end

			if parse.optionalString("+Rank:") then
				e.Rank = parse.getString()
			end

			if parse.optionalString("+Position:") then
				e.Position = parse.getString()
			end

			if parse.optionalString("+Posting:") then
				e.Posting = parse.getString()
			end

			if parse.optionalString("+Status:") then
				e.Status = parse.getString()
			end

			if parse.optionalString("+Commendations:") then
				local commendations = ""

				while parse.optionalString("+Entry:") do
					commendations = commendations .. parse.getString() .. ", "
				end

				-- Remove trailing ", " if present
				if commendations:sub(-2) == ", " then
					commendations = commendations:sub(1, -3)
				end

				e.Commendations = commendations
			end

			if parse.optionalString("+Height:") then
				e.Height = parse.getFloat()
			end

			if parse.optionalString("+Weight:") then
				e.Weight = parse.getFloat()
			end

			if parse.optionalString("+Gender:") then
				e.Gender = parse.getString()
			end

			if parse.optionalString("+Species:") then
				e.Species = parse.getString()
			end

			if parse.optionalString("+Affiliation:") then
				e.Affiliation = parse.getString() --Deliberately just save the acronym for now. We can resolve it when we load the UI because Factions may not be loaded yet
			end

			table.insert(entry.Progression, e)
		end

		table.insert(Personnel.entries, entry)
	end

	parse.requiredString("#End")

	parse.stop()

end

--- Toggle the visibility of a personnel entry and save it to the player file
--- @param name string The name of the personnel entry
--- @param visible boolean Whether the entry should be visible or not
--- @return nil
function Personnel:toggleEntryVisibility(name, visible)
	if name == "<none>" then
		return
	end

	if Personnel:checkPersonExists(name) then
		local datasaver = require("lib_data_saver")
		local saveData = datasaver:loadDataFromFile("personnelLocks", true) or {}

		if visible then
			saveData[name] = true
		else
			saveData[name] = nil
		end

		datasaver:saveDataToFile("personnelLocks", saveData, true)
	end
end

--- Check if a personnel entry exists
--- @param name string The name of the personnel entry
--- @return boolean result Whether the entry exists or not
function Personnel:checkPersonExists(name)

	for _, v in pairs(Personnel.entries) do
		if v.Name == name then
			return true
		end
	end

	return false

end

--- Get a personnel entry by name
--- @param name string The name of the personnel entry
--- @return personnel_entry? entry The personnel entry
function Personnel:getPersonByName(name)

	for _, v in pairs(Personnel.entries) do
		if v.Name == name then
			return v
		end
	end

	ba.warning("Could not find person with name '" .. name .. "'!")

	return nil

end

--- Create the Lua enums for personnel entries
--- @return nil
function Personnel:createLuaEnum()

	local enum = mn.LuaEnums["Personnel"]

	local added = false
	for _, v in pairs(Personnel.entries) do
		enum:addEnumItem(v.Name)
		added = true
	end

	if added then
		enum:removeEnumItem("<none>")
	end

end

--- Get the progression override for a personnel entry
--- @param key number The key of the progression entry
--- @param entry personnel_entry The personnel entry
--- @return personnel_entry_override? override The progression override, if any
function Personnel:getProgressionOverride(key, entry)
	for i, v in pairs(entry.Progression) do
		if key >= v.Key then
			return v
		end
	end

	return nil
end

--- Sort the personnel entries at a specific progression
--- @param progressionValue number? The progression to use
--- @return nil
function Personnel:sortEntries(progressionValue)

    local function splitName(name)
        local first, last = name:match("(%S+)%s+(%S+)")
        return first, last
    end

    local function getOverrideAffiliation(entry, progressionValue)
		if not progressionValue then
			return entry.Affiliation
		end
        local override = Personnel:getProgressionOverride(progressionValue, entry)
        if override ~= nil and progressionValue >= override.Key and override.Affiliation then
            return override.Affiliation
        end
        return entry.Affiliation
    end

    table.sort(Personnel.entries, function(a, b)
        -- Get affiliation considering progression overrides
        local aAffiliation = getOverrideAffiliation(a, progressionValue)
        local bAffiliation = getOverrideAffiliation(b, progressionValue)

        -- First compare affiliations
        if aAffiliation ~= bAffiliation then
            return aAffiliation < bAffiliation
        end

        -- If affiliations are the same, compare last names
        local aFirst, aLast = splitName(a.Name)
        local bFirst, bLast = splitName(b.Name)

        if aLast ~= bLast then
            return aLast < bLast
        end

        -- If last names are the same, compare first names
        return aFirst < bFirst
    end)
end

--- Parse the personnel tables and modular tables then create the enums
--- @return nil
function Personnel:parseTables()
	ScpuiSystem:parseTable(Personnel, Personnel.parsePersonnel, "personnel", "psnl")

	Personnel:sortEntries()

	if #Personnel.entries > 0 then
		Personnel:createLuaEnum()
	end
end

mn.LuaSEXPs['toggle-personnel-bio'].Action = function(person, toggle)
	if person ~= "<none>" then
		Personnel:toggleEntryVisibility(person, toggle)
	end
end

Personnel:parseTables()