--[[	*** DataStore_Quests ***
Written by : Thaoky, EU-Marécages de Zangar
July 8th, 2009
--]]
if not DataStore then return end

local addonName = "DataStore_Quests"

_G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0")

local addon = _G[addonName]

local THIS_ACCOUNT = "Default"

local AddonDB_Defaults = {
	global = {
		Characters = {
			['*'] = {				-- ["Account.Realm.Name"] 
				lastUpdate = nil,
				Quests = {},
				QuestLinks = {},
				Rewards = {},
			}
		}
	}
}

-- ** Mixins **
local function _GetQuestLogSize(character)
	return #character.Quests
end

local function _GetQuestLogInfo(character, index)
	local quest = character.Quests[index]
	local link = character.QuestLinks[index]
	local isHeader, questTag, groupSize, money, isComplete = strsplit("|", quest)
	
	if isHeader == "0" then
		return true, questTag	-- questTag contains the title in a header line
	end
	
	isComplete = tonumber(isComplete)
	return nil, link, questTag, tonumber(groupSize), tonumber(money), tonumber(isComplete)
end

local function _GetQuestLogNumRewards(character, index)
	local reward = character.Rewards[index]
	if reward then
		return select(2, gsub(reward, ",", ",")) + 1		-- returns the number of rewards (=count of ^ +1)
	end
	return 0
end

local function _GetQuestLogRewardInfo(character, index, rewardIndex)
	local reward = character.Rewards[index]
	if not reward then return end
	
	local i = 1
	for v in reward:gmatch("([^,]+)") do
		if rewardIndex == i then
			local rewardType, id, numItems, isUsable = strsplit("|", v)
			
			numItems = tonumber(numItems) or 0
			isUsable = (isUsable and isUsable == 1) and true or nil
						
			return rewardType, tonumber(id), numItems, isUsable
		end	
		i = i + 1
	end
end
	
local function _GetQuestInfo(link)
	assert(type(link) == "string")
	
	local questID, questLevel = link:match("quest:(%d+):(-?%d+)")
	local questName = link:match("%[(.+)%]")
	
	return questName, tonumber(questID), tonumber(questLevel)
end

local PublicMethods = {
	GetQuestLogSize = _GetQuestLogSize,
	GetQuestLogInfo = _GetQuestLogInfo,
	GetQuestLogNumRewards = _GetQuestLogNumRewards,
	GetQuestLogRewardInfo = _GetQuestLogRewardInfo,
	GetQuestInfo = _GetQuestInfo,
}

function addon:OnInitialize()
	addon.db = LibStub("AceDB-3.0"):New(addonName .. "DB", AddonDB_Defaults)

	DataStore:RegisterModule(addonName, addon, PublicMethods)
	DataStore:SetCharacterBasedMethod("GetQuestLogSize")
	DataStore:SetCharacterBasedMethod("GetQuestLogInfo")
	DataStore:SetCharacterBasedMethod("GetQuestLogNumRewards")
	DataStore:SetCharacterBasedMethod("GetQuestLogRewardInfo")
end

function addon:OnEnable()
	addon:RegisterEvent("PLAYER_ALIVE")
	addon:RegisterEvent("UNIT_QUEST_LOG_CHANGED")
end

function addon:OnDisable()
	addon:UnregisterEvent("PLAYER_ALIVE")
	addon:UnregisterEvent("UNIT_QUEST_LOG_CHANGED")
end

-- *** Utility functions ***

-- *** Scanning functions ***
local headersState = {}

local function SaveHeaders()
	local headerCount = 0		-- use a counter to avoid being bound to header names, which might not be unique.
	
	for i = GetNumQuestLogEntries(), 1, -1 do		-- 1st pass, expand all categories
		local _, _, _, _, isHeader, isCollapsed = GetQuestLogTitle(i)
		if isHeader then
			headerCount = headerCount + 1
			if isCollapsed then
				ExpandQuestHeader(i)
				headersState[headerCount] = true
			end
		end
	end
end

local function RestoreHeaders()
	local headerCount = 0
	for i = GetNumQuestLogEntries(), 1, -1 do
		local _, _, _, _, isHeader = GetQuestLogTitle(i)
		if isHeader then
			headerCount = headerCount + 1
			if headersState[headerCount] then
				CollapseQuestHeader(i)
			end
		end
	end
	wipe(headersState)
end

local REWARD_TYPE_CHOICE = "c"
local REWARD_TYPE_REWARD = "r"
local REWARD_TYPE_SPELL = "s"

local function ScanQuests()
	local char = addon.ThisCharacter
	local quests = char.Quests
	local links = char.QuestLinks
	local rewards = char.Rewards
	
	wipe(quests)
	wipe(links)
	wipe(rewards)

	local currentSelection = GetQuestLogSelection()		-- save the currently selected quest
	SaveHeaders()

	local RewardsCache = {}
	for i = 1, GetNumQuestLogEntries() do
		local title, _, questTag, groupSize, isHeader, _, isComplete = GetQuestLogTitle(i);
		
		if isHeader then
			quests[i] = "0|" .. (title or "")
		else
			SelectQuestLogEntry(i)
			local money = GetQuestLogRewardMoney()
			quests[i] = format("1|%s|%d|%d|%d", questTag or "", groupSize, money, isComplete or 0)
			links[i] = GetQuestLink(i)
			
			wipe(RewardsCache)
			local num = GetNumQuestLogChoices()		-- these are the actual item choices proposed to the player
			if num > 0 then
				for i = 1, num do
					local _, _, numItems, _, isUsable = GetQuestLogChoiceInfo(i)
					local link = GetQuestLogItemLink("choice", i)
					if link then
						local id = tonumber(link:match("item:(%d+)"))
						if id then
							table.insert(RewardsCache, REWARD_TYPE_CHOICE .."|"..id.."|"..numItems.."|"..(isUsable or 0))
						end
					end
				end
			end
			
			num = GetNumQuestLogRewards()				-- these are the rewards given anyway
			if num > 0 then
				for i = 1, num do
					local _, _, numItems, _, isUsable = GetQuestLogRewardInfo(i)
					local link = GetQuestLogItemLink("reward", i)
					if link then
						local id = tonumber(link:match("item:(%d+)"))
						if id then
							table.insert(RewardsCache, REWARD_TYPE_REWARD .. "|"..id.."|"..numItems.."|"..(isUsable or 0))
						end
					end
				end
			end

			if GetQuestLogRewardSpell() then		-- apparently, there is only one spell as reward
				local _, _, isTradeskillSpell, isSpellLearned = GetQuestLogRewardSpell()
				if isTradeskillSpell or isSpellLearned then
					local link = GetQuestLogSpellLink()
					if link then
						local id = tonumber(link:match("spell:(%d+)")) 
						if id then
							table.insert(RewardsCache, REWARD_TYPE_SPELL .. "|"..id)
						end
					end
				end
			end

			if #RewardsCache > 0 then
				rewards[i] = table.concat(RewardsCache, ",")
			end
		end
	end
	
	RestoreHeaders()
	SelectQuestLogEntry(currentSelection)		-- restore the selection to match the cursor, must be properly set if a user abandons a quest
	
	addon.ThisCharacter.lastUpdate = time()
end

-- *** EVENT HANDLERS ***
function addon:PLAYER_ALIVE()
	ScanQuests()
end

function addon:UNIT_QUEST_LOG_CHANGED()			-- triggered when accepting/validating a quest .. but too soon to refresh data
	addon:RegisterEvent("QUEST_LOG_UPDATE")		-- so register for this one ..
end

function addon:QUEST_LOG_UPDATE()
	addon:UnregisterEvent("QUEST_LOG_UPDATE")		-- .. and unregister it right away, since we only want it to be processed once (and it's triggered way too often otherwise)
	ScanQuests()
end
