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

-- SousChef:UpdateProvisioningTable() adds the Quality checkbox to the provisioner window and overrides the filtering function to incorporate it
function SousChef:UpdateProvisioningTable()
    local function OnFilterChanged()
        self.settings.qualityChecked = ZO_CheckButton_IsChecked(self.qualityCheckBox)
        PROVISIONER:DirtyRecipeTree()
    end
	-- set up the quality checkbox
    self.qualityCheckBox = SousChef_ControllerOnlyQuality
    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)

    -- make room for the checkbox
    ZO_ProvisionerTopLevelHaveSkills:ClearAnchors()
    ZO_ProvisionerTopLevelHaveSkills:SetAnchor( LEFT, ZO_ProvisionerTopLevelHaveIngredients, RIGHT, 180 )

	-- incorporate new checkbox into the filter
    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

-- this is our "Mark Recipe" button definition
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

-- SousChef:HookRecipeTreeFunction() adds the "Mark Recipe" button to the keybind bar, and set the provisioning window to display checkmarks by marked recipes
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 = zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(data.recipeListIndex, data.recipeIndex, LINK_STYLE_BRACKETS))
                if link then
                    SousChef:AddNotifier(control, type(SousChef.settings.shoppingList[link]) == "table" and u.TableKeyConcat(SousChef.settings.shoppingList[link]))
                end
            end
        end
    end
    PROVISIONER:DirtyRecipeTree()
end

-- SousChef:getMarkedIcon(row, marked) adds the checkmark next to each marked line in the provisioning window
function SousChef:getMarkedIcon(row, marked)
    local rankIcon = SousChef.slotLines[row:GetName()]
	-- first, if the control for the checkmark doesn't exist, create it
    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("OnMouseExit", function()
                                                ClearTooltip(InformationTooltip)
                                            end)
    end
	-- then, if the recipe is marked by someone, show who, and set the checkmark's alpha accordingly
    if marked then
        rankIcon:SetHandler("OnMouseEnter", function()
                                        InitializeTooltip(InformationTooltip, rankIcon, RIGHT, -15, 0)
                                        InformationTooltip:AddLine("Marked by: " .. marked)
                                    end)
        if not marked:find(GetUnitName("player"), 1, true) then
            rankIcon:SetColor(1,1,1,0.2)
        else
            rankIcon:SetColor(1,1,1,1)
        end
    end
    return rankIcon
end

-- SousChef:AddNotifier(control, marked) shows or hides the checkmark next to each recipe according to whether it's marked by anyone
function SousChef:AddNotifier(control, marked)
    local icon = self:getMarkedIcon(control, marked)
    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 type(SousChef.settings.shoppingList[v]) == "table" and next(SousChef.settings.shoppingList[v]) then return SousChef.settings.shoppingList[v] end
    end
    return false
end

function SousChef:MarkRecipe()
    local link = zo_strformat(SI_TOOLTIP_ITEM_NAME, GetRecipeResultItemLink(PROVISIONER:GetSelectedRecipeListIndex(), PROVISIONER:GetSelectedRecipeIndex(), LINK_STYLE_BRACKETS))

    if type(SousChef.settings.shoppingList[link]) ~= "table" then SousChef.settings.shoppingList[link]  = {} end

    if not SousChef.settings.shoppingList[link][GetUnitName("player")] then
        SousChef.settings.shoppingList[link][GetUnitName("player")] = true -- we're now marked
    else
        SousChef.settings.shoppingList[link][GetUnitName("player")] = nil -- we're now unmarked
		-- if that was the last mark we just got rid of, then nil out the entire recipe entry
		if u.TableLength(SousChef.settings.shoppingList[link]) == 0 then
			SousChef.settings.shoppingList[link] = nil
		end
    end

    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()

    if SousChef.settings.sortProvisioningTable then
        SousChef.OldRefreshRecipeTree = ZO_Provisioner.RefreshRecipeTree
        ZO_Provisioner.RefreshRecipeTree =
            function()
                return SousChef.RefreshRecipeTree(PROVISIONER)
            end
    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.useLevelReq == b.useLevelReq 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.useLevelReq < b.useLevelReq
    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)
			local useLevelReq = GetItemLinkRequiredLevel(GetRecipeResultItemLink(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,
						useLevelReq = useLevelReq,
                        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