local _, addon = ...
local L = addon.L;
local API = addon.API;
local LandingPageUtil = addon.LandingPageUtil;


local ipairs = ipairs;
local EJ_SelectInstance = EJ_SelectInstance;
local EJ_SelectEncounter = EJ_SelectEncounter;
local EJ_IsValidInstanceDifficulty = EJ_IsValidInstanceDifficulty;
local EJ_GetInstanceInfo = EJ_GetInstanceInfo;
local EJ_SetDifficulty = EJ_SetDifficulty;
local EJ_GetEncounterInfoByIndex = EJ_GetEncounterInfoByIndex;
local IsLegacyDifficulty = IsLegacyDifficulty;


local EL = CreateFrame("Frame");

function EL:OnUpdate(elapsed)
    self.t = self.t + elapsed;
    if self.t > 0 then
        self.t = nil;
        self:SetScript("OnUpdate", nil);
        if self.callback then
            self.callback();
            self.callback = nil;
        end
    end
end


local function NullifyEJEvents()
    --Pause default EncounterJournal updating
    local f = EncounterJournal;
    if f then
        f:UnregisterEvent("EJ_LOOT_DATA_RECIEVED");
        f:UnregisterEvent("EJ_DIFFICULTY_UPDATE");

        EL.callback = function()
            f:RegisterEvent("EJ_LOOT_DATA_RECIEVED");
            f:RegisterEvent("EJ_DIFFICULTY_UPDATE");
        end;

        EL.t = 0;
        EL:SetScript("OnUpdate", EL.OnUpdate);
    end
end

local function SelectInstanceAndEncounter(journalInstanceID, journalEncounterID)
    NullifyEJEvents();
    EJ_SelectInstance(journalInstanceID);
    if journalEncounterID then
        EJ_SelectEncounter(journalEncounterID);
    end
end
API.SelectInstanceAndEncounter = SelectInstanceAndEncounter;


do  --Derivative of Blizzard_EncounterJournal.lua
    local DifficultyUtil = DifficultyUtil;

    local EJ_DIFFICULTIES = {
        DifficultyUtil.ID.DungeonNormal,
        DifficultyUtil.ID.DungeonHeroic,
        DifficultyUtil.ID.DungeonMythic,
        DifficultyUtil.ID.DungeonChallenge,
        DifficultyUtil.ID.DungeonTimewalker,
        DifficultyUtil.ID.RaidLFR,
        DifficultyUtil.ID.Raid10Normal,
        DifficultyUtil.ID.Raid10Heroic,
        DifficultyUtil.ID.Raid25Normal,
        DifficultyUtil.ID.Raid25Heroic,
        DifficultyUtil.ID.PrimaryRaidLFR,
        DifficultyUtil.ID.PrimaryRaidNormal,
        DifficultyUtil.ID.PrimaryRaidHeroic,
        DifficultyUtil.ID.PrimaryRaidMythic,
        DifficultyUtil.ID.RaidTimewalker,
        DifficultyUtil.ID.Raid40,
    };

    local VALID_DIFFUICULTY_OPEN_WORLD = {
        DifficultyUtil.ID.DungeonNormal,
        DifficultyUtil.ID.DungeonHeroic,
        DifficultyUtil.ID.DungeonMythic,
        DifficultyUtil.ID.Raid10Normal,
        DifficultyUtil.ID.Raid10Heroic,
        DifficultyUtil.ID.Raid25Normal,
        DifficultyUtil.ID.Raid25Heroic,
        DifficultyUtil.ID.PrimaryRaidNormal,
        DifficultyUtil.ID.PrimaryRaidHeroic,
        DifficultyUtil.ID.PrimaryRaidMythic,
        DifficultyUtil.ID.Raid40,
    };

    local ALL_DIFFICULTY_ID = -1;   --If so, track all available difficulties


    local function GetEJDifficultySize(difficultyID)
        if difficultyID ~= DifficultyUtil.ID.RaidTimewalker and not DifficultyUtil.IsPrimaryRaid(difficultyID) then
            return DifficultyUtil.GetMaxPlayers(difficultyID);
        end
        return nil;
    end

    local function GetEJDifficultyString(difficultyID)
        if difficultyID == ALL_DIFFICULTY_ID then
            return L["All Difficulties"];
        end

        local name = DifficultyUtil.GetDifficultyName(difficultyID);
        local size = GetEJDifficultySize(difficultyID);
        if size then
            return string.format(ENCOUNTER_JOURNAL_DIFF_TEXT, size, name);
        else
            return name;
        end
    end
    API.GetRaidDifficultyString = GetEJDifficultyString;


    local function GetValidDifficulties(journalInstanceID, encounterID, showAllDifficulties)
        local n = 0;
        local tbl = {};
        SelectInstanceAndEncounter(journalInstanceID, encounterID);

        for index, difficultyID in ipairs(EJ_DIFFICULTIES) do
            if EJ_IsValidInstanceDifficulty(difficultyID) then
                local text = GetEJDifficultyString(difficultyID);
                n = n + 1;
                tbl[n] = {
                    difficultyID = difficultyID,
                    text = text,
                };
            end
        end

        if n > 0 then
            if showAllDifficulties then
                n = n + 1;
                tbl[n] = {
                    difficultyID = ALL_DIFFICULTY_ID;
                    text = GetEJDifficultyString(ALL_DIFFICULTY_ID);
                }
            end
            return tbl
        end
    end

    local function GetValidDifficultiesForEncounter(instanceID, encounterID, showAllDifficulties)
        return GetValidDifficulties(instanceID, encounterID, showAllDifficulties)
    end
    API.GetValidDifficultiesForEncounter = GetValidDifficultiesForEncounter;

    local function GetValidDifficultiesForInstance(instanceID, showAllDifficulties)
        return GetValidDifficulties(instanceID, nil, showAllDifficulties)
    end
    API.GetValidDifficultiesForInstance = GetValidDifficultiesForInstance;

    function API.GetInstanceInfoForSelector(journalInstanceID)
        --Selectable at the raid entrance
        local n = 0;
        local tbl = {};

        SelectInstanceAndEncounter(journalInstanceID, nil);

        local instanceName, description, bgImage, buttonImage1, loreImage, buttonImage2, dungeonAreaMapID, link, shouldDisplayDifficulty, mapID, covenantID, isRaid = EJ_GetInstanceInfo(journalInstanceID);
        tbl.name = instanceName;
        tbl.isRaid = isRaid;
        tbl.instanceID = mapID;

        local isLegacyRaid;
        local difficulties = {};

        for index, difficultyID in ipairs(VALID_DIFFUICULTY_OPEN_WORLD) do
            if EJ_IsValidInstanceDifficulty(difficultyID) then
                local text = GetEJDifficultyString(difficultyID);
                n = n + 1;
                difficulties[n] = {
                    difficultyID = difficultyID,
                    text = text,
                };

                if IsLegacyDifficulty(difficultyID) then
                    isLegacyRaid = true;
                end
            end
        end

        tbl.difficulties = difficulties;
        tbl.isLegacyRaid = isLegacyRaid;

        return tbl
    end

    function API.GetInstanceEncounters(journalInstanceID, difficultyID)
        EJ_SetDifficulty(difficultyID);

        local encounters = {};
        local i = 1;
        local bossName, description, journalEncounterID, _, _, _, dungeonEncounterID = EJ_GetEncounterInfoByIndex(i, journalInstanceID);    --No dungeonEncounterID in Classic

        while journalEncounterID do
            if not dungeonEncounterID then
                dungeonEncounterID = LandingPageUtil.GetDungeonEncounteID(journalEncounterID);
            end

            encounters[i] = {
                name = bossName,
                id = journalEncounterID,
                dungeonEncounterID = dungeonEncounterID,
                difficultyID = difficultyID,
            };
            i = i + 1;
            bossName, description, journalEncounterID, _, _, _, dungeonEncounterID = EJ_GetEncounterInfoByIndex(i, journalInstanceID);
        end

        return encounters
    end

    local function IsDifficultyValidForEncounter(instanceID, encounterID, difficultyID)
        local difficulties = instanceID and GetValidDifficultiesForEncounter(instanceID, encounterID);
        local valid, bestDifficultyID;
        if difficulties then
            if difficultyID == ALL_DIFFICULTY_ID then
                valid = true;
                bestDifficultyID = ALL_DIFFICULTY_ID;
            else
                if difficultyID then
                    for k, v in ipairs(difficulties) do
                        if v.difficultyID == difficultyID then
                            valid = true;
                            bestDifficultyID = difficultyID;
                            break;
                        end
                    end
                end
                if not bestDifficultyID then
                    bestDifficultyID = difficulties[#difficulties].difficultyID;
                end
            end
        end
        return valid, bestDifficultyID
    end
    API.IsDifficultyValidForEncounter = IsDifficultyValidForEncounter;

    local function IsDifficultyValidForInstance(instanceID, difficultyID)
        local difficulties = instanceID and GetValidDifficultiesForInstance(instanceID);
        local valid, bestDifficultyID;
        if difficulties then
            if difficultyID == ALL_DIFFICULTY_ID then
                valid = true;
                bestDifficultyID = ALL_DIFFICULTY_ID;
            else
                if difficultyID then
                    for k, v in ipairs(difficulties) do
                        if v.difficultyID == difficultyID then
                            valid = true;
                            bestDifficultyID = difficultyID;
                            break;
                        end
                    end
                end
                if not bestDifficultyID then
                    bestDifficultyID = difficulties[#difficulties].difficultyID;
                end
            end
        end
        return valid, bestDifficultyID
    end
    API.IsDifficultyValidForInstance = IsDifficultyValidForInstance;
end