local SousChef = SousChef
local u = SousChef.Utility
local m = SousChef.Media

function SousChef:UpdateProvisioningTable()
    self.qualityCheckBox = SousChef_ControllerOnlyQuality

    local function OnFilterChanged()
        self.settings.qualityChecked = ZO_CheckButton_IsChecked(self.qualityCheckBox)
        PROVISIONER:DirtyRecipeTree()
    end
    self.qualityCheckBox:SetParent(ZO_ProvisionerTopLevel)
    ZO_CheckButton_SetLabelText(self.qualityCheckBox, "Quality")
    ZO_CheckButton_SetToggleFunction(self.qualityCheckBox, OnFilterChanged)
    ZO_CraftingUtils_ConnectCheckBoxToCraftingProcess(self.qualityCheckBox)
    ZO_CheckButton_SetCheckState(self.qualityCheckBox, self.settings.qualityChecked)

    -- Nudge to the left
    ZO_ProvisionerTopLevelHaveSkills:ClearAnchors()
    ZO_ProvisionerTopLevelHaveSkills:SetAnchor( LEFT, ZO_ProvisionerTopLevelHaveIngredients, RIGHT, 180 )

    SousChef.filter = PROVISIONER.DoesRecipePassFilter

    PROVISIONER.DoesRecipePassFilter =
        function(control, specialIngredientType, numCreatable, provisionerLevelReq, qualityReq)
            if ZO_CheckButton_IsChecked(self.qualityCheckBox) and qualityReq < 2 then return false end
            return SousChef.filter(control, specialIngredientType, numCreatable, provisionerLevelReq, qualityReq)
        end
end


SousChef.ProvisioningButton=
{{
    name = "Mark Recipe", -- or function that returns a name
    keybind = "SC_MARK_RECIPE",
    control = self,
    callback = function(descriptor) SousChef:MarkRecipe() end,
    visible = function(descriptor) return PROVISIONER:GetSelectedRecipeIndex() ~= nil end,
    alignment = KEYBIND_STRIP_ALIGN_RIGHT,
}}

local bGroup = SousChef.ProvisioningButton

function SousChef:HookRecipeTreeFunction()
    if not KEYBIND_STRIP:HasKeybindButtonGroup(bGroup) then
        KEYBIND_STRIP:AddKeybindButtonGroup(bGroup)
    else
        KEYBIND_STRIP:UpdateKeybindButtonGroup(bGroup)
    end

    -- Hook the provisioning panel
    if not SousChef.hookedProvisioningFunction then
        local ref = PROVISIONER.recipeTree.templateInfo.ZO_ProvisionerNavigationEntry
        if ref then
            SousChef.hookedProvisioningFunction = ref.setupFunction
            ref.setupFunction =
            function(...)
                local node, control, data, open, userRequested, enabled = ...
                SousChef.hookedProvisioningFunction(...)
                local link = GetRecipeResultItemLink(data.recipeListIndex, data.recipeIndex, LINK_STYLE_BRACKETS)
                if link then
                    SousChef:AddNotifier(control, SousChef.settings.shoppingList[zo_strformat(SI_TOOLTIP_ITEM_NAME, link)])
                end
            end
        end
    end
    PROVISIONER:DirtyRecipeTree()
end

function SousChef:getMarkedIcon(row)
    local rankIcon = SousChef.slotLines[row:GetName()]
    if(not rankIcon) then
        rankIcon =  WINDOW_MANAGER:CreateControl(row:GetName() .. "SousChef", row, CT_TEXTURE)
        SousChef.slotLines[row:GetName()] = rankIcon
        rankIcon:SetTexture(m.CANLEARN)
        rankIcon:SetDimensions(20, 20)
        rankIcon:SetAnchor(RIGHT, row, LEFT, -10)
        rankIcon:SetMouseEnabled(true)
        rankIcon:SetHandler("OnMouseEnter", function()
                                                InitializeTooltip(InformationTooltip, rankIcon, RIGHT, -15, 0)
                                                InformationTooltip:AddLine("Shopping List")
                                            end)
        rankIcon:SetHandler("OnMouseExit", function()
                                                ClearTooltip(InformationTooltip)
                                            end)
    end
    return rankIcon
end

function SousChef:AddNotifier(control, marked)
    local icon = self:getMarkedIcon(control)
    if marked then
        icon:SetHidden(false)
    else
        icon:SetHidden(true)
    end
end

function SousChef:IsOnShoppingList(id)
    for i,v in ipairs(SousChef.settings.ReverseCookbook[id]) do
        if SousChef.settings.shoppingList[v] then return true end
    end
    return false
end

function SousChef:MarkRecipe()
    local link = GetRecipeResultItemLink(PROVISIONER:GetSelectedRecipeListIndex(), PROVISIONER:GetSelectedRecipeIndex(), LINK_STYLE_BRACKETS)
    SousChef.settings.shoppingList[zo_strformat(SI_TOOLTIP_ITEM_NAME, link)] = not SousChef.settings.shoppingList[zo_strformat(SI_TOOLTIP_ITEM_NAME, link)]
    PROVISIONER:DirtyRecipeTree()
end

function SousChef:HookRecipeTree(...)
    local eventId, craftingTable = ...
    if craftingTable ~= CRAFTING_TYPE_PROVISIONING then return end
    zo_callLater(function() SousChef:HookRecipeTreeFunction() end, 1000)
end

function SousChef:UnhookRecipeTree()
    if KEYBIND_STRIP:HasKeybindButtonGroup(bGroup) then
        KEYBIND_STRIP:RemoveKeybindButtonGroup(bGroup)
    end
end

function SousChef:HookGetRecipeInfo()
    SousChef.OldRefreshRecipeTree = ZO_Provisioner.RefreshRecipeTree
    ZO_Provisioner.RefreshRecipeTree =
        function()
            return SousChef.RefreshRecipeTree(PROVISIONER)
        end

    SousChef.OldZO_ProvisionerRow_GetTextColor = ZO_ProvisionerRow_GetTextColor
    ZO_ProvisionerRow_GetTextColor =
        function(self)
            return SousChef.ZO_ProvisionerRow_GetTextColor(self)
        end
end

local function SortRecipe(a, b)
    if a.provisionerLevelReq == b.provisionerLevelReq then
        if a.qualityReq == b.qualityReq then
            if a.name == b.name then
                return a.recipeIndex < b.recipeIndex
            end
            return a.name < b.name
        end
        return a.qualityReq < b.qualityReq
    end
    return a.provisionerLevelReq < b.provisionerLevelReq
end

local function CalculateHowManyCouldBeCreated(recipeListIndex, recipeIndex, numIngredients)
    local minCount

    for ingredientIndex = 1, numIngredients do
        local _, _, requiredQuantity = GetRecipeIngredientItemInfo(recipeListIndex, recipeIndex, ingredientIndex)
        local ingredientCount = GetCurrentRecipeIngredientCount(recipeListIndex, recipeIndex, ingredientIndex)

        minCount = zo_min(zo_floor(ingredientCount / requiredQuantity), minCount or math.huge)
        if minCount == 0 then
            return 0
        end
    end

    return minCount or 0
end

-- This is an almost direct clone of the existing function
-- Except for two parts.
-- 1) Storing the parent in the data (for each category of recipe)
-- 2) Putting them all in a table first, then sorting, then adding them to the tree
function SousChef:RefreshRecipeTree()
    self.dirty = false

    self.recipeTree:Reset()

    local hasAnyRecipes = false
    local RecipeLines = {}
    for recipeListIndex = 1, GetNumRecipeLists() do
        local recipeListName, numRecipes, upIcon, downIcon, overIcon, disabledIcon, createSound = GetRecipeListInfo(recipeListIndex)
        local parent

        for recipeIndex = 1, numRecipes do
            local known, recipeName, numIngredients, provisionerLevelReq, qualityReq, specialIngredientType = GetRecipeInfo(recipeListIndex, recipeIndex)
            if known then
                local numCreatable = CalculateHowManyCouldBeCreated(recipeListIndex, recipeIndex, numIngredients)
                if self:DoesRecipePassFilter(specialIngredientType, numCreatable, provisionerLevelReq, qualityReq) then
                    parent = parent or self.recipeTree:AddNode("ZO_IconHeader", { recipeListIndex = recipeListIndex, name = recipeListName, upIcon = upIcon, downIcon = downIcon, overIcon = overIcon, disabledIcon = disabledIcon }, nil, SOUNDS.PROVISIONING_BLADE_SELECTED)
                    local data = {
                        recipeListIndex = recipeListIndex,
                        recipeIndex = recipeIndex,

                        name = recipeName,
                        provisionerLevelReq = provisionerLevelReq,
                        qualityReq = qualityReq,
                        specialIngredientType = specialIngredientType,
                        numIngredients = numIngredients,
                        numCreatable = numCreatable,
                        createSound = createSound,
                        parent = parent
                    }

                    table.insert(RecipeLines, data)
                    hasAnyRecipes = true
                end
            end
        end
    end

    table.sort(RecipeLines, SortRecipe)
    for i,data in ipairs(RecipeLines) do
        self.recipeTree:AddNode("ZO_ProvisionerNavigationEntry", data, data.parent, SOUNDS.PROVISIONING_ENTRY_SELECTED)
    end

    self.recipeTree:Commit()

    self.noRecipesLabel:SetHidden(hasAnyRecipes)
    if not hasAnyRecipes then
        self:RefreshRecipeDetails()
    end
end

local function Lighten(...)
    r, g, b = ...
    return r + 0.1, g + 0.1, b + 0.1
end

local function Lightest(...)
    r, g, b = ...
    return r + 0.3, g + 0.3, b + 0.3
end

function SousChef:ZO_ProvisionerRow_GetTextColor()
    if not self.enabled then return GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_DISABLED) end

    if self.selected then
        if self.data.qualityReq > 1 then return Lightest(GetInterfaceColor(INTERFACE_COLOR_TYPE_ITEM_QUALITY_COLORS, self.data.qualityReq + 1)) end
        return GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_SELECTED)
    end

    if self.mouseover then
        if self.data.qualityReq > 1 then return Lighten(GetInterfaceColor(INTERFACE_COLOR_TYPE_ITEM_QUALITY_COLORS, self.data.qualityReq + 1)) end
     return GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_HIGHLIGHT)
    end

    if self.meetsLevelReq and self.meetsQualityReq then
        if self.data.qualityReq > 1 then return GetInterfaceColor(INTERFACE_COLOR_TYPE_ITEM_QUALITY_COLORS, self.data.qualityReq + 1) end
        return GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_NORMAL)
    end

    return ZO_ERROR_COLOR:UnpackRGBA()
end