local DGV = DugisGuideViewer
if not DGV then return end

local Obj = DGV:RegisterModule("Objectives")
local L = DugisLocals

DGV.Objectives = Obj
Obj.ReferenceFrame = CreateFrame("Frame", "DugisObjectivesReferenceFrame", UIParent)


function Obj:Initialize()
	--/dump DGV_TRACKER_MODULE.usedBlocks["DugisObjectiveBlockTemplate"]["DGV1"]
	--/run ObjectiveTrackerFrame.isUpdating=false
	--/dump DGV_TRACKER_MODULE.firstBlock.currentLine
	function Obj:Load()
		local module = ObjectiveTracker_GetModuleInfoTable("DGV_TRACKER_MODULE", nil, "DugisObjectiveBlockTemplate")
		DGV_TRACKER_MODULE = module
		module:AddBlockOffset("DugisNoGuideBlockTemplate", 0, -6)
		OBJECTIVE_TRACKER_UPDATE_MODULE_DGV = 0x08000000;
		module.updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_DGV
		module:SetHeader(
			CreateFrame("Frame", nil, module.BlocksFrame, "DugisObjectiveHeaderTemplate"),
			L["Guides"], 
			OBJECTIVE_TRACKER_UPDATE_MODULE_DGV)
		local useItem = { 3, -24 }
		module:AddButtonOffsets("DugisObjectiveBlockTemplate", { ["useItem"] = useItem });

		tinsert(ObjectiveTrackerFrame.MODULES, module)
		tinsert(ObjectiveTrackerFrame.MODULES_UI_ORDER, 1, module)

		local SmallFrame = DGV.Modules.SmallFrame
		function module:OnBlockHeaderClick(block, mouseButton)
			SmallFrame:OnClick(block, mouseButton)
		end

		local function SetDescColorStyle(block, baseColorStyle)
			local colorStyle = baseColorStyle
			if block.isHighlighted and colorStyle.reverse then
				colorStyle = colorStyle.reverse;
			end
			if block.Desc.colorStyle ~= colorStyle then
				block.Desc:SetTextColor("P", colorStyle.r, colorStyle.g, colorStyle.b);
				block.Desc.colorStyle = colorStyle;
			end
		end

		local function ShouldShowQuestPOI()
			return DGV.CurrentTitle == DGV.questAutoGuideName and DugisGuideUser.CurrentQuestIndex == 2
		end

		function module:OnBlockHeaderEnter(block)
			DEFAULT_OBJECTIVE_TRACKER_MODULE.OnBlockHeaderEnter(module, block)
			SetDescColorStyle(block, OBJECTIVE_TRACKER_COLOR["Normal"])
			if ShouldShowQuestPOI() then
				BonusObjectiveTracker_ShowRewardsTooltip(block)
			end
			SmallFrame:OnEnter(block)
		end
		
		function module:OnBlockHeaderLeave(block)
			DEFAULT_OBJECTIVE_TRACKER_MODULE.OnBlockHeaderLeave(module, block)
			SetDescColorStyle(block, OBJECTIVE_TRACKER_COLOR["Normal"])
			SmallFrame:OnLeave(block)
		end

		local function GlowOnFinished(self)
			local line = self:GetParent():GetParent()
			if line.used and type(line.block.id) == "number" then --avoid step index id
				QuestObjectiveTracker_FinishGlowAnim(line)
			end
		end

		module.OnFreeTypedLine = QUEST_TRACKER_MODULE.OnFreeTypedLine

		function module:AddObjective(block, objectiveKey, text, lineType, useFullHeight, dashStyle, colorStyle, adjustForNoText, overrideHeight)
			local line = DEFAULT_OBJECTIVE_TRACKER_MODULE.AddObjective(module, block, objectiveKey, text, lineType, useFullHeight, dashStyle, colorStyle, adjustForNoText, overrideHeight)
			if line.Glow then
				line.Glow.Anim:SetScript("OnFinished", GlowOnFinished)
			end
			return line
		end

		function module:FreeUnusedBlocks()
			for blockTemplate, blockTable in pairs(self.usedBlocks) do
				for blockID, block in pairs(blockTable) do
					if not block.used then
						block.id = blockID --fix modified ids
						self:FreeBlock(block);
					end
				end
			end
		end

		function module:OnFreeBlock(block)
			QuestObjectiveReleaseBlockButton_Item(block)
		end

		local function IsTooltipEmbedded()
			return DGV:UserSetting(DGV_EMBEDDEDTOOLTIP)
		end

		local function ShowObjectives()
			return DGV:UserSetting(DGV_OBJECTIVECOUNTER)
		end

		local function MultistepMode()
			return DGV:UserSetting(DGV_MULTISTEPMODE)
		end

		local function ShowHeader()
			return DGV:UserSetting(DGV_SHOW_SMALL_FRAME_HEADER)
		end

		local function ShowProgress()
			return DugisGuideViewer:UserSetting(DGV_DISPLAYGUIDESPROGRESS)
		end

		local function ShowProgressText()
			return DugisGuideViewer:UserSetting(DGV_DISPLAYGUIDESPROGRESSTEXT)
		end

		local function QuestIterator(stepIndex, control)
			if not control then 
				local qid = DGV.qid[stepIndex]
				return qid and 1, qid and QuestCache:Get(qid) 
			end
			local eqids = DGV.eqids[stepIndex]
			if not eqids then return end
			local id = select(control, strsplit(" ", eqids))
			id = id and tonumber(id)
			if not id then return end
			return control+1, QuestCache:Get(id)
		end

		local LINE_TYPE_ANIM = { template = "QuestObjectiveAnimLineTemplate", freeLines = { } };
		--modify QuestObjectiveTracker_DoQuestObjectives to uniquely identify objectives from various quests and return the first line for anchoring
		local function DoQuestObjectives(self, block, questCompleted, questSequenced, existingBlock, useFullHeight)
			local firstLine
			local objectiveCompleting = false;
			local questLogIndex = C_QuestLog.GetLogIndexForQuestID(block.id);
			local numObjectives = GetNumQuestLeaderBoards(questLogIndex);
			local suppressProgressPercentageInObjectiveText = true;
			for objectiveIndex = 1, numObjectives do
				local objectiveKey = string.format("%s:%s", block.id, objectiveIndex)
				local text, objectiveType, finished = GetQuestLogLeaderBoard(objectiveIndex, questLogIndex, suppressProgressPercentageInObjectiveText);
				if ( text ) then
					local line = block.lines[objectiveKey];
					if ( questCompleted ) then
						-- only process existing lines
						if ( line ) then
							line = self:AddObjective(block, objectiveKey, text, LINE_TYPE_ANIM, useFullHeight, OBJECTIVE_DASH_STYLE_HIDE, OBJECTIVE_TRACKER_COLOR["Complete"]);
							-- don't do anything else if a line is either COMPLETING or FADING, the anims' OnFinished will continue the process
							if ( not line.state or line.state == "PRESENT" ) then
								-- this objective wasn't marked finished
								line.block = block;
								line.Check:Show();
								line.Sheen.Anim:Play();
								line.Glow.Anim:Play();
								line.CheckFlash.Anim:Play();
								line.state = "COMPLETING";
							end
						end
					else
						if ( finished ) then
							if ( line ) then
								line = self:AddObjective(block, objectiveKey, text, LINE_TYPE_ANIM, useFullHeight, OBJECTIVE_DASH_STYLE_HIDE, OBJECTIVE_TRACKER_COLOR["Complete"]);
								if ( not line.state or line.state == "PRESENT" ) then
									-- complete this
									line.block = block;
									line.Check:Show();
									line.Sheen.Anim:Play();
									line.Glow.Anim:Play();
									line.CheckFlash.Anim:Play();
									line.state = "COMPLETING";
								end
							else
								-- didn't have a line, just show completed if not sequenced
								if ( not questSequenced ) then
									line = self:AddObjective(block, objectiveKey, text, LINE_TYPE_ANIM, useFullHeight, OBJECTIVE_DASH_STYLE_HIDE, OBJECTIVE_TRACKER_COLOR["Complete"]);
									line.Check:Show();
									line.state = "COMPLETED";
								end
							end
						else
							if ( not questSequenced or not objectiveCompleting ) then
								-- new objectives need to animate in
								if ( questSequenced and existingBlock and not line ) then
									line = self:AddObjective(block, objectiveKey, text, LINE_TYPE_ANIM, useFullHeight);
									line.Sheen.Anim:Play();
									line.Glow.Anim:Play();
									line.state = "ADDING";
									PlaySound(SOUNDKIT.UI_QUEST_ROLLING_FORWARD_01);
									if ( objectiveType == "progressbar" ) then
										self:AddProgressBar(block, line, block.id, finished);
									end
								else
									line = self:AddObjective(block, objectiveKey, text, nil, useFullHeight);
									if ( objectiveType == "progressbar" ) then
										self:AddProgressBar(block, block.currentLine, block.id, finished);
									end
								end
							end
						end
					end
					if ( line ) then
						line.block = block;
						if ( line.state == "COMPLETING" ) then
							objectiveCompleting = true;
						end
					end
					firstLine = firstLine or line
				end
			end
			if ( questCompleted and not objectiveCompleting ) then
				for _, line in pairs(block.lines) do
					if ( line.state == "COMPLETED" ) then
						line.FadeOutAnim:Play();
						line.state = "FADING";
					end
				end
			end
			return firstLine;
		end

		local function UpdateObjectives(block, stepIndex, existingBlock)
			if ShowObjectives() 
				and strmatch(DGV.actions[stepIndex], "[CNK]")
				and not DGV:ReturnTag("V", stepIndex)
				and (DGV:getIcon(DGV.actions[stepIndex], stepIndex) ~= "Interface\\Minimap\\TRACKING\\Profession" or DGV:ReturnTag("AYG", stepIndex)) 
			then
				local firstLine
				for control, quest in QuestIterator, stepIndex do
					if control == 1 then
						QuestObjective_SetupHeader(block, OBJECTIVE_TRACKER_LINE_WIDTH)
						useItem[2] = -block.height --dynamic item button offset
						QuestObjectiveSetupBlockButton_Item(block, C_QuestLog.GetLogIndexForQuestID(quest.questID))
					end

					block.id = quest.questID
					local line = DoQuestObjectives(block.module, block, quest:IsComplete(), IsQuestSequenced(quest.questID), existingBlock, true)
					firstLine = firstLine or line
				end
				if firstLine then
					firstLine:ClearAllPoints()
					if block.module:IsDescriptionShown() then
						firstLine:SetPoint("TOPLEFT", block.Desc, "BOTTOMLEFT", 14, -block.module.lineSpacing)
					else
						firstLine:SetPoint("TOP", block.HeaderText, "BOTTOM", 14, -8)
						firstLine:SetPoint("RIGHT")
					end
				end
			end
		end

		function Obj.GetDescriptionText(guideIndex)
			local descriptionText = DGV:GetQuestStepText(guideIndex) 
			
			--Removed lua error
			if not descriptionText then
				return "", ""
			end
			
			local rawText = descriptionText
			
			if descriptionText and not string.match(descriptionText, "|Hitem") and string.match(descriptionText, "item:%d") then --ReplaceSpecialTags for items if it didn't get converted yet on load. 
				if DGV.NPCJournalFrame then 
					descriptionText = DGV.NPCJournalFrame:ReplaceSpecialTags(descriptionText, true)
				end
			end
			
			return descriptionText, rawText
		end

		local function GetQuestName(stepIndex)
			local qName = DGV.quests1L[stepIndex]
			qName = DGV.NPCJournalFrame:ReplaceSpecialTags(qName, true, stepIndex, true)
	
			qName = qName .. DGV.GetWaitIcon(DGV.CountRemainingPlayers(stepIndex));
			return qName
		end

		function module:IsDescriptionShown()
			return DGV:UserSetting(DGV_EMBEDDEDTOOLTIP)
		end
		
		function module:UpdateSingle(stepIndex)
			local usedBlockId = "DGV"..stepIndex
			local existingBlock = self:GetExistingBlock(usedBlockId);
			local block = self:GetBlock(usedBlockId)
			block.guideIndex = stepIndex
			block.guestStepIndex = stepIndex
			block.Chk:SetChecked(false)
			if stepIndex and DGV.actions[stepIndex] then
				local questId = DGV.qid[stepIndex]
				
				local level = DGV:GetQuestLevel(questId)
				local qName = DGV.quests1L[stepIndex]
				local questpart = DGV:ReturnTag("QIDP", stepIndex)
				
				if (level and level > 0 and strmatch(DGV.actions[stepIndex], "[ACT]") and DGV:UserSetting(DGV_QUESTLEVELON)) or (level and level > 0 and questpart and strmatch(DGV.actions[stepIndex], "[NK]") and DGV:UserSetting(DGV_QUESTLEVELON)) then qName = "["..level.."] "..qName end
	
				qName = GetQuestName(stepIndex)
	
				local icon = DGV:getIcon(DGV.actions[stepIndex], stepIndex)
				if icon then
					block.Icon:SetNormalTexture(icon)
				end
				self:SetBlockHeader(block, qName);
				
				if DGV:ReturnTag("POI", stepIndex) and questId then 
					local _, posX, posY, objective = QuestPOIGetIconInfo(questId)
					if posX then 
						havePOIwaypoint = true
					end
				end
				
				if (DGV:HasCoord(stepIndex) or havePOIwaypoint) and DGV:UserSetting(DGV_SHOW_EXTRA_WAYPOINT_ICON) then 
					block.Waypoint:Enable()
					block.Waypoint:Show()
					block.Waypoint:SetNormalTexture(
						DGV.Modules.DugisArrow:GetFirstWaypointGuideIndex()==stepIndex and
						"Interface\\AddOns\\DugisGuideViewerZ\\Artwork\\waypoint_selected.tga" or
						"Interface\\AddOns\\DugisGuideViewerZ\\Artwork\\waypoint.tga")
					block.HeaderText:SetPoint("TOPLEFT", 28, 0)
					block.Chk:SetPoint("RIGHT", block.Icon, "LEFT", -14, 0)				
				else 
					block.Waypoint:Disable() 
					block.Waypoint:Hide()
					block.HeaderText:SetPoint("TOPLEFT", 14, 0)
					block.Chk:SetPoint("RIGHT", block.Icon, "LEFT", 0, 0)
				end
				if (stepIndex == DugisGuideUser.CurrentQuestIndex) and DGV:HasModel(stepIndex) then 
					block.ModelButton:Show() 
				else 
					block.ModelButton:Hide() 
				end
				if DGV:ReturnTag("NT", stepIndex)  then 
					block.Chk:Disable()
					block.Chk:Hide()
				else 
					block.Chk:Enable()
					block.Chk:Show()
				end

				if self:IsDescriptionShown() then
					block.Desc:Show()
					local text = Obj.GetDescriptionText(block.guideIndex)
					block.Desc.text = text
					block.Desc:SetText(block.Desc.text)
					block.Desc:SetWidth(block.Desc:GetWidth())
					local descHeight = block.Desc:GetContentHeight() + self.lineSpacing
					block.Desc:SetHeight(descHeight)
					block.height = block.height + descHeight
				else
					block.Desc:Hide()
				end
				--block.currentLine = block.Desc -- anchor first objective line
				UpdateObjectives(block, stepIndex, existingBlock)	
			end
			block.height = block.height + 8 + self.lineSpacing
			block:SetHeight(block.height)

			if ObjectiveTracker_AddBlock(block) then
				block:Show();
				self:FreeUnusedLines(block);
			else
				block.used = false;
			end
			return block
		end

		function module:UpdateNoGuide()
			local block = self:GetBlock("DGV_NO_GUIDE", nil, "DugisNoGuideBlockTemplate")
			block.HeaderText:SetText(L["No Guide Loaded"])
			block.Desc:SetText(L["Right Click Here To Select One"])
			local descHeight = block.Desc:GetContentHeight() + self.lineSpacing
			block.Desc:SetHeight(descHeight)
			block.height = block.height + descHeight + 8
			block:SetHeight(block.height)
			if ObjectiveTracker_AddBlock(block) then
				block:Show();
				self:FreeUnusedLines(block);
			else
				block.used = false;
			end
		end

		function module:Update()
			self:BeginLayout()
			self.Header.ProgressBar:Hide()
			local setting = DGV:UserSetting(DGV_ANCHOREDSMALLFRAME)
			local integrated = setting and self == DGV_TRACKER_MODULE
			local floating = not setting and self == DGV_SMALL_TRACKER_MODULE
			if integrated or floating  then
				if DGV:isValidGuide(CurrentTitle) then
					if ShowProgress() then
						local bar = self.Header.ProgressBar
							local left = ObjectiveTrackerFrame.isOnLeftSideOfScreen
							bar:SetPoint("RIGHT", 
								left and self.Header or self.Header.MinimizeButton, 
								left and "RIGHT" or "LEFT", 
								-14, 0)
						local percentComplete = DGV.Modules.Guides.percentComplete
						if percentComplete then
							bar:SetValue(percentComplete)
							bar:Show()
							if ShowProgressText() then
								bar.text:SetText(string.format("%.0f%%",percentComplete))
								bar.text:Show()
							else
								bar.text:Hide()
							end
						end
					else
						self.Header.ProgressBar:Hide()
					end
					local currentQuestIndex = DugisGuideUser.CurrentQuestIndex
					if currentQuestIndex then
						if MultistepMode() and not DGV:ReturnTag("NT", currentQuestIndex) then
							local maxstep = 0
							local total = math.ceil(DugisGuideViewer:GetDB(DGV_SMALLFRAME_STEPS) - 0.5) or 6
							
							if DGV:ReturnTag("SID", currentQuestIndex) or DGV:ReturnTag("SID", currentQuestIndex + 1 ) then 
								total = 2
							end
							
							for guideIndex in DGV.IterateRelevantSteps do				
								maxstep = maxstep + 1
								if maxstep <= total then 
									self:UpdateSingle(guideIndex)
								end
								if maxstep == 2 and not DGV:ReturnTag("AYG", guideIndex) then 
									DugisGuideUser.NextQuestIndex = guideIndex
								elseif DGV:ReturnTag("AYG", currentQuestIndex + 1) then 
									DugisGuideUser.NextQuestIndex = currentQuestIndex
								end
							end
						elseif DGV:ReturnTag("AYG", currentQuestIndex) and not DGV:ReturnTag("NT", currentQuestIndex) then -- AYG to make As you go step stick. 
							local maxstep = 0
							for guideIndex in DGV.IterateRelevantSteps do				
								maxstep = maxstep + 1
								if maxstep <= 2 then 
									self:UpdateSingle(guideIndex)
								end
								if maxstep == 2 and not DGV:ReturnTag("AYG", guideIndex) then 
									DugisGuideUser.NextQuestIndex = guideIndex
								elseif DGV:ReturnTag("AYG", currentQuestIndex + 1) then
									DugisGuideUser.NextQuestIndex = currentQuestIndex
								end
							end
						else
							self:UpdateSingle(currentQuestIndex)
						end
					end
				else
					self:UpdateNoGuide()
				end

				if not ShowHeader() then
					self.fromHeaderOffsetY = 4
					self.Header:Hide()
				else
					self.fromHeaderOffsetY = -10
				end

				if not InCombatLockdown() then
				Obj.ReferenceFrame:SetAllPoints(integrated and ObjectiveFrameDugiBkg or floating and DugisSmallFrameContainer)
				end
			end

			self:EndLayout();
		end

		local function GetQuestPOI(questID, type)
			for index, buton in pairs(ObjectiveTrackerFrame.BlocksFrame.poiTable[type]) do
				if buton.questID == questID then
					return buton
				end
			end
		end

		function module:UpdatePOIs(numPOINumeric)
			local stepIndex = DugisGuideUser.CurrentQuestIndex
			local block = stepIndex and self:GetExistingBlock("DGV"..stepIndex)
			if block then
				local world = block.WorldQuestPOI
				block.module.ShowWorldQuests = false
				if not block.poiTable then
					QuestPOI_Initialize(block)
				end
				QuestPOI_ResetUsage(block)
				world:Hide()
				if ShouldShowQuestPOI() then
					local questId = DGV.qid[stepIndex]
					if questId then
						block.id = questId
						block.Icon:ClearNormalTexture()
						C_TaskQuest.RequestPreloadRewardData(questId)
						if QuestUtils_IsQuestWorldQuest(questId) then
							QuestUtil.SetupWorldQuestButton(world, 
								C_QuestLog.GetQuestTagInfo(questId), 
								true, 
								questId == C_SuperTrack.GetSuperTrackedQuestID())
							world.questID = questId
							block.module.ShowWorldQuests = true
							world:Show()
						else
							local mapBtn = GetQuestPOI(questId, "numeric")
							local poiButton = mapBtn and QuestPOI_GetButton(block, questId, "numeric", mapBtn.index) or
								GetQuestPOI(questId, "completed") and QuestPOI_GetButton(block, questId, "completed")
							if poiButton then
								poiButton:SetAllPoints(world)
								QuestPOI_SelectButton(poiButton)
							else
								QuestPOI_ClearSelection(block)
							end
						end
					end
				end
				QuestPOI_HideUnusedButtons(block)
			end
		
			return numPOINumeric
		end

		--ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_DGV)

		local function DescriptionHyperlinkOnEnter(desc, linkData, link)
			if desc and desc.text and link and not link:find("|cffdddddd", 1, true) then
				local linkStart, linkEnd = desc.text:find(link, 1, true)
				if linkStart then
					local linkPrefix = (linkStart > 1 and desc.text:sub(1, linkStart - 1)) or ""
					local linkSuffix = (linkEnd < desc.text:len() and desc.text:sub(linkEnd + 1)) or ""
					local replacement = link:gsub("|c........", ""):gsub("|r", "") --strip color tags
						:gsub("(%[[^%]]*%])", "|cffdddddd%1|r") --new color tags
					desc:SetText(string.format("%s%s%s", linkPrefix, replacement, linkSuffix))
				end
			end
		end

		function DugiIcon_OnEnter(self)
			DugisGuideViewer:Button_OnEnter(self)
		end

		function DugiIcon_OnLeave()
			GameTooltip:Hide()
		end

		function DugiIcon_OnMouseUp(self)
			if DugisGuideViewer:UserSetting(DGV_SHOW_EXTRA_WAYPOINT_ICON) then return end
			local texture = self:GetNormalTexture()
			if texture then
				texture:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
				texture:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 0, 0)
			end
		end

		function DugiIcon_OnMouseDown(self)
			if DugisGuideViewer:UserSetting(DGV_SHOW_EXTRA_WAYPOINT_ICON) then return end
			local texture = self:GetNormalTexture()
			if texture then
				texture:SetPoint("TOPLEFT", self, "TOPLEFT", -1, -2)
				texture:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", -1, -2)
			end
		end

		function DugiIcon_OnClick(self)
			if DugisGuideViewer:UserSetting(DGV_SHOW_EXTRA_WAYPOINT_ICON) then return end
			DugisGuideViewer:WayPoint_OnClick(self:GetParent().Waypoint)
		end

		function DugiDesc_OnHyperlinkClick(...)
			DGV.NPCJournalFrame.OnHyperlinkClick(...)
		end
		
		function DugiDesc_OnHyperlinkEnter(self, linkData, link, button)
			DGV.NPCJournalFrame.OnHyperlinkEnter(self, linkData, link, button, self)
			DGV.QueueInvocation(DescriptionHyperlinkOnEnter, self, linkData, link)
		end 
		
		function DugiDesc_OnHyperlinkLeave(self, ...)
			DGV.NPCJournalFrame.OnHyperlinkLeave(self, ...)
			DGV.QueueInvocation(self.SetText, self, self.text)
		end

		function DugiDesc_OnMouseUp(self, mouseButton, ...)
			SmallFrame:OnClick(self:GetParent(), mouseButton)
		end
	end

	function Obj:Unload()
		tremove(ObjectiveTrackerFrame.MODULES, #ObjectiveTrackerFrame.MODULES)
		tremove(ObjectiveTrackerFrame.MODULES_UI_ORDER, 1)
	end
end