local ADDON_NAME, ns = ...

-- Integrates the Field Manual directly into Blizzard's Adventure Guide
-- (Encounter Journal) as an additional bottom tab, instead of a standalone
-- window. When the tab is active, Blizzard's own content panels are hidden and
-- our manual panel is shown inside the Adventure Guide's inset.

local AG = {}
ns.AdventureGuide = AG

local TAB_TEXT = "Field Manual"
local TAB_ID
local setupDone = false

local function EJ()
    return _G.EncounterJournal
end

function AG:IsActive()
    return self.active == true
end

-- Blizzard's own bottom tabs, in layout order.
local function BlizzardTabs(ej)
    return {
        ej.JourneysTab, ej.MonthlyActivitiesTab, ej.suggestTab,
        ej.dungeonsTab, ej.raidsTab, ej.LootJournalTab, ej.TutorialsTab,
    }
end

-- Reposition our tab to the right of the last visible Blizzard tab. Blizzard
-- re-lays-out its own tabs on every OnShow based on visibility, so we run this
-- afterwards to stay anchored to the correct neighbour.
function AG:UpdateTab()
    local ej = EJ()
    if not ej or not self.tab then return end

    local last
    for _, t in ipairs(BlizzardTabs(ej)) do
        if t and t:IsShown() then last = t end
    end

    if PanelTemplates_TabResize then
        PanelTemplates_TabResize(self.tab, 0)
    end

    self.tab:ClearAllPoints()
    if last then
        self.tab:SetPoint("LEFT", last, "RIGHT", 3, 0)
    else
        self.tab:SetPoint("TOPLEFT", ej, "BOTTOMLEFT", 11, 2)
    end
    self.tab:Show()
end

function AG:CreateTab(ej)
    -- PanelTabButtonTemplate has parentArray="Tabs", so creating this button
    -- automatically appends it to EncounterJournal.Tabs. Its index becomes our
    -- tab id so Blizzard's PanelTemplates_* helpers can address it.
    local tab = CreateFrame("Button", "RatedTrackerFieldManualEJTab", ej, "PanelTabButtonTemplate")
    TAB_ID = #ej.Tabs
    tab:SetID(TAB_ID)
    tab:SetText(TAB_TEXT)
    if PanelTemplates_TabResize then
        PanelTemplates_TabResize(tab, 0)
    end
    tab:SetScript("OnClick", function()
        if SOUNDKIT and PlaySound then
            PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
        end
        AG:Activate()
    end)
    -- Extend Blizzard's tab count so UpdateTabs iterates over ours too.
    ej.numTabs = TAB_ID
    self.tab = tab
    self:UpdateTab()
end

-- Every Blizzard widget we hid, so leaving our tab puts the journal back exactly
-- as we found it. EJ_ContentTab_Select does not re-show navBar, so relying on
-- Blizzard to undo our hides costs the player the Home button on its own tabs.
local hiddenByUs = {}
local savedTitle
local normalizing = false

local function HideOne(frame)
    if frame and frame.IsShown and frame:IsShown() then
        frame:Hide()
        hiddenByUs[#hiddenByUs + 1] = frame
    end
end

local function CurrentTitle(ej)
    local container = ej.TitleContainer
    local fs = container and (container.TitleText or container.Text)
    if fs and fs.GetText then return fs:GetText() end
    return nil
end

local function HideBlizzardContent(ej)
    if #hiddenByUs > 0 then return end
    savedTitle = CurrentTitle(ej)

    HideOne(ej.instanceSelect)
    HideOne(ej.encounter)
    HideOne(ej.suggestFrame)
    HideOne(ej.LootJournal)
    HideOne(ej.LootJournalItems)
    HideOne(ej.MonthlyActivitiesFrame)
    HideOne(ej.JourneysFrame)
    HideOne(ej.TutorialsFrame)

    HideOne(ej.searchBox)
    HideOne(ej.navBar)
    HideOne(ej.LootJournalViewDropdown)
    if ej.instanceSelect then
        HideOne(ej.instanceSelect.ExpansionDropdown)
        HideOne(ej.instanceSelect.GreatVaultButton)
    end
end

local function RestoreBlizzardContent(ej)
    if #hiddenByUs == 0 then return end
    for i = #hiddenByUs, 1, -1 do
        hiddenByUs[i]:Show()
        hiddenByUs[i] = nil
    end
    if savedTitle and ej.SetTitle then
        ej:SetTitle(savedTitle)
    end
    savedTitle = nil

    -- Re-showing everything is deliberately too generous; hand the final say back
    -- to Blizzard so the tab the player picked lays itself out normally.
    local target = ej.selectedTab
    if target and target ~= TAB_ID and type(_G.EJ_ContentTab_Select) == "function" then
        normalizing = true
        _G.EJ_ContentTab_Select(target)
        normalizing = false
    end
end

-- Our own Blizzard NavBar (the red "Home" breadcrumb bar), placed exactly where
-- the Adventure Guide's own nav bar sits. We drive it instead of Blizzard's so
-- clicks route to our views without touching Blizzard's nav state.
function AG:CreateNav(ej)
    if not (CreateFrame and NavBar_Initialize) then return end
    local nav = CreateFrame("Frame", "RatedTrackerFieldManualNav", ej, "NavBarTemplate")
    -- Match Blizzard's own Adventure Guide nav bar exactly so the breadcrumb sits
    -- flush in the header band with no gaps above/below it.
    local matched = false
    if ej.navBar and ej.navBar.GetNumPoints and ej.navBar:GetNumPoints() > 0 then
        local w, h = ej.navBar:GetSize()
        if w and w > 0 and h and h > 0 then
            nav:SetSize(w, h)
            for i = 1, ej.navBar:GetNumPoints() do
                local p, rel, rp, x, y = ej.navBar:GetPoint(i)
                nav:SetPoint(p, rel == ej.navBar and ej or rel, rp, x, y)
            end
            matched = true
        end
    end
    if not matched then
        nav:SetSize(480, 34)
        nav:SetPoint("TOPLEFT", ej, "TOPLEFT", 61, -22)
    end
    nav:SetFrameLevel((ej:GetFrameLevel() or 1) + 25)

    local homeData = {
        name = HOME or "Home",
        OnClick = function()
            -- Our nav sits on top of Blizzard's in the same header slot; never
            -- act on a click that belongs to one of their tabs.
            if not AG:IsActive() then return end
            if ns.MainFrame then ns.MainFrame:GoHome() end
        end,
    }
    NavBar_Initialize(nav, "NavButtonTemplate", homeData, nav.home, nav.overflow)
    nav:Hide()
    self.nav = nav
end

-- Home screen: show the "Home" breadcrumb node (top row), exactly like Blizzard's
-- category pages. The section title ("Field Manual") is drawn separately in the
-- band beneath it by HomeView.
function AG:ShowHomeNav()
    local nav = self.nav
    if not (nav and NavBar_Reset and self:IsActive()) then return false end
    nav:Show()
    NavBar_Reset(nav)   -- keeps only the Home node
    return true
end

-- Home header title, drawn in the exact row Blizzard uses for its tab header
-- text (the nav bar band), so "Field Manual" lines up with Dungeons / Raids /
-- etc. Shown only on Home, where the breadcrumb nav is hidden.
function AG:CreateHomeTitle(ej)
    local host = CreateFrame("Frame", "RatedTrackerFieldManualHomeTitle", ej)
    host:SetFrameLevel((ej:GetFrameLevel() or 1) + 30)
    host:SetSize(400, 28)
    -- Exact same font object Blizzard uses for the "Raids"/"Dungeons" instance
    -- select title (GameFontNormalLarge2), so ours is pixel-identical.
    local t = host:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge2")
    t:SetJustifyH("LEFT")
    t:SetPoint("TOPLEFT", 0, 0)
    t:SetText("Field Manual")
    host:Hide()
    self.homeTitleHost = host
    self.homeTitle = t
end

-- Position the "Field Manual" title in the EXACT spot Blizzard's instance-select
-- title occupies. Blizzard anchors instanceSelect.Title TOPLEFT (20, -15) inside
-- instanceSelect, and instanceSelect sits at (0, -2) in the inset -> (20, -17)
-- from the inset top-left, so the title holds one spot across tabs.
function AG:ShowHomeTitle()
    local host = self.homeTitleHost
    local ej = EJ()
    if not (host and ej and ej.inset and self:IsActive()) then return end
    host:ClearAllPoints()
    host:SetPoint("TOPLEFT", ej.inset, "TOPLEFT", 20, -17)
    host:Show()
end

function AG:HideHomeTitle()
    if self.homeTitleHost then self.homeTitleHost:Hide() end
end

-- Populate the nav bar from a breadcrumb trail. segments[1] is always Home
-- (the fixed home button); later segments become clickable nodes, and a segment
-- may carry a `list` function to show a dropdown of sibling entries.
function AG:SetNavTrail(segments)
    local nav = self.nav
    if not (nav and NavBar_Reset and NavBar_AddButton and self:IsActive()) then return false end
    nav:Show()
    NavBar_Reset(nav)
    for i = 2, #segments do
        local seg = segments[i]
        NavBar_AddButton(nav, {
            id = i,
            name = seg.text,
            OnClick = function()
                if seg.onClick then seg.onClick() end
            end,
            listFunc = seg.list,
        })
    end
    return true
end

-- First-time callout: when the player opens the Adventure Guide for the first
-- time after installing, point a big Blizzard HelpTip at our Field Manual tab so
-- they know it exists. Shown once, then remembered account-wide.
function AG:MaybeShowIntro()
    if not (self.tab and ns.db and ns.db.global) then return end
    if ns.db.global.introShown then return end
    if not (HelpTip and HelpTip.Show) then return end

    local function show()
        if not (self.tab and self.tab:IsShown()) then return end
        if ns.db.global.introShown then return end
        local info = {
            text = "New: RatedTracker Field Manual\n\nFast in-game Rated PvP & Battleground Blitz strategy \226\128\148 maps, macros, and terminology. Click here to open it.",
            buttonStyle = HelpTip.ButtonStyle.Close,
            targetPoint = HelpTip.Point.BottomEdgeCenter,
            alignment = HelpTip.Alignment.Center,
            offsetY = -6,
            useParentStrata = true,
        }
        HelpTip:Show(EJ() or self.tab, info, self.tab)
        ns.db.global.introShown = true
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(0.6, show)
    else
        show()
    end
end

-- Forward left-button drags on a mouse-enabled frame to the Encounter Journal
-- window, so grabbing empty space in our panel moves the whole Adventure Guide
-- just like Blizzard's own tabs.
function AG:MakeDraggable(frame)
    if not (frame and frame.RegisterForDrag) then return end
    frame:RegisterForDrag("LeftButton")
    frame:HookScript("OnDragStart", function()
        local f = EJ()
        if f then
            if f.SetMovable then f:SetMovable(true) end
            if f.StartMoving then f:StartMoving() end
        end
    end)
    frame:HookScript("OnDragStop", function()
        local f = EJ()
        if f and f.StopMovingOrSizing then f:StopMovingOrSizing() end
    end)
end

function AG:ShowHeaderSeam(anchorFrame, gap)
    local seam = self.headerSeam
    if not (seam and self.host and self:IsActive()) then return end
    -- Sit the divider in the gap between the title band and the content, spanning
    -- the full panel width -- the same place as Blizzard's "Raids" header/content
    -- boundary. The host is inset+(2,-2); Blizzard's tiles start ~-52 from the
    -- inset, so -44 from the host top lands just above them.
    seam:ClearAllPoints()
    seam:SetPoint("TOPLEFT", self.host, "TOPLEFT", 0, -44)
    seam:SetPoint("TOPRIGHT", self.host, "TOPRIGHT", 0, -44)
    seam:Show()
end

function AG:HideHeaderSeam()
    if self.headerSeam then self.headerSeam:Hide() end
    if self.headerBand then self.headerBand:Hide() end
end

function AG:Activate()
    local ej = EJ()
    if not ej or not TAB_ID then return end

    self.active = true
    -- Separate from `active`: survives Blizzard re-selecting a tab during its
    -- own OnShow, so reopening the journal returns to our tab.
    self.wanted = true

    -- Select our tab; this deselects every Blizzard tab through PanelTemplates.
    if PanelTemplates_SetTab then
        PanelTemplates_SetTab(ej, TAB_ID)
    end

    HideBlizzardContent(ej)

    if ej.SetTitle then
        ej:SetTitle(TAB_TEXT)
    end

    if self.host then self.host:Show() end
    if self.search then self.search:Show() end
    -- Nav (breadcrumb) visibility is driven by the current view: hidden at Home,
    -- shown once the player drills into a section/guide.

    self:UpdateTab()

    if ns.MainFrame and ns.MainFrame:IsBuilt() then
        ns.MainFrame:Enter()
    end
end

-- Tear down every widget we own. Always runs (never early-returns on `active`)
-- so a stale nav bar or title can't survive on top of a Blizzard tab.
-- userSwitched: the player clicked one of Blizzard's tabs, so stop restoring
-- ours when the journal reopens.
function AG:Deactivate(userSwitched)
    self.active = false
    if userSwitched then self.wanted = false end
    if self.host then self.host:Hide() end
    if self.nav then self.nav:Hide() end
    self:HideHomeTitle()
    self:HideHeaderSeam()
    if self.search then
        self.search:SetText("")
        self.search:ClearFocus()
        self.search:Hide()
    end

    local ej = EJ()
    if ej then RestoreBlizzardContent(ej) end
end

function AG:Setup()
    if setupDone then return end
    local ej = EJ()
    if not ej or not ej.inset then return end
    if not (ns.MainFrame and ns.MainFrame.BuildInto) then return end
    setupDone = true

    -- Content host covering the Adventure Guide inset.
    local host = CreateFrame("Frame", "RatedTrackerFieldManualEJPanel", ej)
    host:SetPoint("TOPLEFT", ej.inset, "TOPLEFT", 2, -2)
    host:SetPoint("BOTTOMRIGHT", ej.inset, "BOTTOMRIGHT", -2, 2)
    host:SetFrameLevel((ej:GetFrameLevel() or 1) + 20)
    host:EnableMouse(true)
    host:Hide()
    self.host = host

    -- Dark-gray fallback (only shows if the texture ever fails to load).
    local bg = host:CreateTexture(nil, "BACKGROUND", nil, 0)
    bg:SetAllPoints(host)
    bg:SetColorTexture(0.14, 0.14, 0.15, 1)
    self.hostBG = bg

    -- Custom dark gray parchment background, stretched to fill the panel.
    local tex = host:CreateTexture(nil, "BACKGROUND", nil, 1)
    tex:SetAllPoints(host)
    tex:SetTexture("Interface\\AddOns\\RatedTrackerFieldManual\\Media\\backgrounds\\parchment-dark")
    self.hostTex = tex

    -- Header band kept as an (unused) very subtle tone only; consistency comes
    -- from the uniform texture above, so it stays hidden.
    local band = host:CreateTexture(nil, "BACKGROUND", nil, 2)
    band:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
    band:SetPoint("TOPRIGHT", host, "TOPRIGHT", 0, 0)
    band:SetHeight(64)
    band:SetColorTexture(0.30, 0.34, 0.42, 0.12)
    band:Hide()
    self.headerBand = band

    -- Let the player drag the whole Adventure Guide by grabbing empty space in
    -- our panel, exactly like Blizzard's own tabs. Our frames cover the inset and
    -- eat mouse input, so we forward drags to the Encounter Journal frame.
    self:MakeDraggable(host)

    -- Header search box, matching the Adventure Guide's own placement.
    local search = CreateFrame("EditBox", "RatedTrackerFieldManualEJSearch", ej, "SearchBoxTemplate")
    search:SetSize(210, 20)
    search:SetPoint("TOPRIGHT", ej, "TOPRIGHT", -10, -32)
    search:SetAutoFocus(false)
    search:SetMaxLetters(80)
    search:SetFrameLevel((ej:GetFrameLevel() or 1) + 25)
    if search.Instructions then
        search.Instructions:SetText(SEARCH or "Search")
    end
    search:Hide()
    self.search = search

    ns.MainFrame:BuildInto(host, search)

    -- Header/content seam, in the same place as the Adventure Guide
    -- "Suggested Content" / "Raids" divider: a crisp full-width engraved line
    -- between the title band and the content. Home only.
    local seam = CreateFrame("Frame", "RatedTrackerFieldManualHeaderSeam", host)
    seam:SetHeight(3)
    seam:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
    seam:SetPoint("TOPRIGHT", host, "TOPRIGHT", 0, 0)
    seam:SetFrameStrata("HIGH")
    seam:SetFrameLevel((host:GetFrameLevel() or 1) + 50)
    seam:EnableMouse(false)
    seam:Hide()

    -- Incised look: a dark groove with a subtle highlight beneath, edge to edge.
    local shadow = seam:CreateTexture(nil, "OVERLAY", nil, 0)
    shadow:SetHeight(1)
    shadow:SetPoint("TOPLEFT", 0, 0)
    shadow:SetPoint("TOPRIGHT", 0, 0)
    shadow:SetColorTexture(0, 0, 0, 0.85)

    local hi = seam:CreateTexture(nil, "OVERLAY", nil, 1)
    hi:SetHeight(1)
    hi:SetPoint("TOPLEFT", shadow, "BOTTOMLEFT", 0, 0)
    hi:SetPoint("TOPRIGHT", shadow, "BOTTOMRIGHT", 0, 0)
    hi:SetColorTexture(0.42, 0.45, 0.52, 0.8)

    self.headerSeam = seam

    self:CreateNav(ej)
    self:CreateHomeTitle(ej)
    self:CreateTab(ej)

    -- When any Blizzard tab is selected, drop out of our view. Cover every
    -- route: their tab buttons, the content-tab helper, and the generic panel
    -- tab setter, so our nav/title can never linger over their pages.
    for _, t in ipairs(BlizzardTabs(ej)) do
        if t and t.HookScript then
            t:HookScript("OnClick", function()
                if normalizing then return end
                AG:Deactivate(true)
            end)
        end
    end
    if hooksecurefunc then
        if type(_G.EJ_ContentTab_Select) == "function" then
            hooksecurefunc("EJ_ContentTab_Select", function(id)
                if normalizing then return end
                if id ~= TAB_ID then AG:Deactivate() end
            end)
        end
        if type(_G.PanelTemplates_SetTab) == "function" then
            hooksecurefunc("PanelTemplates_SetTab", function(frame, id)
                if normalizing then return end
                if frame == ej and id ~= TAB_ID then AG:Deactivate() end
            end)
        end
    end

    ej:HookScript("OnShow", function()
        AG:UpdateTab()
        if AG.wanted then
            AG:Activate()
        end
        AG:MaybeShowIntro()
    end)
    ej:HookScript("OnHide", function()
        if AG.search then AG.search:ClearFocus() end
    end)

    -- Catch the very first open (the show that triggered on-demand loading).
    if ej:IsShown() then
        AG:MaybeShowIntro()
    end
end

-- Set up as soon as the Adventure Guide addon is available.
local loader = CreateFrame("Frame")
loader:RegisterEvent("ADDON_LOADED")
loader:SetScript("OnEvent", function(_, _, name)
    if name == "Blizzard_EncounterJournal" then
        AG:Setup()
    end
end)

local function AlreadyLoaded()
    if C_AddOns and C_AddOns.IsAddOnLoaded then
        return C_AddOns.IsAddOnLoaded("Blizzard_EncounterJournal")
    end
    if IsAddOnLoaded then
        return IsAddOnLoaded("Blizzard_EncounterJournal")
    end
    return false
end

if AlreadyLoaded() then
    AG:Setup()
end
