local _, addon = ...
local RemixAPI = addon.RemixAPI
if not RemixAPI then return end;


local API = addon.API;
local L = addon.L;
local GetInventoryItemID = GetInventoryItemID;
local GetSpellTexture = C_Spell.GetSpellTexture;
local DataProvider = RemixAPI.DataProvider;


local TEXTURE_FILE = "Interface/AddOns/Plumber/Art/Timerunning/LegionPaperDollWidget.png";


local Controller = CreateFrame("Frame");
do  --Controller
	Controller.WidgetContainer = CreateFrame("Frame");
	Controller.WidgetContainer:SetSize(24, 24);

	function Controller:Init()
		self.Init = nil;

		local parentFrame = PaperDollFrame;

		parentFrame:HookScript("OnShow", function()
			if self.isEnabled then
				self:ListenEvents(true);
				self.WidgetContainer:Show();
				self:UpdateWidgets();
			end
		end);

		parentFrame:HookScript("OnHide", function()
			self:ListenEvents(false);
		end);

		local pane2 = PaperDollFrame.TitleManagerPane;
		local pane3 = PaperDollFrame.EquipmentManagerPane;

		local function UpdateVisibility()
			if pane2:IsShown() or pane3:IsShown() then
				self:ListenEvents(false);
				self.WidgetContainer:Hide();
			else
				if self.isEnabled and parentFrame:IsVisible() then
					self:ListenEvents(true);
					self.WidgetContainer:Show();
					self:UpdateWidgets();
				end
			end
		end

		pane2:HookScript("OnShow", UpdateVisibility);
		pane2:HookScript("OnHide", UpdateVisibility);
		pane3:HookScript("OnShow", UpdateVisibility);
		pane3:HookScript("OnHide", UpdateVisibility);

		self.Init = nil;
	end

	function Controller:UpdateParent()
		if GwDressingRoomGear then
			local parentFrame = GwDressingRoomGear;
			self.WidgetContainer:SetParent(parentFrame);

			parentFrame:HookScript("OnShow", function()
				if Controller.isEnabled then
					Controller:ListenEvents(true);
					self.WidgetContainer:Show();
					Controller:UpdateWidgets();
				end
			end);

			parentFrame:HookScript("OnHide", function()
				Controller:ListenEvents(false);
			end);
		end
	end

	function Controller:UpdatePosition_OnShow()
		--adjustment for serveral addons/WA
		local WidgetContainer = Controller.WidgetContainer;

		if CSPilvl then
			--Chonky Character Sheet    wago.io/bRl2gJIgz
			WidgetContainer:ClearAllPoints();
			WidgetContainer:SetPoint("CENTER", CSPilvl, "RIGHT", 22, 0);     --anchor changed after swapping items, IDK why
		elseif C_AddOns.IsAddOnLoaded("DejaCharacterStats") then
			WidgetContainer:ClearAllPoints();
			WidgetContainer:SetPoint("CENTER", PaperDollFrame, "TOPRIGHT", -1, -84);
		elseif GwDressingRoomGear then   --GW2 UI
			WidgetContainer:SetParent(GwDressingRoomGear);
			WidgetContainer:ClearAllPoints();
			WidgetContainer:SetPoint("CENTER", GwDressingRoomGear, "TOPRIGHT", 12, -60);
		elseif CharacterFrame and CharacterStatsPane and CharacterStatsPane.ItemLevelFrame then
			--A universal approach to align to the ItemLevelFrame center    (DejaCharStats)
			local anchor = CharacterStatsPane.ItemLevelFrame;
			local _, anchorY = anchor:GetCenter();
			if anchorY then
				local y0 = CharacterFrame:GetTop();
				WidgetContainer:ClearAllPoints();
				WidgetContainer:SetPoint("CENTER", PaperDollFrame, "TOPRIGHT", -1, anchorY - y0);
			end
		end

		Controller:ResetWidgetPosition();
		WidgetContainer:SetScript("OnShow", nil);
	end

	function Controller:Enable()
		if self.Init then
			self:Init();
		end

		local parentFrame = PaperDollFrame;
		local WidgetContainer = self.WidgetContainer;
		WidgetContainer:ClearAllPoints();
		WidgetContainer:SetParent(parentFrame);
		WidgetContainer:SetPoint("CENTER", parentFrame, "TOPRIGHT", -1, -119);
		WidgetContainer:Show();
		WidgetContainer:SetFrameStrata("HIGH");
		WidgetContainer:SetScript("OnShow", self.UpdatePosition_OnShow);
		self:SetScript("OnEvent", self.OnEvent);
		self:UpdateParent();
		self.isEnabled = true;

		for _, func in ipairs(self.controllerEnabledCallbacks) do
			func();
		end
	end

	function Controller:Disable()
		if not self.isEnabled then return end;
		self.isEnabled = false;
		self:ListenEvents(false);
		self.WidgetContainer:ClearAllPoints();
		self.WidgetContainer:Hide();
		self.WidgetContainer:SetParent(self);
	end

	function Controller:ListenEvents(state)
		if state then
			self:RegisterEvent("BAG_UPDATE_DELAYED");
			self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED");
		else
			self:UnregisterEvent("BAG_UPDATE_DELAYED");
			self:UnregisterEvent("PLAYER_EQUIPMENT_CHANGED");
		end
	end

	function Controller:OnEvent(event, ...)
		self.t = 0;
		self:SetScript("OnUpdate", self.OnUpdate);
	end

	function Controller:OnUpdate(elapsed)
		self.t = self.t + elapsed;
		if self.t >= 0.1 then
			self.t = nil;
			self:SetScript("OnUpdate", nil);
			self:UpdateWidgets();
		end
	end

	function Controller:UpdateWidgets()
		if self.widgets then
			for _, widget in pairs(self.widgets) do
				if widget.enabled then
					widget:Update();
				else
					widget:Hide();
				end
			end
		end
	end

	function Controller:AddWidget(newWidget, index, dbKey)
		if not self.widgets then
			self.widgets = {};
		end

		if self.widgets[index] then return false end;

		newWidget.parent = self.WidgetContainer;
		newWidget.dbKey = dbKey;
		newWidget:ResetAnchor();
		self.widgets[index] = newWidget;
		return true
	end

	function Controller:RemoveWidget(removedWidget)
		for index, widget in pairs(self.widgets) do
			if widget == removedWidget then
				self.widgets[index] = nil;
				widget:Hide();
				widget:ClearAllPoints();
				widget:SetParent(nil);
				break
			end
		end
	end

	function Controller:ResetWidgetPosition()
		if self.widgets then
			for _, widget in pairs(self.widgets) do
				widget:ResetAnchor();
			end
		end
	end

	function Controller:HideNarcissusWidgets()
		local con = NarciPaperDollWidgetController;
		if not con then return end;

		if con.isEnabled then
			con:ListenEvents(false);
			con.WidgetContainer:Hide();
			con.isEnabled = false;
		end
	end

	local PaperDollSlots = {
		[1] = "HeadSlot",
		[2] = "NeckSlot",
		[3] = "ShoulderSlot",
		[4] = "ShirtSlot",
		[5] = "ChestSlot",
		[6] = "WaistSlot",
		[7] = "LegsSlot",
		[8] = "FeetSlot",
		[9] = "WristSlot",
		[10]= "HandsSlot",
		[11]= "Finger0Slot",
		[12]= "Finger1Slot",
		[13]= "Trinket0Slot",
		[14]= "Trinket1Slot",
		[15]= "BackSlot",
		[16]= "MainHandSlot",
		[17]= "SecondaryHandSlot",
		[18]= "AmmoSlot",
		[19]= "TabardSlot",
	};

	function Controller:GetSlotButton(slotID)
		local slotButton = _G["Character"..PaperDollSlots[slotID]];
		return slotButton
	end

	Controller.controllerEnabledCallbacks = {};
	function Controller:AddOnEnabledCallbacks(func)
		table.insert(self.controllerEnabledCallbacks, func);
	end
end


local CreatePaperDollWidget;
do
	local PaperDollWidgetSharedMixin = {};

	function CreatePaperDollWidget(template)
		local f = CreateFrame("Button", nil, nil, template);
		API.Mixin(f, PaperDollWidgetSharedMixin);
		return f
	end

	function PaperDollWidgetSharedMixin:ResetAnchor()
		self:ClearAllPoints();
		self:SetParent(self.parent);
		self:SetPoint("CENTER", self.parent, "CENTER", self.offsetX or 0, 0);
	end

	function PaperDollWidgetSharedMixin:Update()

	end
end


local IconPoolUtil = {};
do
	IconPoolUtil.updateFuncs = {};
	local SubIconPools = {};

	function IconPoolUtil:CreateSubIconPool()
		local function IconFrame_Create()
			local object = CreateFrame("Frame");
			object:SetSize(16, 16);
			object.Icon = object:CreateTexture(nil, "OVERLAY");
			object.Icon:SetSize(16, 16);
			object.Icon:SetPoint("CENTER", object, "CENTER", 0, 0);
			local shrink = 8;
			object.Icon:SetTexCoord(shrink/64, 1-shrink/64, shrink/64, 1-shrink/64);
			object.Border = object:CreateTexture(nil, "OVERLAY", nil, 2);
			object.Border:SetTexture(TEXTURE_FILE);
			object.Border:SetTexCoord(384/512, 448/512, 0, 64/512);
			object.Border:SetSize(32, 32);
			object.Border:SetPoint("CENTER", object, "CENTER", 0, 0);
			object.Icon:SetTexture(134400);
			return object
		end

		local function IconFrame_Remove(object)
			object:ClearAllPoints();
			object:Hide();
			object:SetParent(nil);
		end

		local pool = API.CreateObjectPool(IconFrame_Create, IconFrame_Remove);
		table.insert(SubIconPools, pool);
		return pool
	end

	function IconPoolUtil.LoadSettings()
		local v = addon.GetDBValue("LegionRemix_TraitSubIconStyle");
		if not (v == 1 or v == 2 or v == 3) then
			v = 3;
		end
		IconPoolUtil.traitSubIconStyle = v;

		for _, func in ipairs(IconPoolUtil.updateFuncs) do
			func();
		end
	end

	function IconPoolUtil:AddUpdator(func)
		table.insert(self.updateFuncs, func)
	end

	Controller:AddOnEnabledCallbacks(IconPoolUtil.LoadSettings);
end



local LegionWidget = CreatePaperDollWidget("PlumberLegionRemixPaperDollWidgetTemplate");
do
	LegionWidget.Background:SetTexture(TEXTURE_FILE);
	LegionWidget.Background:SetTexCoord(0, 120/512, 0, 96/512);
	LegionWidget.LoopingGlow:SetTexture(TEXTURE_FILE);
	LegionWidget.LoopingGlow:SetTexCoord(256/512, 376/512, 128/512, 224/512);
	LegionWidget.Sheen:SetTexture(TEXTURE_FILE);
	LegionWidget.Sheen:SetTexCoord(128/512, 248/512, 0, 96/512);
	LegionWidget.SheenMask:SetTexture("Interface/AddOns/Plumber/Art/Timerunning/Mask-Halo", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
	LegionWidget.Tooltip.BackgroundArt:SetTexture(TEXTURE_FILE);
	LegionWidget.Tooltip.BackgroundArt:SetTexCoord(0/512, 160/512, 96/512, 256/512);
	LegionWidget:RegisterForClicks("LeftButtonUp", "RightButtonUp");

	Controller:AddWidget(LegionWidget, 1, "LegionRemixPaperDollWidget");

	function LegionWidget:PlaySheen()
		self.Sheen:Show();
		self.AnimSheen:Play();
	end

	function LegionWidget:PlayPulse()
		self.LoopingGlow:Show();
		self.AnimLoopPulse:Play();
	end

	function LegionWidget:OnEnter()
		self:PlaySheen();
		self:ShowTooltip(true);
		self:RegisterEvent("MODIFIER_STATE_CHANGED");
		self:SetScript("OnEvent", self.OnEvent);
	end
	LegionWidget:SetScript("OnEnter", LegionWidget.OnEnter);

	function LegionWidget:OnLeave()
		self.Tooltip:Hide();
		self:UnregisterEvent("MODIFIER_STATE_CHANGED");
	end
	LegionWidget:SetScript("OnLeave", LegionWidget.OnLeave);

	function LegionWidget:OnClick(button)
		if button == "RightButton" then
			self.Tooltip:Hide();
			self:ShowContextMenu();
			return
		end

		RemixAPI.ToggleArtifactUI();
	end
	LegionWidget:SetScript("OnClick", LegionWidget.OnClick);

	function LegionWidget:OnHide()
		self.Tooltip:Hide();
		self.t = nil;
		self:SetScript("OnUpdate", nil);
		self:UnregisterEvent("MODIFIER_STATE_CHANGED");
		self:SetScript("OnEvent", nil);
	end
	LegionWidget:SetScript("OnHide", LegionWidget.OnHide);

	function LegionWidget:OnEvent(event, ...)
		if event == "MODIFIER_STATE_CHANGED" then
			local key, down = ...
			if (key == "LALT" or key == "RALT") and down == 1 and self:IsMouseMotionFocus() then
				addon.FlipDBBool("LegionRemix_PaperDollTraitDetail");
				self:ShowTooltip();
			end
		end
	end

	local function SharedFadeIn_OnUpdate(self, elapsed)
		self.t = self.t + elapsed;
		local alpha = 8 * self.t;
		if alpha > 1 then
			alpha = 1;
			self:SetScript("OnUpdate", nil);
			self.t = 0;
		end
		self:SetAlpha(alpha);
	end

	function LegionWidget:ShowTooltip(fadeIn)
		local tooltip = self.Tooltip;
		tooltip:SetParent(UIParent);    --Change parent so FrameStrata can work

		local isLoaded = true;
		local text3;

		local currencyInfo = C_CurrencyInfo.GetCurrencyInfo(3292);    --Infinite Knowledge
		if currencyInfo then
			text3 = string.format("|cffe6cc80%s:|r |cffffffff%s/%s|r\n", currencyInfo.name, currencyInfo.quantity, currencyInfo.maxQuantity);
		end


		local spellsAndRanks = DataProvider:GetIncreasedTraitSpellsAndRanks();
		if spellsAndRanks then
			if text3 then
				text3 = text3.."\n|cff999999"..L["Bonus Traits"].."|r";
			end

			local traitFormat = "+%d  |T%s:16:16:0:-2:64:64:4:60:4:60|t |cffffd100%s|r";
			for k, v in ipairs(spellsAndRanks) do
				local spellID = v[1];
				local totalIncreased = v[2];
				local spellIcon = GetSpellTexture(spellID) or 134400;
				local spellName = C_Spell.GetSpellName(spellID);
				if not spellName then
					spellName = " ";
					isLoaded = false;
				end
				local lineText = string.format(traitFormat, totalIncreased, spellIcon, spellName);
				if text3 then
					text3 = text3.."\n"..lineText;
				else
					text3 = lineText;
				end
			end
		end

		local nextUpgradeInfo = DataProvider:GetNextUpgradeInfoForUI();
		if nextUpgradeInfo then
			tooltip.Text1:SetText(nextUpgradeInfo.line1);
			tooltip.Text1:SetTextColor(0.6, 0.6, 0.6);
			tooltip.Text2:SetText(nextUpgradeInfo.line2);
			tooltip.Text2:SetTextColor(177/255, 190/255, 95/255);
		end

		tooltip.Text3:SetText(text3);
		tooltip.Instruction:SetText(L["Instruction Open Artifact UI"]);
		tooltip.Instruction:SetTextColor(0.6, 0.6, 0.6);

		local spacing = 16;
		local width = math.max(tooltip.Text1:GetWrappedWidth(), tooltip.Text2:GetWrappedWidth(), (text3 and tooltip.Text3:GetWrappedWidth()) or 0, tooltip.Instruction:GetWrappedWidth());
		local height = tooltip.Text1:GetHeight() + 4 + tooltip.Text2:GetHeight() + (text3 and (spacing + tooltip.Text3:GetHeight()) or 0) + spacing + tooltip.Instruction:GetHeight();
		local padding = 20;

		tooltip:SetSize(width + 2*padding, height + 2*padding + 6);

		tooltip.Text1:ClearAllPoints();
		tooltip.Text1:SetPoint("TOPLEFT", tooltip, "TOPLEFT", padding, -padding);
		tooltip.Text2:ClearAllPoints();
		tooltip.Text2:SetPoint("TOPLEFT", tooltip.Text1, "BOTTOMLEFT", 0, -4);
		if text3 then
			tooltip.Text3:ClearAllPoints();
			tooltip.Text3:SetPoint("TOPLEFT", tooltip.Text2, "BOTTOMLEFT", 0, -spacing);
			tooltip.Instruction:ClearAllPoints();
			tooltip.Instruction:SetPoint("TOPLEFT", tooltip.Text3, "BOTTOMLEFT", 0, -spacing);
		else
			tooltip.Instruction:ClearAllPoints();
			tooltip.Instruction:SetPoint("TOPLEFT", tooltip.Text2, "BOTTOMLEFT", 0, -spacing);
		end

		API.UpdateTextureSliceScale(tooltip.BackgroundFrame.Texture);
		if fadeIn then
			tooltip.t = 0;
			tooltip:SetAlpha(0);
			tooltip:SetScript("OnUpdate", SharedFadeIn_OnUpdate);
		end
		tooltip:Show();
		tooltip:SetFrameStrata("TOOLTIP");
		tooltip:SetFixedFrameStrata(true);

		if addon.GetDBBool("LegionRemix_PaperDollTraitDetail") and nextUpgradeInfo and nextUpgradeInfo.nextEntryID then
			local TraitTooltipFrame = RemixAPI.GetTraitTooltipFrame();
			--TraitTooltipFrame:SetTooltipSpell(nextSpellID);
			TraitTooltipFrame:SetTooltipTrait(nextUpgradeInfo.nextEntryID, nextUpgradeInfo.nextRank);
			TraitTooltipFrame:SetOwner(self);
			TraitTooltipFrame:SetParent(tooltip);
			TraitTooltipFrame:ClearAllPoints();
			TraitTooltipFrame:SetPoint("TOPLEFT", tooltip, "TOPRIGHT", 4, 0);
			TraitTooltipFrame:Show();
		else
			RemixAPI.HideTraitTooltipFrame();
		end

		if not isLoaded then
			self.t = 0;
			self:SetScript("OnUpdate", self.OnUpdate);
		end
	end

	local LegionContextMenu = {
		tag = "PlumberPaperDollWidgetLegionRemix",
		objects = {
			{type = "Title", name = L["LegionRemix Widget Title"]},
			{type = "Divider"},

			{type = "Title", name = L["Trait Icon Mode"]},
			{type = "Radio", response = "Refresh",
				IsSelected = function(index)
					return addon.GetDBValue("LegionRemix_TraitSubIconStyle") == index
				end,

				SetSelected = function(index)
					addon.SetDBValue("LegionRemix_TraitSubIconStyle", index, true);
					IconPoolUtil:LoadSettings();
				end,

				radios = {
					L["Trait Icon Mode Hidden"],
					L["Trait Icon Mode Mini"],
					L["Trait Icon Mode Replace"],
				},
			},

			{type = "Divider"},
			{type = "Checkbox", name = L["Auto Learn Traits"], tooltip = L["Auto Learn Traits Tooltip"],
				IsSelected = function()
					return addon.GetDBBool("LegionRemix_AutoUpgrade");
				end,

				ToggleSelected = function()
					return addon.FlipDBBool("LegionRemix_AutoUpgrade");
				end,
			},
		};
	};

	function LegionWidget:ShowContextMenu()
		local menu = API.ShowBlizzardMenu(self, LegionContextMenu);
		menu:ClearAllPoints();
		menu:SetPoint("TOPLEFT", self, "TOPRIGHT", 4, 0);
	end

	function LegionWidget:OnUpdate(elapsed)
		self.t = self.t + elapsed;
		if self.t > 0.2 then
			self.t = 0;
			self:SetScript("OnUpdate", nil);
			if self.Tooltip:IsVisible() then
				self:ShowTooltip();
			end
		end
	end

	local IconPool;     --ItemButton SubIconTexture
	local ValidSlots = {    --Neck, Rings, Trinkets
		2, 11, 12, 13, 14
	};

	function LegionWidget:ShowSlotTraitIcons(state)
		if IconPool then
			IconPool:Release();
		end

		if IconPoolUtil.traitSubIconStyle ~= 2 then return end;

		if state then
			if not IconPool then
				IconPool = IconPoolUtil:CreateSubIconPool();
			end

			for i, slotID in ipairs(ValidSlots) do
				local itemID = GetInventoryItemID("player", slotID);
				if itemID then
					local slotButton = Controller:GetSlotButton(slotID);
					if slotButton then
						local texture = DataProvider:GetItemTraitTexture(itemID);
						if texture then
							local object = IconPool:Acquire();
							object:SetParent(slotButton);
							object:SetPoint("TOPRIGHT", slotButton, "TOPRIGHT", -2, -2);
							object.Icon:SetTexture(texture);
						end
					end
				end
			end
		end
	end

	function LegionWidget:Update()
		if not DataProvider:GetCurrentConfigID() then
			self:Hide();
			return
		else
			self:Show();
		end
		self:ShowSlotTraitIcons(true);
		Controller:HideNarcissusWidgets();
	end

	IconPoolUtil:AddUpdator(function()
		if PaperDollItemsFrame:IsVisible() then
			LegionWidget:ShowSlotTraitIcons(true);
		end
	end);

	IconPoolUtil:AddUpdator(function()
		if not PaperDollItemsFrame:IsVisible() then return end;

		local style = IconPoolUtil.traitSubIconStyle;
		for i, slotID in ipairs(ValidSlots) do
			local itemID = GetInventoryItemID("player", slotID);
			if itemID then
				local slotButton = Controller:GetSlotButton(slotID);
				if slotButton then
					local texture = style == 3 and DataProvider:GetItemTraitTexture(itemID) or GetInventoryItemTexture("player", slotID);
					slotButton.icon:SetTexture(texture);
				end
			end
		end
	end);
end


do  --EquipmentFlyout
	local moduleEnabled = false;
	local FlyoutIconPool = IconPoolUtil:CreateSubIconPool();
	local EQUIPMENTFLYOUT_FIRST_SPECIAL_LOCATION = EQUIPMENTFLYOUT_FIRST_SPECIAL_LOCATION or 0xFFFFFFFD;
	local EquipmentManager_GetItemInfoByLocation = EquipmentManager_GetItemInfoByLocation;

	local function UpdateFlyout()
		FlyoutIconPool:Release();
		if not moduleEnabled then return end;
		local style = IconPoolUtil.traitSubIconStyle;
		if not(style == 2 or style == 3) then return end;

		local flyout = EquipmentFlyoutFrame;
		--if not (flyout and flyout:IsVisible()) then return end;

		local buttons = flyout.buttons;
		local numButtons = flyout.numItemButtons or 0;

		for i = 1, numButtons do
			local location = buttons[i].location;
			if location and location < EQUIPMENTFLYOUT_FIRST_SPECIAL_LOCATION then
				local itemID = EquipmentManager_GetItemInfoByLocation(location);
				if itemID then
					local texture = DataProvider:GetItemTraitTexture(itemID);
					if texture then
						if style == 2 then
							local object = FlyoutIconPool:Acquire();
							object:SetParent(buttons[i]);
							object:SetPoint("TOPRIGHT", buttons[i], "TOPRIGHT", -2, -2);
							object.Icon:SetTexture(texture);
						elseif style == 3 then
							buttons[i].icon:SetTexture(texture);
						end
					end
				end
			end
		end
	end


	IconPoolUtil:AddUpdator(function()
		if EquipmentFlyoutFrame:IsVisible() then
			UpdateFlyout();
		end
	end);


	local isHooked = false;
	local function HookItemButtonFunctions()
		if isHooked then return end;
		isHooked = true;
		hooksecurefunc("EquipmentFlyout_UpdateItems", UpdateFlyout);
		hooksecurefunc("PaperDollItemSlotButton_Update", function(slotButton)
			if not moduleEnabled then return end;
			if IconPoolUtil.traitSubIconStyle ~= 3 then return end;
			local slotID = slotButton:GetID();
			local itemID = slotID and GetInventoryItemID("player", slotID);
			if itemID then
				local texture = DataProvider:GetItemTraitTexture(itemID);
				if texture and slotButton.icon then
					slotButton.icon:SetTexture(texture);
				end
			end
		end);
	end

	addon.CallbackRegistry:Register("LegionRemix.EnableModule", function(state)
		LegionWidget.enabled = state;
		moduleEnabled = state;
		if state then
			Controller:Enable();
			HookItemButtonFunctions();
		else
			Controller:Disable();
		end
	end);
end
