-- GetItemTransmogrifyInfo(item link/item id/item name)
--   returns: canBeChanged, noChangeReason, canBeSource, noSourceReason
-- CanTransmogrifyItemWithItem(target item link/target item id/target item name, source item link/source item id/source item name)
--   returns: canBeTransmogrified, failReason

UIPanelWindows["TransmogrifyFrame"] =	{ area = "left", pushable = 0 };

local FLYOUT_BUTTON_HIDE_DELAY_TIME = 0.1;

local BUTTONS = { };

function TransmogrifyFrame_Show()
	ShowUIPanel(TransmogrifyFrame);
	if ( not TransmogrifyFrame:IsShown() ) then
		CloseTransmogrifyFrame();
	end
end

function TransmogrifyFrame_Hide()
	HideUIPanel(TransmogrifyFrame);
end

function TransmogrifyFrame_OnLoad(self)
	TransmogrifyArtFrameTitleText:SetText(TRANSMOGRIFY);
	TransmogrifyArtFrameTitleBg:SetDrawLayer("BACKGROUND", -1);
	TransmogrifyArtFrameTopTileStreaks:Hide();
	TransmogrifyArtFrameBg:Hide();
	SetPortraitToTexture(TransmogrifyArtFramePortrait, "Interface\\Icons\\INV_Arcane_Orb");

	RaiseFrameLevel(TransmogrifyArtFrame);
	RaiseFrameLevelByTwo(TransmogrifyFrameButtonFrame);
	TransmogrifyArtFrameCloseButton:SetScript("OnClick", function() HideUIPanel(TransmogrifyFrame); end);
	
	self:RegisterEvent("TRANSMOGRIFY_UPDATE");
	self:RegisterEvent("TRANSMOGRIFY_SUCCESS");
	self:RegisterEvent("TRANSMOGRIFY_BIND_CONFIRM");

	-- flyout settings
	self.flyoutSettings = {
		onClickFunc = TransmogrifyItemFlyoutButton_OnClick,
		getItemsFunc =  TransmogrifyItemFlyout_GetItems,
		hasPopouts = false,
		parent = TransmogrifyFrame,
		anchorX = 0,
		anchorY = -3,
		verticalAnchorX = 0,
		verticalAnchorY = 0,
		keepShownOnClick = true,
	};
end

function TransmogrifyFrame_OnEvent(self, event, ...)
	if ( event == "TRANSMOGRIFY_UPDATE" ) then
		local slot = ...;
		-- play sound?
		local button = BUTTONS[slot];
		if ( button ) then
			local isTransmogrified, canTransmogrify, cannotTransmogrifyReason, hasPending, hasUndo = GetTransmogrifySlotInfo(button.id);
			if ( hasUndo ) then
				PlaySound("UI_Transmogrify_Undo");
			elseif ( not hasPending ) then
				if ( button.hadUndo ) then
					PlaySound("UI_Transmogrify_Redo");
					button.hadUndo = nil;
				end
			end
		end
		if ( TransmogrifyConfirmationPopup:IsShown() and TransmogrifyConfirmationPopup.slot == slot ) then
			TransmogrifyConfirmationPopup.slot = nil;		-- to keep popup from clearing slot on hide
			StaticPopupSpecial_Hide(TransmogrifyConfirmationPopup);
		end
		TransmogrifyFrame_Update(self);
	elseif ( event == "BAG_UPDATE" ) then
		ValidateTransmogrifications();
	elseif ( event == "PLAYER_EQUIPMENT_CHANGED" ) then
		ValidateTransmogrifications();
		local slot, hasItem = ...;
		if ( slot == INVSLOT_TABARD or slot == INVSLOT_BODY ) then
			if ( hasItem ) then
				local itemID, itemAppearanceModID = GetInventoryItemID("player", slot);
				TransmogrifyModelFrame:TryOn(itemID, nil, itemAppearanceModID);
			else
				TransmogrifyModelFrame:UndressSlot(slot);
			end
		end
	elseif ( event == "TRANSMOGRIFY_CLOSE" ) then
		self:Hide();
	elseif ( event == "TRANSMOGRIFY_SUCCESS" ) then
		local slot = ...;
		local button = BUTTONS[slot];
		if ( button ) then
			TransmogrifyFrame_AnimateSlotButton(button);
			TransmogrifyFrame_UpdateSlotButton(button);
			TransmogrifyFrame_UpdateApplyButton();
		end
	elseif ( event == "TRANSMOGRIFY_BIND_CONFIRM" ) then
		TransmogrifyConfirmationPopup_Show(...);
	elseif ( event == "UNIT_MODEL_CHANGED" ) then
		local unit = ...;
		if ( unit == "player" ) then
			local hasAlternateForm, inAlternateForm = HasAlternateForm();
			if ( self.alternateForm ~= inAlternateForm ) then
				self.alternateForm = inAlternateForm;
				TransmogrifyModelFrame:SetUnit("player");
				TransmogrifyFrame_Update(self);
			end
		end
	end
end

function TransmogrifyFrame_OnShow(self)
	PlaySound("UI_EtherealWindow_Open");
	self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED");
	self:RegisterEvent("BAG_UPDATE");
	local hasAlternateForm, inAlternateForm = HasAlternateForm();
	if ( hasAlternateForm ) then
		self:RegisterEvent("UNIT_MODEL_CHANGED");
		self.alternateForm = inAlternateForm;
	end
	TransmogrifyModelFrame:SetUnit("player");
	Model_Reset(TransmogrifyModelFrame);
	self.headSlot.displayHelm = ShowingHelm();
	self.backSlot.displayCloak = ShowingCloak();
	TransmogrifyFrame_Update(self);
end

function TransmogrifyFrame_OnHide(self)
	PlaySound("UI_EtherealWindow_Close");
	StaticPopupSpecial_Hide(TransmogrifyConfirmationPopup);
	for _, button in pairs(BUTTONS) do
		button.popoutButton:Hide();
	end
	self:UnregisterEvent("PLAYER_EQUIPMENT_CHANGED");
	self:UnregisterEvent("BAG_UPDATE");
	self:UnregisterEvent("UNIT_MODEL_CHANGED");
	CloseTransmogrifyFrame();
end

function TransmogrifyFrame_GetAnimationFrame()
	local name, frame;
	local i = 1;
	while true do
		name = "TransmogrifyAnimation"..i;
		frame = _G[name];
		if ( frame ) then
			if ( not frame:IsShown() ) then
				return frame;
			end
		else
			frame = CreateFrame("Frame", name, TransmogrifyFrame, "TransmogrifyAnimationFrameTemplate");
			return frame;
		end
		i = i + 1;
		assert(i < 20);
	end
end

function TransmogrifyFrame_GetPendingFrame()
	local name, frame;
	local i = 1;
	while true do
		name = "TransmogrifyPending"..i;
		frame = _G[name];
		if ( frame ) then
			if ( not frame:IsShown() ) then
				return frame;
			end
		else
			frame = CreateFrame("Frame", name, TransmogrifyFrame, "TransmogrifyPendingFrameTemplate");
			return frame;
		end
		i = i + 1;
		assert(i < 20);
	end
end

function TransmogrifyFrame_AnimateSlotButton(button)
	-- don't do anything if this button already has an animation frame;
	if ( button.animationFrame ) then
		return;
	end
	local animationFrame = TransmogrifyFrame_GetAnimationFrame();
	animationFrame:SetParent(button);
	animationFrame:SetPoint("CENTER");
	button.animationFrame = animationFrame;
	local isTransmogrified = GetTransmogrifySlotInfo(button.id);
	if ( isTransmogrified ) then
		animationFrame.transition:Show();
	else
		animationFrame.transition:Hide();
	end
	animationFrame:Show();
	animationFrame.anim:Play();
end

function TransmogrifySlotButton_OnLoad(self)
	self:RegisterForClicks("LeftButtonUp", "RightButtonUp");
	self:RegisterForDrag("LeftButton");
	local slotName = strsub(self:GetName(), 18);
	local id, textureName = GetInventorySlotInfo(slotName);
	self.id = id;
	self.defaultTexture = textureName;
	self.icon:SetTexture(textureName);
	self.verticalFlyout = VERTICAL_FLYOUTS[id];
	BUTTONS[id] = self;
	RaiseFrameLevelByTwo(self);

	local popoutButton = self.popoutButton;
	if ( popoutButton ) then
		if ( self.verticalFlyout ) then
			popoutButton:SetHeight(16);
			popoutButton:SetWidth(38);
			
			popoutButton:GetNormalTexture():SetTexCoord(0.15625, 0.84375, 0.5, 0);
			popoutButton:GetHighlightTexture():SetTexCoord(0.15625, 0.84375, 1, 0.5);
			popoutButton:ClearAllPoints();
			popoutButton:SetPoint("TOP", self, "BOTTOM", 0, 0);
		else
			popoutButton:SetHeight(38);
			popoutButton:SetWidth(16);
			
			popoutButton:GetNormalTexture():SetTexCoord(0.15625, 0.5, 0.84375, 0.5, 0.15625, 0, 0.84375, 0);
			popoutButton:GetHighlightTexture():SetTexCoord(0.15625, 1, 0.84375, 1, 0.15625, 0.5, 0.84375, 0.5);
			popoutButton:ClearAllPoints();
			popoutButton:SetPoint("LEFT", self, "RIGHT", 0, 0);
		end
	end
end

function TransmogrifySlotButton_OnEvent(self, event, ...)
	if ( event == "MODIFIER_STATE_CHANGED" ) then
		if ( IsModifiedClick("SHOWITEMFLYOUT") and self:IsMouseOver() ) then
			TransmogrifySlotButton_OnEnter(self);
		end
	end
end

function TransmogrifySlotButton_OnClick(self, button)
	local isTransmogrified, canTransmogrify, cannotTransmogrifyReason, hasPending, hasUndo = GetTransmogrifySlotInfo(self.id);
	-- save for sound to play on TRANSMOGRIFY_UPDATE event
	self.hadUndo = hasUndo;
	if ( button == "RightButton" ) then
		ClearTransmogrifySlot(self.id);
	else
		ClickTransmogrifySlot(self.id);
	end
	self.undoIcon:Hide();
	TransmogrifySlotButton_OnEnter(self);
end

function TransmogrifySlotButton_OnEnter(self)
	local isTransmogrified, canTransmogrify, cannotTransmogrifyReason, hasPending, hasUndo = GetTransmogrifySlotInfo(self.id);
	local cursorItem = GetCursorInfo();
	if ( cursorItem ~= "item" and isTransmogrified and not ( hasPending or hasUndo ) ) then
		self.undoIcon:Show();
	end

	self:RegisterEvent("MODIFIER_STATE_CHANGED");
	EquipmentFlyout_UpdateFlyout(self);
	if ( not EquipmentFlyout_SetTooltipAnchor(self) ) then
		GameTooltip:SetOwner(self, "ANCHOR_RIGHT", 14, 0);
	end
	if ( hasPending or hasUndo ) then
		GameTooltip:SetTransmogrifyItem(self.id);
	elseif ( not canTransmogrify ) then
		local slotName = _G[strupper(strsub(self:GetName(), 18))];
		GameTooltip:SetText(slotName);
		local errorMsg = _G["TRANSMOGRIFY_INVALID_REASON"..cannotTransmogrifyReason];
		if ( errorMsg ) then
			GameTooltip:AddLine(errorMsg, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, true);
		end
		GameTooltip:Show();
	else
		local hasItem = GameTooltip:SetInventoryItem("player", self.id);
	end
	if ( canTransmogrify ) then
		self.popoutButton:Show();
		self.popoutButton.persistTime = nil;
	end
	TransmogrifyModelFrame.controlFrame:Show();
end

function TransmogrifySlotButton_OnLeave(self)
	self:UnregisterEvent("MODIFIER_STATE_CHANGED");
	TransmogrifyModelFrame.controlFrame:Hide();
	self.undoIcon:Hide();
	self.popoutButton.persistTime = 0;
	GameTooltip:Hide();
end

function TransmogrifyFrame_Update(self)

	for _, button in pairs(BUTTONS) do
		TransmogrifyFrame_UpdateSlotButton(button);
	end
	TransmogrifyFrame_UpdateApplyButton();
end

function TransmogrifyFrame_UpdateApplyButton()
	local cost, numChanges = GetTransmogrifyCost();
	local canApply;
	if ( cost > GetMoney() ) then
		SetMoneyFrameColor("TransmogrifyMoneyFrame", "red");
	else
		SetMoneyFrameColor("TransmogrifyMoneyFrame");
		if (numChanges > 0 ) then
			canApply = true;
		end
	end
	if ( TransmogrifyConfirmationPopup:IsShown() ) then
		canApply = false;
	end
	MoneyFrame_Update("TransmogrifyMoneyFrame", cost);
	if ( canApply ) then
		TransmogrifyApplyButton:Enable();
	else
		TransmogrifyApplyButton:Disable();
	end
end

function TransmogrifyFrame_UpdateSlotButton(button)
	local isTransmogrified, canTransmogrify, cannotTransmogrifyReason, hasPending, hasUndo, visibleItemID, textureName, visibleItemAppearanceModID = GetTransmogrifySlotInfo(button.id);
	local hasChange = hasPending or hasUndo;

	if ( canTransmogrify ) then
		button.icon:SetTexture(textureName);
		button.noItem:Hide();
	else
		button.icon:SetTexture(button.defaultTexture);
		button.noItem:Show();
	end

	-- desaturate icon if it's not transmogrified
	if ( isTransmogrified or hasPending ) then
		button.icon:SetDesaturated(false);
	else
		button.icon:SetDesaturated(true);
	end

	-- show altered texture if the item is transmogrified and doesn't have a pending transmogrification or is animating
	if ( isTransmogrified and not hasChange and not button.animationFrame ) then
		button.altTexture:Show();
	else
		button.altTexture:Hide();
	end

	-- show ants frame is the item has a pending transmogrification and is not animating
	if ( hasChange and not button.animationFrame ) then
		local pendingFrame = button.pendingFrame;
		if ( not pendingFrame ) then
			pendingFrame = TransmogrifyFrame_GetPendingFrame();
			pendingFrame:SetParent(button);
			pendingFrame:SetPoint("CENTER");
			button.pendingFrame = pendingFrame;
		end
		pendingFrame:Show();
		if ( hasUndo ) then
			pendingFrame.undo:Show();
		else
			pendingFrame.undo:Hide();
		end
	elseif ( button.pendingFrame ) then
		button.pendingFrame:Hide();
		button.pendingFrame = nil;
	end
	

	local showModel = true;
	if (button.id == INVSLOT_HEAD and not button.displayHelm) then
		if ( hasChange ) then
			button.displayHelm = true;
		else
			showModel = false;
		end
	end
	if (button.id == INVSLOT_BACK and not button.displayCloak) then
		if ( hasChange ) then
			button.displayCloak = true;
		else
			showModel = false;
		end
	end
	if ( showModel ) then
		if ( visibleItemID and visibleItemID > 0 ) then
			local slot;
			if ( button.id == INVSLOT_MAINHAND ) then
				slot = "mainhand";
			elseif ( button.id == INVSLOT_OFFHAND ) then
				slot = "offhand";
			end
			TransmogrifyModelFrame:TryOn(visibleItemID, slot, visibleItemAppearanceModID);
		else
			if ( button.id == INVSLOT_RANGED ) then
				-- clear both hands
				TransmogrifyModelFrame:UndressSlot(INVSLOT_MAINHAND);
				TransmogrifyModelFrame:UndressSlot(INVSLOT_OFFHAND);
			else
				TransmogrifyModelFrame:UndressSlot(button.id);
			end
		end
	end
end

function TransmogrifyItemFlyoutButton_OnClick(self)
	if ( self.location ) then
		local player, bank, bags, voidStorage, slot, bag, tab, voidSlot = EquipmentManager_UnpackLocation(self.location);
		if ( voidStorage ) then
			UseVoidItemForTransmogrify(tab, voidSlot, EquipmentFlyoutFrame.button.id);
		elseif ( bag ) then
			UseItemForTransmogrify(bag, slot, EquipmentFlyoutFrame.button.id);
		else
			UseItemForTransmogrify(nil, slot, EquipmentFlyoutFrame.button.id);
		end
	end
end

function TransmogrifyItemFlyout_GetItems(slot, itemTable)
	GetInventoryItemsForSlot(slot, itemTable, "transmogrify");
end

function TransmogrifyConfirmationPopup_Show(slot, sourceItemLink, destinationItemLink)
	local popup = TransmogrifyConfirmationPopup;
	local baseHeight;
	if ( sourceItemLink and destinationItemLink ) then
		TransmogrifyConfirmationPopup_SetItem(popup.ItemFrame1, sourceItemLink);
		TransmogrifyConfirmationPopup_SetItem(popup.ItemFrame2, destinationItemLink);
		popup.Text:SetText(TRANSMOGRIFY_BIND_CONFIRMATION_BOTH.."\n"..CONFIRM_CONTINUE);
		baseHeight = 160;
	else
		popup.ItemFrame2:Hide();
		if ( sourceItemLink ) then
			TransmogrifyConfirmationPopup_SetItem(popup.ItemFrame1, sourceItemLink);
			popup.Text:SetText(TRANSMOGRIFY_BIND_CONFIRMATION_SOURCE.."\n"..CONFIRM_CONTINUE);
		else
			TransmogrifyConfirmationPopup_SetItem(popup.ItemFrame1, destinationItemLink);
			popup.Text:SetText(TRANSMOGRIFY_BIND_CONFIRMATION_DESTINATION.."\n"..CONFIRM_CONTINUE);
		end
		baseHeight = 115;
	end
	popup:SetHeight(baseHeight + popup.Text:GetHeight());
	StaticPopupSpecial_Show(popup);
	popup.slot = slot;
	TransmogrifyApplyButton:Disable();
end

function TransmogrifyConfirmationPopup_SetItem(itemFrame, itemLink)
	local itemName, _, itemQuality, _, _, _, _, _, _, texture = GetItemInfo(itemLink);
	local r, g, b = GetItemQualityColor(itemQuality or 1);
	itemFrame.Text:SetText(itemName);
	itemFrame.Text:SetTextColor(r, g, b);
	itemFrame.icon:SetTexture(texture);
	itemFrame:Show();
	itemFrame.link = itemLink;
end

function TransmogrifyFrame_CloseFlyout()
	local flyout = EquipmentFlyoutFrame;
	if ( flyout:IsShown() ) then
		local flyoutSettings = flyout.button:GetParent().flyoutSettings;
		if ( flyoutSettings and flyoutSettings.parent == TransmogrifyFrame ) then
			EquipmentFlyoutFrame:Hide();
		end
	end
end

function TransmogrifySlotFlyoutButton_OnUpdate(self, elapsed)
	if ( self.persistTime and not self.flyoutLocked ) then
		if ( not EquipmentFlyoutFrame:IsShown() or EquipmentFlyoutFrame.button ~= self:GetParent() ) then
			self.persistTime = self.persistTime + elapsed;
			if ( self.persistTime >= FLYOUT_BUTTON_HIDE_DELAY_TIME ) then
				self:Hide();
			end
		end
	end
end