local tablet = AceLibrary("Tablet-2.0")
local deformat = AceLibrary("Deformat-2.0")
local babbleclass = AceLibrary("Babble-Class-2.2")
local crayon = AceLibrary("Crayon-2.0")
local tourist = AceLibrary("Tourist-2.0")
local quixote = AceLibrary("Quixote-1.0")
local dewdrop = AceLibrary("Dewdrop-2.0")

local _G = getfenv(0)

local L = AceLibrary("AceLocale-2.2"):new("QuestsFu")

QuestsFu = AceLibrary("AceAddon-2.0"):new("AceModuleCore-2.0", "AceEvent-2.0", "AceHook-2.1", "AceConsole-2.0", "AceDB-2.0", "FuBarPlugin-2.0")

QuestsFu.version = "r" .. string.sub("$Revision: 31305 $", 12, -3)
QuestsFu.revision = tonumber(("$Revision: 31305 $"):match("%d+"))
QuestsFu.date = string.sub("$Date: 2007-03-29 20:04:06 -0400 (Thu, 29 Mar 2007) $", 8, 17)
QuestsFu.hasIcon = true
QuestsFu.clickableTooltip = true
QuestsFu.cannotHideText = true

function QuestsFu:OnInitialize()
	self:RegisterDB("QuestsFuDB")
	self:RegisterDefaults('profile', {
		showLevelsTablet = true,
		showLevelsZone = true,
		showImpossible = true,
		showArea = true,
		showCurrentAreaOnly = false,
		showClassAnyway = true,
		showCurrentAreaDescriptionOnly = false,
		showDescription = true,
		showCompletedObjectives = false,
		wrapQuests = true,
		showTextOpt = {
			total = true,
			current = true,
			complete = false,
			lastmessage = false,
		},
		color = {
			zone = true,
			level = true,
			title = true,
			objectives = true,
		},
	})
	self:RegisterDefaults('char', {
		hidden = {},
	})
	
	self.options = {
		handler = QuestsFu,
		type = 'group',
		args = {
			text = {
				type = 'group', name = L["Text"], order = 99,
				desc = L["Toolbar text"],
				args = {
					current = {
						type = 'toggle', name = L["Current"],
						desc = L["Show current # of quests"],
						get = function() return self.db.profile.showTextOpt.current end,
						set = function(t) self.db.profile.showTextOpt.current = t; self:UpdateText() end,
					},
					complete = {
						type = 'toggle', name = L["Completed"],
						desc = L["Show # of quests completed"],
						get = function() return self.db.profile.showTextOpt.complete end,
						set = function(t) self.db.profile.showTextOpt.complete = t; self:UpdateText() end,
					},
					total = {
						type = 'toggle', name = L["Total"],
						desc = L["Show total possible quests"],
						get = function() return self.db.profile.showTextOpt.total end,
						set = function(t) self.db.profile.showTextOpt.total = t; self:UpdateText() end,
					},
					lastmessage = {
						type = 'toggle', name = L["Last message"],
						desc = L["Show last quest status message"],
						get = function() return self.db.profile.showTextOpt.lastmessage end,
						set = function(t) self.db.profile.showTextOpt.lastmessage = t; self:UpdateText() end,
					},
				},
			},
			tablet = {
				type = 'group', name = L["FuBar Tablet"], order = 99,
				desc = L["Settings for the main FuBar tablet"],
				args = {
					levels = {
						type = 'group', name = L["Levels"], order = 99,
						desc = L["Show quest levels"],
						args = {
							tablet = {
								type = "toggle", name = L["In QuestsFu"],
								desc = L["Show quest levels in the QuestsFu tooltip"],
								get = function() return self.db.profile.showLevelsTablet end,
								set = function(t) self.db.profile.showLevelsTablet = t; self:UpdateTooltip() end,
							},
							zone = {
								type = "toggle", name = L["Zone"],
								desc = L["Show zone levels in the QuestsFu tooltip"],
								get = function() return self.db.profile.showLevelsZone end,
								set = function(t) self.db.profile.showLevelsZone = t; self:UpdateTooltip() end,
							},
						},
					},
					impossible = {
						type = 'toggle', name = L["Impossible quests"],
						desc = L["Show impossible (red) quests"],
						get = function() return self.db.profile.showImpossible end,
						set = function(t) self.db.profile.showImpossible = t; self:UpdateTooltip() end,
					},
					area = {
						type = 'toggle', name = L["Zone"],
						desc = L["Show quest zones"],
						get = function() return self.db.profile.showArea end,
						set = function(t) self.db.profile.showArea = t; self:UpdateTooltip() end,
					},
					caonly = {
						type = 'toggle', name = L["Current area only"],
						desc = L["Only show quests for the current zone"],
						get = function() return self.db.profile.showCurrentAreaOnly end,
						set = function(t) self.db.profile.showCurrentAreaOnly = t; self:UpdateTooltip() end,
					},
					classanyway = {
						type = 'toggle', name = L["Class quests always"],
						desc = L["Always show class quests"],
						get = function() return self.db.profile.showClassAlways end,
						set = function(t) self.db.profile.showClassAlways = t; self:UpdateTooltip() end,
					},
					description = {
						type = 'toggle', name = L["Details"],
						desc = L["Show quest details"],
						get = function() return self.db.profile.showDescription end,
						set = function(t) self.db.profile.showDescription = t; self:UpdateTooltip() end,
					},
					cadonly = {
						type = 'toggle', name = L["Current area details only"],
						desc = L["Show details for current area quests only"],
						get = function() return self.db.profile.showCurrentAreaDescriptionOnly end,
						set = function(t) self.db.profile.showCurrentAreaDescriptionOnly = t; self:UpdateTooltip() end,
					},
					completed= {
						type = 'toggle', name = L["Completed objectives"],
						desc = L["Show completed objectives"],
						get = function() return self.db.profile.showCompletedObjectives end,
						set = function(t) self.db.profile.showCompletedObjectives = t; self:UpdateTooltip() end,
					},
					color = {
						type = 'group', name = L["Color"], order = 99,
						desc = L["Color settings"],
						args = {
							zone = {
								type = 'toggle', name = L["Zone"],
								desc = L["Color zone names by suggested level"],
								get = function() return self.db.profile.color.zone end,
								set = function(t) self.db.profile.color.zone = t; self:UpdateTooltip() end,
							},
							title = {
								type = 'toggle', name = L["Title"],
								desc = L["Color the quest title by difficulty"],
								get = function() return self.db.profile.color.title end,
								set = function(t) self.db.profile.color.title = t; self:UpdateTooltip() end,
							},
							level = {
								type = 'toggle', name = L["Level"],
								desc = L["Color the quest level by difficulty"],
								get = function() return self.db.profile.color.level end,
								set = function(t) self.db.profile.color.level = t; self:UpdateTooltip() end,
							},
							objectives = {
								type = 'toggle', name = L["Objectives"],
								desc = L["Color objective completion status"],
								get = function() return self.db.profile.color.objectives end,
								set = function(t) self.db.profile.color.objectives = t; self:UpdateTooltip() end,
							},
						},
					},
					wrap = {
						type = 'toggle', name = L["Wrap quest titles"],
						desc = L["Wrap long quest titles on to multiple lines"],
						get = function() return self.db.profile.wrapQuests end,
						set = function(t) self.db.profile.wrapQuests = t; self:UpdateTooltip() end,
					},
				},
			},
		},
	}
	self:RegisterChatCommand({"/questsfu", "/fuq" }, self.options)
	self.OnMenuRequest = self.options --FuBarPlugin
	
	self.lastuimessage = ""
	self.lastquestmessage = ""
	
	self.loadedWatchedQuests = false
end

function QuestsFu:OnEnable()
	if quixote.firstScanDone then
		self:RegisterEvent("Quixote_Update", "Update")
		self:RegisterEvent("PLAYER_LEVEL_UP", "UpdateTooltip") -- Quest difficulty colors can change on level; just redraw the tooltip in case they have it open while levelling.
		self:RegisterEvent("UI_INFO_MESSAGE","OnUIInfoMessage")
		self:RegisterEvent("MINIMAP_ZONE_CHANGED", "Update")
		self:RegisterEvent("ZONE_CHANGED", "Update")
		self:RegisterEvent("ZONE_CHANGED_INDOORS", "Update")
		self:RegisterEvent("ZONE_CHANGED_NEW_AREA", "Update")
	else
		self:RegisterEvent("Quixote_Ready", function() self:OnEnable() end, true)
	end
end

----------------------------------------------------
-- Quest info messages
----------------------------------------------------

function QuestsFu:OnUIInfoMessage()
	self.lastuimessage = arg1
	self:ScheduleEvent(self.ClearUIInfoMessage, 0.5, self)
end

function QuestsFu:ClearUIInfoMessage()
	self.lastuimessage = ""
end

----------------------------------------------------
-- FuBarPlugin OnFooUpdates
----------------------------------------------------

function QuestsFu:OnDataUpdate()
	self.lastquestmessage = self.lastuimessage
end

function QuestsFu:OnTextUpdate()
	local text = ""
	local maxQuests = MAX_QUESTS
	local r,g,b = GameFontNormal:GetTextColor()
	local color = string.format("%02X%02X%02X", r * 255, g * 255, b * 255)
	local numQuests, numQuestsDone = quixote:GetNumQuests()
	if self.db.profile.showTextOpt.complete then
		color = crayon:GetThresholdHexColor(numQuestsDone / numQuests)
		text = text .. crayon:Colorize(color, numQuestsDone)
	end
	if self.db.profile.showTextOpt.complete and (self.db.profile.showTextOpt.current or self.db.profile.showTextOpt.total) then
		text = text .. crayon:Colorize(color, "/")
	end
	if self.db.profile.showTextOpt.current then
		color = crayon:GetThresholdHexColor((maxQuests - numQuests) / maxQuests)
		text = text .. crayon:Colorize(color, numQuests)
	end
	if self.db.profile.showTextOpt.current and self.db.profile.showTextOpt.total then
		text = text .. crayon:Colorize(color, "/")
	end
	if self.db.profile.showTextOpt.total then
		text = text .. crayon:Colorize(color, maxQuests)
	end
	if (self.db.profile.showTextOpt.total or self.db.profile.showTextOpt.complete or self.db.profile.showTextOpt.current) and self.db.profile.showTextOpt.lastmessage and self.lastquestmessage ~= "" then
		text = text .. " m: "
	end
	if self.db.profile.showTextOpt.lastmessage and self.lastquestmessage ~= "" then
		text = text .. self.lastquestmessage
	end
	self:SetText(text)
end

function QuestsFu:OnTooltipUpdate()
	if quixote:GetNumQuests() > 0 then
		if not self.db.profile.showArea then
			cat = tablet:AddCategory('columns', 2, 'child_func', 'QuestClick', 'child_arg1', self)
			for _,questid in quixote:QuestsByLevel() do
				self:AddQuestToCategory(cat, questid, true, self.db.profile.showDescription)
			end
		else
			local currentZone = GetZoneText()
			local zonename
			local cat
			for _, zone in quixote:IterZones() do
				-- If showing area headers and either not showing only the current area's quests or this is the current area.
				if self.db.profile.showArea and ((currentZone == zone) or not self.db.profile.showCurrentAreaOnly or (self.db.profile.showClassAnyway and UnitClass("player") == zone)) then
					local r,g,b = 0.749,1,0.749
					zonename = zone
					if zone == UnitClass("player") then 
						r, g, b = babbleclass:GetColor(zone)
					else
						if self.db.profile.showLevelsZone and tourist:IsZoneOrInstance(zone) then
							local high, low = tourist:GetLevel(zone)
							if high > 0 and low > 0 then
								zonename = string.format("%s (%d-%d)", zone, high, low)
							end
						end
						if self.db.profile.color.zone and tourist:IsZoneOrInstance(zone) then
							r, g, b = tourist:GetLevelColor(zone)
						end
					end
					cat = tablet:AddCategory(
						'id', zone, 'text', zonename,
						'columns', 2,
						'hideBlankLine', true, 'showWithoutChildren', true,
						'checked', true, 'hasCheck', true, 'checkIcon', self.db.char.hidden[zone] and "Interface\\Buttons\\UI-PlusButton-Up" or "Interface\\Buttons\\UI-MinusButton-Up",
						'func', 'ToggleCategory', 'arg1', self, 'arg2', zone,
						'textR', r, 'textG', g, 'textB', b,
						'child_func', 'QuestClick', 'child_arg1', self
					)
					-- If we're not hiding the zone and it's either the current zone or we're not showing only the current zone.
					if (not self.db.char.hidden[zone]) and ((not self.db.profile.showCurrentAreaOnly) or (currentZone == zone) or (self.db.profile.showClassAnyway and UnitClass("player") == zone)) then
						for _,questid in quixote:QuestsByZone(zone) do
							self:AddQuestToCategory(cat, questid, false, self.db.profile.showDescription)
						end
					end
				end
			end
		end
	end
	tablet:SetHint(L["TOOLTIP_HINT"])
end

----------------------------------------------------
-- Tablet utility functions
----------------------------------------------------

function QuestsFu:AddQuestToCategory(cat, questref, appendZone, showObjectives, hideChecks)
	local title, level, tag, suggestedGroup, complete, leaderboard, zone, questid = quixote:GetQuest(questref)
	if not title or not level then return end
	
	--Are we hiding impossible quests, and is this quest impossible?
	local questImpossible = (GetDifficultyColor(level) == QuestDifficultyColor.impossible)
	if not questImpossible or (questImpossible and self.db.profile.showImpossible) then
		local thisQuest, thisQuestColor
		local questLevel, questZone = "",""
		local currentZone = GetZoneText()
		
		local questShared = QuestsFu:MembersOnQuest(questid)
		if questShared == 0 then questShared = "" end
		if appendZone then questZone = string.format(" (%s)", zone) end
		if self.db.profile.showLevelsTablet then questLevel = self:MakeTag(level, tag) end
		thisQuestColor = GetDifficultyColor(level)
		if complete == 1 then complete = L["(done)"]
		elseif complete == -1 then complete = L["(failed)"]
		else complete = nil end
		
		if not self.db.profile.color.title then title = crayon:White(title) end
		if not self.db.profile.color.level then questLevel = crayon:White(questLevel) end
		
		
		cat:AddLine(
			'text', questLevel..title..questZone..((questShared ~= "") and " |c0000ff00<"..questShared..">|r" or ''), 'wrap', self.db.profile.wrapQuests,
			'text2', complete,
			'textR', (thisQuestColor and thisQuestColor.r) or 1, 'textG', (thisQuestColor and thisQuestColor.g) or 1, 'textB', (thisQuestColor and thisQuestColor.b) or 1,
			'text2R', (complete == L["(failed)"]) and 1 or 0, 'text2G', (complete == L["(done)"]) and 1 or 0, 'text2B', 0,
			'func', 'QuestClick', 'arg1', self, 'arg2', questid,
			'indentation', 6,
			'checked', self:IsWatched(questid),
			'hasCheck', (not hideChecks) and self:IsWatched(questid) or false
		)

		-- If we're showing descriptions/objectives and it's either the current zone or we're not showing descriptions only for the current zone.
		if showObjectives and ((currentZone == zone) or not self.db.profile.showCurrentAreaDescriptionOnly) then
			-- If we know of leaderboard objectives:
			if leaderboard > 0 then
				for i=1,leaderboard do
					self:AddLeaderboardToCategory(cat, questid, i)
				end
			-- Otherwise, if the quest is incomplete (or complete and we're showing completed objectives), add the generic objective.
			elseif not (complete and not self.db.profile.showCompletedObjectives) then
				objective = quixote:GetQuestText(questid)
				cat:AddLine(
					'text', objective, 'wrap', true,
					'textR', (thisQuestColor and thisQuestColor.r) or 1, 'textG', (thisQuestColor and thisQuestColor.g) or 1, 'textB', (thisQuestColor and thisQuestColor.b) or 1,
					'size', tablet:GetNormalFontSize()-2,
					'arg2', questid,
					'indentation', 12
				)
			end
		end
	end
end

function QuestsFu:AddLeaderboardToCategory(cat, questid, i, fontSize, indent)
	local title, level = quixote:GetQuest(questid)
	local description, qtype, numGot, numNeeded, done = quixote:GetQuestStatusById(questid, i)
	if not fontSize then fontSize = tablet:GetNormalFontSize()-2 end
	if not indent then indent = 12 end
	if not (done and not self.db.profile.showCompletedObjectives) then
		local r,g,b
		local thisQuestColor = GetDifficultyColor(level)
		if self.db.profile.color.objectives then
			--if numGot == nil then self:Print("The author of QuestsFu would very much like to know if you see this message.  Email the rest of this line to him at kemayo at gmail dot com.", quixote:GetQuestStatusById(questid, i)) end
			r,g,b = crayon:GetThresholdColor((qtype == 'reputation' and quixote:GetReactionLevel(numGot) or numGot) / (qtype == 'reputation' and quixote:GetReactionLevel(numNeeded) or numNeeded))
		end
		local party = ''
		for i=1, GetNumPartyMembers(), 1 do
			local unit = 'party'..i
			local n = quixote:GetPartyQuestStatus(unit, title, description)
			if n and type(n) == 'number' then
				if n == numNeeded then n = 'd' end
				party = string.format("%s|c00%s%s:%s|r ", party, babbleclass:GetHexColor(UnitClass(unit)), string.sub(UnitName(unit), 1, 3), n)
			end
		end
		cat:AddLine(
			'text', '  '..description,
			'text2', party .. (done and L["(done)"] or string.format("%s/%s", numGot, numNeeded)),
			'textR', r or ((thisQuestColor and thisQuestColor.r) or 1), 'textG', g or ((thisQuestColor and thisQuestColor.g) or 1), 'textB', b or ((thisQuestColor and thisQuestColor.b) or 1),
			'text2R', r or ((thisQuestColor and thisQuestColor.r) or 1), 'text2G', g or ((thisQuestColor and thisQuestColor.g) or 1), 'text2B', b or ((thisQuestColor and thisQuestColor.b) or 1),
			'size', fontSize, 'size2', fontSize,
			'arg2', questid,
			'indentation', indent
		)
	end
end

function QuestsFu:ToggleCategory(id, button)
	if self.db.char.hidden[id] then
		self.db.char.hidden[id] = false
	else
		self.db.char.hidden[id] = true
	end
	-- Refresh in place
	self:UpdateTooltip()
end

function QuestsFu:QuestClick(questid)
	if IsAltKeyDown() then
		-- Shift-click toggles quest-watch on this quest.
		self:ToggleQuestWatch(questid)
	elseif IsShiftKeyDown() and IsControlKeyDown() and ChatFrameEditBox:IsVisible() then
		-- Add the quest objectives to the chat editbox, if it's open.
		_,_,_,_,_,leaderboard = quixote:GetQuestById(questid)
		local i = 1
		for i=1,leaderboard do
			local description, qtype, numGot, numNeeded = quixote:GetQuestStatusById(questid, i)
			ChatFrameEditBox:Insert(string.format("{%s %s/%s} ", description, numGot, numNeeded))
		end
	elseif IsShiftKeyDown() and ChatFrameEditBox:IsVisible() then
		-- Add quest title to the chat editbox if it's open.
		local name,level,tag = quixote:GetQuestById(questid)
		ChatFrameEditBox:Insert(self:MakeTag(level, tag)..name)
	elseif IsControlKeyDown() then
		self:ShareQuest(questid)
	else
		self:ShowLog(questid)
	end
end

function QuestsFu:ToggleQuestWatch(questid)
	if IsQuestWatched(questid) then
		RemoveQuestWatch(questid)
	else
		-- Set error if no objectives
		if GetNumQuestLeaderBoards(questid) == 0 then
			UIErrorsFrame:AddMessage(QUEST_WATCH_NO_OBJECTIVES, 1.0, 0.1, 0.1, 1.0, UIERRORS_HOLD_TIME)
			return
		end
		-- Set an error message if trying to show too many quests
		if GetNumQuestWatches() >= MAX_WATCHABLE_QUESTS then
			UIErrorsFrame:AddMessage(format(QUEST_WATCH_TOO_MANY, MAX_WATCHABLE_QUESTS), 1.0, 0.1, 0.1, 1.0, UIERRORS_HOLD_TIME)
			return
		end
		AddQuestWatch(questid)
	end
	QuestWatch_Update()
end

function QuestsFu:OnClick()
	ToggleQuestLog()
end

----------------------------------------------------
-- Utility functions
----------------------------------------------------

function QuestsFu:MakeTag(level, tag)
	return "[" .. level .. tag .. "] "
end

function QuestsFu:MembersOnQuest(questid)
	--Return the number of party members on a quest.  Returns zero if not in a party.
	local n = 0
	local party = GetNumPartyMembers()
	if party > 0 then
		for i = 1,party do
			n = n + (IsUnitOnQuest(questid, "party"..i) and 1 or 0)
		end
	end
	return n
end

function QuestsFu:ShowLog(questid)
	if QuestLogFrame:IsVisible() then
		if self.lastIndex == questid then
			HideUIPanel(QuestLogFrame)
		end
	else
		ShowUIPanel(QuestLogFrame)
	end
	local numEntries = GetNumQuestLogEntries()
	if (numEntries > QUESTS_DISPLAYED) then
		if (questid < numEntries - QUESTS_DISPLAYED) then
			FauxScrollFrame_SetOffset(QuestLogListScrollFrame, questid - 1)
			QuestLogListScrollFrameScrollBar:SetValue((questid - 1) * QUESTLOG_QUEST_HEIGHT)
		else
			FauxScrollFrame_SetOffset(QuestLogListScrollFrame, numEntries - QUESTS_DISPLAYED)
			QuestLogListScrollFrameScrollBar:SetValue((numEntries - QUESTS_DISPLAYED) * QUESTLOG_QUEST_HEIGHT)
		end
	end
	
	SelectQuestLogEntry(questid)
	QuestLog_SetSelection(questid)
	self.lastIndex = questid
end

function QuestsFu:ShareQuest(questid)
	-- Share the quest with nearby party members.
	local wasSelected = GetQuestLogSelection()
	SelectQuestLogEntry(questid)
	if GetQuestLogPushable() and GetNumPartyMembers() > 0 then
		QuestLogPushQuest()
	end
	SelectQuestLogEntry(questid)
end

function QuestsFu:IsWatched(questid)
	if type(questid) == 'string' then
		local title, level, tag, suggestedGroup, complete, nObjectives, zone, id = quixote:GetQuest(self.questid)
		questid = select(8, quixote:GetQuestByName(questid))
	end
	return IsQuestWatched(questid)
end
