ZO_HELP_NAVIGATION_CONTAINER_WIDTH = 365
-- 55 is the inset from the left side of the header to the left side of the text in ZO_IconHeader, 16 is the offset for the Scroll from ZO_ScrollContainerBase
ZO_HELP_NAVIGATION_CATEGORY_LABEL_WIDTH = ZO_HELP_NAVIGATION_CONTAINER_WIDTH - 55 - 16

ZO_Help_Keyboard = ZO_Object:Subclass()

function ZO_Help_Keyboard:New(...)
    local help = ZO_Object.New(self)
    help:Initialize(...)
    return help
end

function ZO_Help_Keyboard:Initialize(control)
    self.control = control
    control.owner = self

    self.helpControls = {}

    self.noMatchMessage = control:GetNamedChild("NoMatchMessage")
    self.searchBox = control:GetNamedChild("SearchBox")

    self.detailsScrollChild = control:GetNamedChild("DetailsContainerScrollChild")

    self.helpTitle = control:GetNamedChild("DetailsTitle")
    self.helpBody = control:GetNamedChild("DetailsBody1")
    self.helpImage = control:GetNamedChild("DetailsImage")
    self.helpBody2 = control:GetNamedChild("DetailsBody2")

    self.helpTitle:SetParent(self.detailsScrollChild)
    self.helpTitle:SetAnchor(TOPLEFT, nil, TOPLEFT, 10, 0)
    self.helpBody:SetParent(self.detailsScrollChild)
    self.helpImage:SetParent(self.detailsScrollChild)
    self.helpBody2:SetParent(self.detailsScrollChild)

    self.noMatchMessage:SetParent(self.detailsScrollChild)
    self.noMatchMessage:SetAnchor(TOPLEFT, nil, TOPLEFT, 10, 0)

    self:InitializeTree()

    self.systemFilters = nil

    local function UpdateHelp()
        if control:IsHidden() then 
            self.dirty = true
        else
            self:Refresh()
        end
    end

    control:RegisterForEvent(EVENT_HELP_INITIALIZED, UpdateHelp)
    HELP_MANAGER:RegisterCallback("UpdateSearchResults", UpdateHelp)

    local function OnLinkClicked(...)
        if not IsInGamepadPreferredMode() then
            return self.OnLinkClicked(...)
        end
    end

    LINK_HANDLER:RegisterCallback(LINK_HANDLER.LINK_CLICKED_EVENT, OnLinkClicked, self)
    LINK_HANDLER:RegisterCallback(LINK_HANDLER.LINK_MOUSE_UP_EVENT, OnLinkClicked, self)

    UpdateHelp()

    HELP_TUTORIALS_FRAGMENT = ZO_FadeSceneFragment:New(control)
    HELP_TUTORIALS_FRAGMENT:RegisterCallback("StateChange", function(oldState, newState)
        if newState == SCENE_FRAGMENT_SHOWING then
            self:OnShowing()
        elseif newState == SCENE_FRAGMENT_SHOWN then
            self:OnShown()
        elseif newState == SCENE_FRAGMENT_HIDDEN then
            self:OnHidden()
        end
    end)


    self:InitializeKeybindStripDescriptors()
end

function ZO_Help_Keyboard:InitializeKeybindStripDescriptors()
    self.keybindStripDescriptor =
    {
        alignment = KEYBIND_STRIP_ALIGN_CENTER,

        {
            name = GetString(SI_ITEM_ACTION_LINK_TO_CHAT),
            keybind = "UI_SHORTCUT_SECONDARY",

            callback = function()
                local selectedData = self.navigationTree:GetSelectedData()
                local link = ZO_LinkHandler_CreateChatLink(GetHelpLink, selectedData.helpCategoryIndex, selectedData.helpIndex)
                ZO_LinkHandler_InsertLink(link)
            end,

            visible = function()
                local selectedData = self.navigationTree:GetSelectedData()
                if selectedData and selectedData.helpCategoryIndex and selectedData.helpIndex then
                    return IsChatSystemAvailableForCurrentPlatform()
                end

                return false
            end,
        },
    }
end

function ZO_Help_Keyboard:ShowSpecificHelp(helpCategoryIndex, helpIndex)
    if SCENE_MANAGER:IsShowing("helpTutorials") then
        self:SelectHelp(helpCategoryIndex, helpIndex)
    else
        self.showHelpCategoryIndex = helpCategoryIndex
        self.showHelpIndex = helpIndex
        MAIN_MENU_KEYBOARD:ShowScene("helpTutorials")
    end    
end

function ZO_Help_Keyboard:OnShowing()
    local systemFilters = HELP_MANAGER:GetShowingOverlaySceneSystemFilters()
    if systemFilters ~= self.systemFilters then
        self.systemFilters = systemFilters
        self.dirty = true
    end

    self:ActivateInventorySearch()

    if self.dirty then
        self:Refresh()
    end

    KEYBIND_STRIP:AddKeybindButtonGroup(self.keybindStripDescriptor)
end

function ZO_Help_Keyboard:OnShown()
    if self.showHelpCategoryIndex then
        self:SelectHelp(self.showHelpCategoryIndex, self.showHelpIndex)
        self.showHelpCategoryIndex = nil
        self.showHelpIndex = nil
    end
end

function ZO_Help_Keyboard:OnHidden()
    if self.searchBox:GetText() ~= "" then
        self.searchBox:SetText("")
    end

    self:DeactivateInventorySearch()

    KEYBIND_STRIP:RemoveKeybindButtonGroup(self.keybindStripDescriptor)
end

local HELP_SEARCH_CONTEXT = "helpSearchContext"
function ZO_Help_Keyboard:ActivateInventorySearch()
    if not TEXT_SEARCH_MANAGER:IsActiveTextSearch(HELP_SEARCH_CONTEXT) then
        TEXT_SEARCH_MANAGER:ActivateTextSearch(HELP_SEARCH_CONTEXT)

        local function OnListTextFilterComplete()
            self:Refresh()
        end
        self.onListTextFilterCompleteCallback = OnListTextFilterComplete

        TEXT_SEARCH_MANAGER:RegisterCallback("UpdateSearchResults", self.onListTextFilterCompleteCallback)
    end
end

function ZO_Help_Keyboard:DeactivateInventorySearch()
    if TEXT_SEARCH_MANAGER:IsActiveTextSearch(HELP_SEARCH_CONTEXT) then
        TEXT_SEARCH_MANAGER:DeactivateTextSearch(HELP_SEARCH_CONTEXT)

        TEXT_SEARCH_MANAGER:UnregisterCallback("UpdateSearchResults", self.onListTextFilterCompleteCallback)
        self.onListTextFilterCompleteCallback = nil
    end
end

function ZO_Help_Keyboard:InitializeTree()
    self.navigationTree = ZO_Tree:New(GetControl(self.control, "NavigationContainerScrollChild"), 60, -10, 350)

    local function TreeHeaderSetup(node, control, data, open)
        control.text:SetText(data.name)

        control.icon:SetTexture(open and data.downIcon or data.upIcon)
        control.iconHighlight:SetTexture(data.overIcon)

        ZO_IconHeader_Setup(control, open)
    end
    local function TreeHeaderEquality(left, right)
        return left.helpCategoryIndex == right.helpCategoryIndex
    end
    self.navigationTree:AddTemplate("ZO_Help_Header", TreeHeaderSetup, nil, TreeHeaderEquality, nil, 0)

    local function TreeEntrySetup(node, control, data, open)
        control:SetSelected(false)
        control:SetText(data.name)
    end
    local function TreeEntryOnSelected(control, data, selected, reselectingDuringRebuild)
        control:SetSelected(selected)
        if selected and not reselectingDuringRebuild then
            self:RefreshDetails()
        end
    end
    local function TreeEntryEquality(left, right)
        return left.helpCategoryIndex == right.helpCategoryIndex and left.helpIndex == right.helpIndex
    end
    self.navigationTree:AddTemplate("ZO_Help_NavigationEntry", TreeEntrySetup, TreeEntryOnSelected, TreeEntryEquality)

    self.navigationTree:SetExclusive(true)
    self.navigationTree:SetOpenAnimation("ZO_TreeOpenAnimation")
end

function ZO_Help_Keyboard:SelectHelp(helpCategoryIndex, helpIndex)
    if self.helpControls and self.helpControls[helpCategoryIndex] then
        local helpControl = self.helpControls[helpCategoryIndex][helpIndex]
        if helpControl then
            helpControl.node:GetTree():SelectNode(helpControl.node)
        end
    end
end

function ZO_Help_Keyboard:AddHelpEntry(helpCategoryIndex, helpIndex)
    local systemFilters = self.systemFilters
    if systemFilters and #systemFilters > 0 then
        if not ZO_IsElementInNumericallyIndexedTable(systemFilters, GetUISystemAssociatedWithHelpEntry(helpCategoryIndex, helpIndex)) then
            return
        end
    end

    local parent
    if not self.categoryControls[helpCategoryIndex] then
        self.helpControls[helpCategoryIndex] = {}

        local categoryName, _, upIcon, downIcon, overIcon = GetHelpCategoryInfo(helpCategoryIndex)
        if categoryName ~= "" then
            local categoryData =    {
                                        name = categoryName,
                                        upIcon = upIcon,
                                        downIcon = downIcon,
                                        overIcon = overIcon,
                                        helpCategoryIndex = helpCategoryIndex,
                                    }

            parent = self.navigationTree:AddNode("ZO_Help_Header", categoryData)
            self.categoryControls[helpCategoryIndex] = parent
        end
    else
        parent = self.categoryControls[helpCategoryIndex]
    end

    if parent then
        local helpName, _,_,_,_,_, showOption = GetHelpInfo(helpCategoryIndex, helpIndex)

        if IsKeyboardHelpOption(showOption) then
            local helpData =    {
                                    name = helpName,
                                    helpCategoryIndex = helpCategoryIndex,
                                    helpIndex = helpIndex,
                                }

            local helpNode = self.navigationTree:AddNode("ZO_Help_NavigationEntry", helpData, parent)
            self.helpControls[helpCategoryIndex][helpIndex] = helpNode.control
            self.activeHelpCount = self.activeHelpCount + 1
        end
    end
end

function ZO_Help_Keyboard:AddTrialEntry()
    local systemFilters = HELP_MANAGER:GetShowingOverlaySceneSystemFilters()
    if systemFilters and #systemFilters > 0 then
        return
    end

    local accountTypeId, title, description = GetTrialInfo();
    if accountTypeId ~= 0 and title ~= "" and description ~= "" then
        local parent
        self.trialIndex = GetNumHelpCategories() + 1
        self.trialDescription = description
        if not self.categoryControls[self.trialIndex] then
            self.helpControls[self.trialIndex] = {}
            local categoryData =    {
                                        name = GetString(SI_TRIAL_ACCOUNT_HELP_CATEGORY),
                                        upIcon = "EsoUI/Art/Help/help_tabIcon_trial_up.dds",
                                        downIcon = "EsoUI/Art/Help/help_tabIcon_trial_down.dds",
                                        overIcon = "EsoUI/Art/Help/help_tabIcon_trial_over.dds",
                                        helpCategoryIndex = self.trialIndex,
                                    }
            parent = self.navigationTree:AddNode("ZO_Help_Header", categoryData)
            self.categoryControls[self.trialIndex] = parent
        else
            parent = self.categoryControls[self.trialIndex]
        end

        if parent then
            local helpData =    {
                                    name = title,
                                    helpCategoryIndex = self.trialIndex,
                                    helpIndex = 1,
                                }
            local helpNode = self.navigationTree:AddNode("ZO_Help_NavigationEntry", helpData, parent)
            self.helpControls[self.trialIndex][1] = helpNode.control
            self.activeHelpCount = self.activeHelpCount + 1
        end
    end
end

function ZO_Help_Keyboard:RefreshList()
    self.navigationTree:Reset()

    self.helpControls = {}
    self.categoryControls = {}
    self.activeHelpCount = 0
    self.trialIndex = nil
    self.trialDescription = nil

    local isSearchInactive = not TEXT_SEARCH_MANAGER:IsActiveTextSearch(HELP_SEARCH_CONTEXT)
    self:AddTrialEntry()
    for helpCategoryIndex = 1, GetNumHelpCategories() do
        for helpIndex = 1, GetNumHelpEntriesWithinCategory(helpCategoryIndex) do
            if isSearchInactive or TEXT_SEARCH_MANAGER:IsDataInSearchTextResults(HELP_SEARCH_CONTEXT, BACKGROUND_LIST_FILTER_TARGET_HELP_ID, GetHelpId(helpCategoryIndex, helpIndex)) then
                self:AddHelpEntry(helpCategoryIndex, helpIndex)
            end
        end
    end

    self.noMatchMessage:SetHidden(self.activeHelpCount > 0)

    self.navigationTree:Commit()

    self:RefreshDetails()
end

function ZO_Help_Keyboard:Refresh()
    self.dirty = false
    self:RefreshList()
end

function ZO_Help_Keyboard:RefreshDetails()
    local selectedData = self.navigationTree:GetSelectedData()

    if selectedData and selectedData.helpCategoryIndex and selectedData.helpIndex then
        local _, name, description, description2, image, showOption
        if selectedData.helpCategoryIndex == self.trialIndex then
            _, name, description = GetTrialInfo()
        else
            name, description, description2, image, _, _, showOption = GetHelpInfo(selectedData.helpCategoryIndex, selectedData.helpIndex)
        end

        if IsKeyboardHelpOption(showOption) then
            self.helpTitle:SetHidden(false)
            self.helpTitle:SetText(name)

            self.helpBody:SetHidden(false)
            self.helpBody:SetText(description)
            self.helpBody2:SetHidden(false)
            self.helpBody2:SetText(description2)

            if image then
                self.helpImage:SetHidden(false)
                self.helpImage:SetTexture(image)
            else
                self.helpImage:SetHidden(true)
                self.helpImage:SetHeight(0)
            end
        else
            self.helpTitle:SetHidden(true)
            self.helpBody:SetHidden(true)
            self.helpBody2:SetHidden(true)
            self.helpImage:SetHidden(true)
        end
    else
        self.helpTitle:SetHidden(true)
        self.helpBody:SetHidden(true)
        self.helpBody2:SetHidden(true)
        self.helpImage:SetHidden(true)
    end
end

function ZO_Help_Keyboard:OnLinkClicked(link, button, text, color, linkType, ...)
    if linkType == HELP_LINK_TYPE and button == MOUSE_BUTTON_INDEX_LEFT then
        local helpCategoryIndex, helpIndex = GetHelpIndicesFromHelpLink(link)
        if helpCategoryIndex and helpIndex then
            self:ShowSpecificHelp(helpCategoryIndex, helpIndex)
        end
        return true
    end
end

-- Global XML functions

function ZO_Help_OnSearchTextChanged(editBox)
    TEXT_SEARCH_MANAGER:SetSearchText(HELP_SEARCH_CONTEXT, editBox:GetText())
end

function ZO_Help_OnSearchEnterKeyPressed(editBox)
    TEXT_SEARCH_MANAGER:SetSearchText(HELP_SEARCH_CONTEXT, editBox:GetText())
    editBox:LoseFocus()
end

local HELP_MAX_IMAGE_WIDTH = 490
function ZO_Tutorials_Entries_OnTextureLoaded(control)
    -- when hidden we directly manipulate the height, so don't apply constraints in those cases
    if not control:IsControlHidden() then
        ZO_ResizeTextureWidthAndMaintainAspectRatio(control, HELP_MAX_IMAGE_WIDTH)
    end
end

function ZO_Help_Initialize(control)
    HELP = ZO_Help_Keyboard:New(control)
end
