--[[------------------------------------------------------------------
--SousChef.lua
--Author: Wobin

inspired by ingeniousclown's Research Assistant
Thanks to Ayantir for the French translations, and sirinsidiator for the German

------------------------------------------------------------------]]--

SousChef = {}
SousChef.Utility = {}
SousChef.Media = {}
SousChef.version = "2.18"

local SousChef = SousChef
local u = SousChef.Utility

local LAM = LibStub:GetLibrary("LibAddonMenu-2.0")

local BACKPACK = ZO_PlayerInventoryBackpack
local BANK = ZO_PlayerBankBackpack
local GUILD_BANK = ZO_GuildBankBackpack

SousChef.Pantry = {}
SousChef.Cookbook = {}
SousChef.CookbookIndex = {}
SousChef.ReverseCookbook = {}
SousChef.settings = nil
SousChef.slotLines = {}
SousChef.hookedFunctions = {}
SousChef.hookedDataFunction = nil
SousChef.lang = GetCVar("Language.2")
if SousChef.lang ~= "en" and SousChef.lang ~= "de" and SousChef.lang ~= "fr" then SousChef.lang = "en" end

local rowHandler = {}

local typeIconLookup = {
	-- en
	["Grilled"] = 9,
	["Bread and Pies"] = 10,
	["Soup and Stew"] = 11,
	["Beer"] = 12,
	["Spirits"] = 13,
	["Wine"] = 14,
	-- de
	["Gegrillt"] = 9,
	["Brot und Kuchen"] = 10,
	["Suppen und Eintöpfe"] = 11,
	["Bier"] = 12,
	["Getränke"] = 13,
	["Wein"] = 14,
	-- fr
	["Grillades"] = 9,
	["Pain et Tartes"] = 10,
	["Soupe et Ragoût"] = 11,
	["Bi\195\169re"] = 12,
	["Spiritueux"] = 13,
	["Vin"] = 14,
}

-- FUNCTIONS
-- AddRecipe(Cookbook, link) adds the linked recipe to the player cookbook if it isn't there already.
local function AddRecipe(Cookbook, link)
	for _,v in pairs(Cookbook) do
		if v == link then return end
	end
	table.insert(Cookbook, link)
end

-- RefreshViews() incorporates changes to the rendered inventory if it is visible.
function SousChef:RefreshViews()
	ZO_ScrollList_RefreshVisible(BACKPACK)
	ZO_ScrollList_RefreshVisible(BANK)
	ZO_ScrollList_RefreshVisible(GUILD_BANK)
end

-- every time you change the main character in the settings menu, you need to update the "player" cookbook, cookbook index, pantry, and reverse cookbook with the new character's data
local function ChangeMainChar()
	if SousChef.settings.mainChar == "(current)" then
		SousChef.CookbookIndex = SousChef.settings.CookbookIndex[GetUnitName("player")]
	else
		SousChef.CookbookIndex = SousChef.settings.CookbookIndex[SousChef.settings.mainChar]
	end

	SousChef.Cookbook = {}
	SousChef.Pantry = {}
	SousChef.ReverseCookbook = {}
	if SousChef.CookbookIndex == nil then SousChef.CookbookIndex = {} end
	for name, data in pairs(SousChef.CookbookIndex) do
		SousChef.Cookbook[u.CleanString((GetRecipeResultItemInfo(data.listIndex, data.recipeIndex)))] = true
		local _, _, _, level, _, specialType = GetRecipeInfo(data.listIndex, data.recipeIndex)
		for ingredient = 1, data.numIngredients do
			local ingredientID = u.GetItemID(GetRecipeIngredientItemLink(data.listIndex, data.recipeIndex, ingredient))
			if ingredient < 3 or not SousChef.settings.showSpecialIngredients then
				SousChef.Pantry[ingredientID] = math.max(level, SousChef.Pantry[ingredientID] or 0)
			else
				SousChef.Pantry[ingredientID] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8
			end
			if not SousChef.ReverseCookbook[ingredientID] then SousChef.ReverseCookbook[ingredientID] = {} end
			AddRecipe(SousChef.ReverseCookbook[ingredientID], name)
		end
	end
end

-- ParseRecipes() goes through the player's known recipes and records their info.
function SousChef:ParseRecipes()
	local lists = GetNumRecipeLists()

	for listIndex = 1, lists do
		local name, count = GetRecipeListInfo(listIndex)
		for recipeIndex = 1, count do
			if GetRecipeInfo(listIndex, recipeIndex) then
				-- Store the recipes known:
				local recipeName = u.CleanString((GetRecipeResultItemInfo(listIndex, recipeIndex)))
				-- in this character's cookbook
                --SousChef.Cookbook[recipeName] = true
				-- and in the settings, so other characters can see what recipes this character knows
				if not SousChef.settings.Cookbook[recipeName] then SousChef.settings.Cookbook[recipeName] = {} end
                SousChef.settings.Cookbook[recipeName][GetUnitName("player")] = true

				-- now record information about the recipe's ingregients
				local _, _, ingredientCount, level, _, specialType = GetRecipeInfo(listIndex, recipeIndex)
				-- store the recipe's index numbers and number of ingredients
				local resultLink = GetRecipeResultItemLink(listIndex, recipeIndex)
				local coloredName = u.GetColoredLinkName(resultLink)
				if not SousChef.settings.CookbookIndex[GetUnitName("player")] then SousChef.settings.CookbookIndex[GetUnitName("player")] = {} end
				SousChef.settings.CookbookIndex[GetUnitName("player")][coloredName] = {listIndex = listIndex, recipeIndex = recipeIndex, numIngredients = ingredientCount}
				-- now, for every ingredient in the current recipe...
				for ingredientIndex = 1, ingredientCount do
					local link = u.GetItemID(GetRecipeIngredientItemLink(listIndex, recipeIndex, ingredientIndex, LINK_STYLE_DEFAULT))
					-- Store the fact that the ingredient is used
                    if ingredientIndex < 3 or not SousChef.settings.showSpecialIngredients then
						-- if this isn't a special ingredient, or if the user doesn't want special ingredients marked as special, just record the highest-tier recipe this ingredient appears in
						-- for this character,
                        --SousChef.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0)
						-- and across characters
                        SousChef.settings.Pantry[link] = math.max(level, SousChef.Pantry[link] or 0)
                    else
						-- if this is a special ingredient, record:
						-- 						type icon number if user wants type icons,								7 if ingredient is a flavoring,				8 otherwise (ie, ingredient is a spice)
                        --SousChef.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8
                        SousChef.settings.Pantry[link] = SousChef.settings.typeIcon and typeIconLookup[name] or specialType == PROVISIONER_SPECIAL_INGREDIENT_TYPE_FLAVORING and 7 or 8
                    end
					-- Store the recipe it's used in to the character reverseCookbook
					--if not SousChef.ReverseCookbook[link] then SousChef.ReverseCookbook[link] = {} end
					--AddRecipe(SousChef.ReverseCookbook[link], coloredName)
					-- ...and to the account-wide reverseCookbook
					if not SousChef.settings.ReverseCookbook[link] then SousChef.settings.ReverseCookbook[link] = {} end
					AddRecipe(SousChef.settings.ReverseCookbook[link], coloredName)
				end
			end
		end
	end

	ChangeMainChar()
	SousChef:RefreshViews()
end

-- auto-junk ingredients if they're not in the shopping list
local function AutoJunker()
	local bagSize = GetBagSize(BAG_BACKPACK)
	for i = 0, bagSize do
		local itemLink = GetItemLink(BAG_BACKPACK, i)
		if itemLink ~= "" then
			local itemType = GetItemLinkItemType(itemLink)
			if itemType == ITEMTYPE_FLAVORING or itemType == ITEMTYPE_SPICE or itemType == ITEMTYPE_INGREDIENT then
				if not SousChef:IsOnShoppingList(u.GetItemID(itemLink)) then
					SetItemIsJunk(BAG_BACKPACK, i, true)
				end
			end
		end
	end
end

-- SousChefCreateSettings() creates the configuration menu for the add-on
local function SousChefCreateSettings()
	local str = SousChef.Strings[SousChef.lang]
	local panelData = {
		type = "panel",
		name = "Sous Chef",
		displayName = "Sous Chef",
		author = "Wobin & KatKat42 & CrazyDutchGuy",
		version = SousChef.version,
		registerForRefresh = true,
		slashCommand = "/souschef",
	}

	LAM:RegisterAddonPanel("SousChefSettings", panelData)

	local optionsMenu = {
		[1] = {
			type = "header",
			name = str.MENU_RECIPE_HEADER,
			width = "full",
		},
		[2] = {
			type = "checkbox",
			name = str.MENU_PROCESS_RECIPES,
			tooltip = str.MENU_PROCESS_RECIPES_TOOLTIP,
			getFunc = function() return SousChef.settings.processRecipes end,
			setFunc = function(value) SousChef.settings.processRecipes = value end,
			width = "full",
		},
		[3] = {
			type = "dropdown",
			name = str.MENU_MAIN_CHAR,
			tooltip = str.MENU_MAIN_CHAR_TOOLTIP,
			choices = SousChef.settings.knownChars,
			getFunc = function() return SousChef.settings.mainChar end,
			setFunc = function(value)
				SousChef.settings.mainChar = value
				ChangeMainChar()
			end,
			disabled = function() return not SousChef.settings.processRecipes end,
		},
		[4] = {
			type = "dropdown",
			name = str.MENU_MARK_IF_KNOWN,
			tooltip = str.MENU_MARK_IF_KNOWN_TOOLTIP,
			choices = {str.MENU_KNOWN, str.MENU_UNKNOWN},
			getFunc = function()
				if SousChef.settings.checkKnown == "known" then
					return str.MENU_KNOWN
				elseif SousChef.settings.checkKnown == "unknown" then
					return str.MENU_UNKNOWN
				else
					-- can't happen
					d("Yikes! MENU_MARK_IF_KNOWN getter")
					return str.MENU_UNKNOWN
				end
			end,
			setFunc = function(valueString)
				if valueString == str.MENU_KNOWN then
					SousChef.settings.checkKnown = "known"
				elseif valueString == str.MENU_UNKNOWN then
					SousChef.settings.checkKnown = "unknown"
				else
					-- can't happen
					d("Oops! MENU_MARK_IF_KNOWN setter")
					SousChef.settings.checkKnown = "unknown"
				end
			end,
			disabled = function() return not SousChef.settings.processRecipes end,
		},
		[5] = {
			type = "checkbox",
			name = str.MENU_MARK_IF_ALT_KNOWS,
			tooltip = str.MENU_MARK_IF_ALT_KNOWS_TOOLTIP,
			getFunc = function() return SousChef.settings.markAlt end,
			setFunc = function(value) SousChef.settings.markAlt = value SousChef:RefreshViews() end,
			disabled = function() return (not SousChef.settings.processRecipes) or (SousChef.settings.checkKnown == "known") end,
		},
		[6] = {
			type = "checkbox",
			name = str.MENU_TOOLTIP_IF_ALT_KNOWS,
			tooltip = str.MENU_TOOLTIP_IF_ALT_KNOWS_TOOLTIP,
			getFunc = function() return SousChef.settings.showAltKnowledge end,
			setFunc = function(value) SousChef.settings.showAltKnowledge = value SousChef:RefreshViews() end,
			disabled = function() return not SousChef.settings.processRecipes end,
		},
		[7] = {
			type = "checkbox",
			name = str.MENU_MATCHER,
			tooltip = str.MENU_MATCHER_TOOLTIP,
			getFunc = function() return SousChef.settings.experimentalMatch end,
			setFunc = function(value) SousChef.settings.experimentalMatch = value end,
			disabled = function() return not SousChef.settings.processRecipes end,
		},
		[8] = {
			type = "checkbox",
			name = str.MENU_SORT_PROVISIONING,
			tooltip = str.MENU_SORT_PROVISIONING_TOOLTIP,
			getFunc = function() return SousChef.settings.sortProvisioningTable end,
			setFunc = function(value)
				SousChef.settings.sortProvisioningTable = value
				if value then
					SousChef:HookGetRecipeInfo()
				else
					SousChef:UnhookGetRecipeInfo()
				end
			end,
		},

		[9] = {
			type = "header",
			name = str.MENU_TOOLTIP_HEADER,
			width = "full",
		},
		[10] = {
			type = "checkbox",
			name = str.MENU_TOOLTIP_CLICK,
			tooltip = str.MENU_TOOLTIP_CLICK_TOOLTIP,
			warning = str.MENU_RELOAD,
			getFunc = function() return SousChef.settings.showOnClick end,
			setFunc = function(value) SousChef.settings.showOnClick = value end,
		},
		[11] = {
			type = "checkbox",
			name = str.MENU_RESULT_COUNTS,
			tooltip = str.MENU_RESULT_COUNTS_TOOLTIP,
			getFunc = function() return SousChef.settings.showCounts end,
			setFunc = function(value) SousChef.settings.showCounts = value end,
		},
		[12] = {
			type = "checkbox",
			name = str.MENU_ALT_USE,
			tooltip = str.MENU_ALT_USE_TOOLTIP,
			getFunc = function() return SousChef.settings.showAltIngredientKnowledge end,
			setFunc = function(value) SousChef.settings.showAltIngredientKnowledge = value SousChef:RefreshViews() end,
		},

		[13] = {
			type = "header",
			name = str.MENU_INDICATOR_HEADER,
			width = "full",
		},
		[14] = {
			type = "checkbox",
			name = str.MENU_ICON_SET,
			tooltip = str.MENU_ICON_SET_TOOLTIP,
			getFunc = function() return SousChef.settings.boldIcon end,
			setFunc = function(value) SousChef.settings.boldIcon = value SousChef:RefreshViews() end,
		},
		[15] = {
			type = "checkbox",
			name = str.MENU_SPECIAL_ICONS,
			tooltip = str.MENU_SPECIAL_ICONS_TOOLTIP,
			getFunc = function() return SousChef.settings.showSpecialIngredients end,
			setFunc = function(value)
				SousChef.settings.showSpecialIngredients = value
				SousChef.ParseRecipes()
			end,
		},
		[16] = {
			type = "checkbox",
			name = str.MENU_SPECIAL_TYPES,
			tooltip = str.MENU_SPECIAL_TYPES_TOOLTIP,
			getFunc = function() return SousChef.settings.typeIcon end,
			setFunc = function(value) SousChef.settings.typeIcon = value SousChef.ParseRecipes() SousChef:RefreshViews() end,
			disabled = function() return not SousChef.settings.showSpecialIngredients end,
		},
		[17] = {
			type = "colorpicker",
			name = str.MENU_INDICATOR_COLOR,
			tooltip = str.MENU_INDICATOR_COLOR_TOOLTIP,
			getFunc = function() return SousChef.settings.colour[1], SousChef.settings.colour[2], SousChef.settings.colour[3] end,
			setFunc = function(r,g,b)
				SousChef.settings.colour[1] = r
				SousChef.settings.colour[2] = g
				SousChef.settings.colour[3] = b
				SousChef:RefreshViews()
			end,
		},
		[18] = {
			type = "colorpicker",
			name = str.MENU_SHOPPING_COLOR,
			tooltip = str.MENU_SHOPPING_COLOR_TOOLTIP,
			getFunc = function() return SousChef.settings.shoppingColour[1], SousChef.settings.shoppingColour[2], SousChef.settings.shoppingColour[3] end,
			setFunc = function(r,g,b)
				SousChef.settings.shoppingColour[1] = r
				SousChef.settings.shoppingColour[2] = g
				SousChef.settings.shoppingColour[3] = b
				SousChef:RefreshViews()
			end,
		},
		[19] = {
			type = "checkbox",
			name = str.MENU_SHOW_ALT_SHOPPING,
			tooltip = str.MENU_SHOW_ALT_SHOPPING_TOOLTIP,
			getFunc = function() return SousChef.settings.showAltShopping end,
			setFunc = function(value) SousChef.settings.showAltShopping = value SousChef:RefreshViews() end,
		},
		[20] = {
			type = "checkbox",
			name = str.MENU_ONLY_MARK_SHOPPING,
			tooltip = str.MENU_ONLY_MARK_SHOPPING_TOOLTIP,
			getFunc = function() return SousChef.settings.onlyShowShopping end,
			setFunc = function(value) SousChef.settings.onlyShowShopping = value SousChef:RefreshViews() end,
		},
		[21] = {
			type = "checkbox",
			name = str.MENU_AUTO_JUNK,
			tooltip = str.MENU_AUTO_JUNK_TOOLTIP,
			getFunc = function() return SousChef.settings.autoJunk end,
			setFunc = function(value)
				if value then
					SousChef.settings.autoJunk = true
					EVENT_MANAGER:RegisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED, function(...) zo_callLater(AutoJunker, 100) end)
				else
					SousChef.settings.autoJunk = false
					EVENT_MANAGER:UnregisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED)
				end
			end,
			warning = str.MENU_AUTO_JUNK_WARNING,
		},
		[22] = {
			type = "checkbox",
			name = str.MENU_SORT_INGREDIENTS,
			tooltip = str.MENU_SORT_INGREDIENTS_TOOLTIP,
			getFunc = function() return SousChef.settings.sortKnownIngredients end,
			setFunc = function(value)
				SousChef.settings.sortKnownIngredients = not SousChef.settings.sortKnownIngredients
				if not SousChef.settings.sortKnownIngredients then
					SousChef.UnregisterSort()
				else
					SousChef.SetupSort()
				end
			end,
		},
	}
	LAM:RegisterOptionControls("SousChefSettings", optionsMenu)
end

-- SousChef_Loaded(eventCode, addOnName) runs when the EVENT_ADD_ON_LOADED event fires.
local function SousChef_Loaded(eventCode, addOnName)
	if(addOnName ~= "SousChef") then return end

	-- default config settings
	local defaults = {
		--watching = true,
		checkKnown = "unknown",
		markAlt = false,
		colour = {1, 1, 1},
		shoppingColour = {0,1,1},
		Cookbook = {},
		CookbookIndex = {},
		Pantry = {},
		ReverseCookbook = {},
		showAltKnowledge = false,
		showAltIngredientKnowledge = false,
		boldIcon = false,
		typeIcon = true,
        experimentalMatch = false,
        processRecipes = true,
        showSpecialIngredients = false,
        ignoredRecipes = {},
        showOnClick = false,
        showCounts = true,
        shoppingList = {},
        onlyShowShopping = false,
        qualityChecked = false,
        sortProvisioningTable = true,
        sortKnownIngredients = false,
		mainChar = GetUnitName("player"),
		knownChars = { "(current)" },
		autoJunk = false,
		showAltShopping = true,
	}

	local localized = SousChef.Strings[SousChef.lang]

	-- Fetch the saved variables
    SousChef.settings = ZO_SavedVars:NewAccountWide("SousChef_Settings", 10, SousChef.lang, defaults)
	-- if this character isn't in the list of known chars, add it
	local addMe = true
	local addCurrent = true
	for _, v in pairs(SousChef.settings.knownChars) do
		if GetUnitName("player") == v then addMe = false end
		if v == "(current)" then addCurrent = false end
	end
	if addMe then
		local myName = GetUnitName("player")
		table.insert(SousChef.settings.knownChars, GetUnitName("player"))
	end
	if addCurrent then
		table.insert(SousChef.settings.knownChars, "(current)")
	end

	-- define some slash commands
	SLASH_COMMANDS['/scstats'] = function()
		d(localized.SC_NUM_RECIPES_KNOWN.. NonContiguousCount(SousChef.settings.Cookbook))
		d(localized.SC_NUM_INGREDIENTS_TRACKED..NonContiguousCount(SousChef.settings.Pantry))
	end
	SLASH_COMMANDS['/sciadd'] = SousChef.AddRecipeToIgnoreList
	SLASH_COMMANDS['/sciremove'] = SousChef.RemoveRecipeFromIgnoreList
	SLASH_COMMANDS['/scilist'] = SousChef.ListIgnoredRecipes

	-- initialize the configuration menu
	SousChefCreateSettings()

	-- parse the recipes this character knows, in a second
	zo_callLater(SousChef.ParseRecipes, 500)

	SousChef:UpdateProvisioningTable()
	SousChef:HookGetRecipeInfo()

	if SousChef.settings.sortKnownIngredients then SousChef.SetupSort() end

	ZO_CreateStringId("SI_BINDING_NAME_SC_MARK_RECIPE", localized.KEY_MARK)

	-- Now we register for some events, and hook into the function that sets the details on the inventory slot
	zo_callLater(SousChef.HookEvents, 2000)
end

-- HookEvents() registers the add-on for some events
function SousChef.HookEvents()
	-- let us know if we're opening a trading house, so we can look at its contents
	EVENT_MANAGER:RegisterForEvent("SousChefTrading", EVENT_TRADING_HOUSE_RESPONSE_RECEIVED, SousChef.HookTrading)
	-- let us know if we've learned a new recipe, so we can integrate it into our cookbook
	EVENT_MANAGER:RegisterForEvent("SousChefLearnt", EVENT_RECIPE_LEARNED, SousChef.ParseRecipes)
	-- if the user has turned on auto-junking unmarked ingredients, set that up
	if SousChef.settings.autoJunk then
		EVENT_MANAGER:RegisterForEvent("SousChefLootJunker", EVENT_LOOT_CLOSED, function(...) zo_callLater(AutoJunker, 100) end)
	end
	-- let us know when we open a crafting station, so we can sort the recipe tree
    EVENT_MANAGER:RegisterForEvent("SousChefProvi", EVENT_CRAFTING_STATION_INTERACT, function(...) SousChef:HookRecipeTree(...) end)
    EVENT_MANAGER:RegisterForEvent("SousChefProviEnd", EVENT_END_CRAFTING_STATION_INTERACT, function() SousChef:UnhookRecipeTree() end)
    -- and finally, hook the opening of the inventory, bank, guild bank, and loot windows so we can add icons and hook the tooltips
    SousChef.HookInventory()
end

EVENT_MANAGER:RegisterForEvent("SousChefLoaded", EVENT_ADD_ON_LOADED, SousChef_Loaded)