-- ====================================================================================
-- TUM.PLAYERCAREER - HANDLES THE PERSISTENT PILOT CAREER IN SINGLE-PLAYER MISSIONS
-- ====================================================================================
-- (local const) MAX_RIBBONS
-- (local const) MEDAL_BOX_DISPLAY_TIME
-- (local const) OBJECTIVES_PER_RIBBON
-- (local const) MEDALS
-- (local const) RANKS
-- (local table) careerStats
-- (local) fixIncompleteStats()
-- (local) getHighestMedal()
-- (local) getRibbonCount()
-- TUM.playerCareer.awardScore(score, objectives)
-- TUM.playerCareer.createMenu()
-- TUM.playerCareer.displayMedalBox(printSummary)
-- TUM.playerCareer.getCareerSummary()
-- TUM.playerCareer.load()
-- TUM.playerCareer.onStartUp()
-- TUM.playerCareer.reset()
-- TUM.playerCareer.save()
-- ====================================================================================

TUM.playerCareer = {}

do
    local MAX_RIBBONS = 40 -- Maximum number of ribbons
    local MEDAL_BOX_DISPLAY_TIME = 15 -- in seconds
    local OBJECTIVES_PER_RIBBON = 4 -- How many completed objectives to gain a new ribbon?

    local MEDALS = {
        { "Air medal", 150 },
        { "Bronze star", 225 },
        { "Airman's medal", 300 },
        { "Distinguished Flying Cross", 375 },
        { "Silver Star for Valor", 450 },
        { "Air Force Cross", 525 },
        { "Congressional Medal of Honor", 600 },
    }

    local RANKS = {
        { "2d Lt.", "Second lieutenant", 0 },
        { "1st Lt.", "First lieutenant", 400 },
        { "Capt.", "Captain", 1600 },
        { "Maj.", "Major", 6400 },
        { "Lt Col.", "Lieutenant colonel", 16000 },
        { "Col.", "Colonel", 32000 },
    }

    local careerStats = {}

    -------------------------------------
    -- Adds missing fields, if any, to the careerStats table
    -------------------------------------
    local function fixIncompleteStats()
        if not careerStats then careerStats = { } end
        if not careerStats.bestSortie then careerStats.bestSortie = 0 end
        if not careerStats.completedObjectives then careerStats.completedObjectives = 0 end
        if not careerStats.completedSorties then careerStats.completedSorties = 0 end
        if not careerStats.medals then careerStats.medals = 0 end
        if not careerStats.medalWounded then careerStats.medalWounded = false end
        if not careerStats.rank then careerStats.rank = 1 end
        if not careerStats.score then careerStats.score = 0 end
        careerStats.version = TUM.VERSION_NUMBER
    end

    -------------------------------------
    -- Returns the highest medal a player has obtained for scoring a high number of points during a single sortie
    -- @return A number (index in the MEDALS table)
    -------------------------------------
    local function getHighestMedal()
        local medal = 0
        for i=1,#MEDALS do
            if careerStats.bestSortie >= MEDALS[i][2] then
                medal = i
            end
        end

        return medal
    end

    -------------------------------------
    -- Returns the current number a ribbons a player was awarded for completing objectives
    -- @return A number
    -------------------------------------
    local function getRibbonCount()
        return DCSEx.math.clamp(math.floor(careerStats.completedObjectives / OBJECTIVES_PER_RIBBON), 0, MAX_RIBBONS)
    end

    -------------------------------------
    -- Awards career points. Only works in single-player missions
    -- @param score Number of career points to award
    -- @param objectives Number of completed objectives to award
    -- @return True if a ribbon, medal or promotion was awarded, false otherwise
    -------------------------------------
    function TUM.playerCareer.awardScore(score, objectives)
        if not DCSEx.io.canReadAndWrite() then return false end -- IO disabled, career and scoring disabled
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No career in multiplayer
        score = math.max(0, math.floor(score or 0))

        fixIncompleteStats()

        local oldRibbonCount = getRibbonCount()
        careerStats.bestSortie = math.max(careerStats.bestSortie, score)
        careerStats.score = careerStats.score + score
        careerStats.completedObjectives = careerStats.completedObjectives + objectives
        trigger.action.outText(DCSEx.string.toStringThousandsSeparator(score).." xp and "..tostring(objectives).." completed objective(s) were registered in your flight log.", 5)
        local newRibbonCount = getRibbonCount()

        local somethingWasAwarded = false

        -- Check for promotions
        if careerStats.rank < #RANKS and careerStats.score >= RANKS[careerStats.rank + 1][3] then
            careerStats.rank = careerStats.rank + 1
            somethingWasAwarded = true
            trigger.action.outText("✪ You have been promoted to the rank of "..RANKS[careerStats.rank][2]..".", MEDAL_BOX_DISPLAY_TIME)
        end

        -- Check for medals
        for i=1,#MEDALS do
            if i > careerStats.medals and score >= MEDALS[i][2] then
                trigger.action.outText("✪ You have been awarded the "..MEDALS[i][1]..".", MEDAL_BOX_DISPLAY_TIME)
                careerStats.medals = i
                somethingWasAwarded = true
                break
            end
        end

        -- Check for ribbons
        if newRibbonCount > oldRibbonCount then
            trigger.action.outText("✪ You have been awarded a battle ribbon.", MEDAL_BOX_DISPLAY_TIME)
            somethingWasAwarded = true
        end

        TUM.playerCareer.save()

        if somethingWasAwarded then
            TUM.playerCareer.displayMedalBox(false)
        end

        TUM.playerScore.reset(false)

        return somethingWasAwarded
    end

    -------------------------------------
    -- Appends the career menu to the F10 menu. Only works in single-player missions
    -------------------------------------
    function TUM.playerCareer.createMenu()
        local rootMenu = TUM.getOrCreateRootMenu()
        if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer

        missionCommands.addCommand("✪ View pilot career stats", rootMenu, TUM.playerCareer.displayMedalBox, true)
    end

    -------------------------------------
    -- Displays the player's medal box and carrer summary. Only works in single-player missions
    -------------------------------------
    function TUM.playerCareer.displayMedalBox(printSummary)
        if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer
        fixIncompleteStats()

        if printSummary then
            trigger.action.outText(TUM.playerCareer.getCareerSummary(), MEDAL_BOX_DISPLAY_TIME, true)
        end

        DCSEx.dcs.outPicture("Pic-MedalBox.png", MEDAL_BOX_DISPLAY_TIME, true, 0, 2, 2, 50, 1)
        DCSEx.dcs.outPicture("Pic-Rank"..tostring(careerStats.rank)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)

        local ribbonCount = getRibbonCount()
        for i=1,ribbonCount do
            DCSEx.dcs.outPicture("Pic-Ribbon"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)
        end

        for i=1,careerStats.medals do
            DCSEx.dcs.outPicture("Pic-Medal"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1)
        end
        trigger.action.outSound("UI-Career.ogg")
    end

    -------------------------------------
    -- Returns the player career summary as a string. Only works in single-player missions
    -- @return A string
    -------------------------------------
    function TUM.playerCareer.getCareerSummary()
        if not DCSEx.io.canReadAndWrite() then return "" end -- IO disabled, career and scoring disabled
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return "" end -- No career in multiplayer
        fixIncompleteStats()

        local playerName = "Player"
        local players = DCSEx.world.getAllPlayers()
        if #players > 0 then
            playerName = players[1]:getPlayerName()
        end

        local summary = ""
        summary = summary.."CAREER STATS FOR "..RANKS[careerStats.rank][1]:upper().." "..playerName:upper()..":\n"
        summary = summary.."=======================\n"
        summary = summary.."- Rank: "..RANKS[careerStats.rank][2].."\n"
        summary = summary.."- Best sortie XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.bestSortie).."\n"
        summary = summary.."- Total career XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.score).."\n"
        summary = summary.."- Completed objectives: "..tostring(careerStats.completedObjectives).."\n"

        if careerStats.medals == 0 then
            summary = summary.."- Medals: None"
        else
            summary = summary.."- Medals:"
            for i=1,careerStats.medals do
                summary = summary.."\n  - "..MEDALS[i][1]
            end
        end

        local ribbonCount = getRibbonCount()
        if ribbonCount < MAX_RIBBONS or careerStats.rank < #RANKS or careerStats.medals < #MEDALS then
            summary = summary.."\n"

            if careerStats.rank < #RANKS then
                summary = summary.."\n- Next promotion: "..DCSEx.string.toStringThousandsSeparator(RANKS[careerStats.rank + 1][3]).." xp"
            end

            if ribbonCount < MAX_RIBBONS then
                summary = summary.."\n- Next ribbon: "..tostring((ribbonCount + 1) * OBJECTIVES_PER_RIBBON).." objectives"
            end

            if careerStats.medals < #MEDALS then
                summary = summary.."\n- Next medal: "..DCSEx.string.toStringThousandsSeparator(MEDALS[careerStats.medals + 1][2]).." xp in a single flight"
            end
        end

        return summary
    end

    -------------------------------------
    -- Loads the player career from the disk. Only works in single-player missions
    -- @return True if everything worked (or disabled because of MP), false if an error happened
    -------------------------------------
    function TUM.playerCareer.load()
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer
        if not DCSEx.io.canReadAndWrite() then return false end

        local jsonString = DCSEx.io.load("TheUniversalMission.sav")
        if jsonString then
            -- TODO: what if Json is malformed?
            careerStats = net.json2lua(jsonString)
            if not careerStats then
                careerStats = {}
                fixIncompleteStats()
                TUM.log("Failed to load player career data, career data reset.")
            else
                TUM.log("Player career data loaded successfully.")
            end
        else
            fixIncompleteStats()
            return false
        end

        fixIncompleteStats()
    end

    -------------------------------------
    -- Called on mission start up
    -- @return True if started up properly, false if an error happened
    -------------------------------------
    function TUM.playerCareer.onStartUp()
        fixIncompleteStats()
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer

        if DCSEx.io.canReadAndWrite() then
            TUM.log("Lua IO module available, can read and write.")
            TUM.playerCareer.load()
        else
            local msg = "IO module is disabled, CANNOT read and write persistant data. Player progress will NOT be saved.\n"
            msg = msg.."To enable the IO module, comment or remove the \"sanitizeModule('io')\" line in \n"
            msg = msg.."[DCSWorld installation directory]\\Scripts\\MissionScripting.lua and restart the game."

            TUM.log(msg, TUM.logger.logLevel.WARNING)
        end

        return true
    end

    -------------------------------------
    -- Resets the player career stats and save them. Only works in single-player missions
    -------------------------------------
    function TUM.playerCareer.reset()
        if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer

        careerStats = nil
        fixIncompleteStats()
        TUM.playerCareer.save()
    end

    -------------------------------------
    -- Save the player career to the disk. Only works in single-player missions
    -- @return True if everything worked (or disabled), false if an error happened
    -------------------------------------
    function TUM.playerCareer.save()
        if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer
        if not DCSEx.io.canReadAndWrite() then return true end -- IO disabled, career and scoring disabled

        fixIncompleteStats()

        if DCSEx.io.save("TheUniversalMission.sav", net.lua2json(careerStats)) then
            return true
        else
            return false
        end
    end
end
