--[[
	Informant
	An addon for World of Warcraft that shows pertinent information about
	an item in a tooltip when you hover over the item in the game.
	3.5.0.0912 (Platypus)
	$Id: Informant.lua 912 2006-06-26 13:53:44Z mentalpower $

	License:
		This program is free software; you can redistribute it and/or
		modify it under the terms of the GNU General Public License
		as published by the Free Software Foundation; either version 2
		of the License, or (at your option) any later version.

		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		You should have received a copy of the GNU General Public License
		along with this program(see GLP.txt); if not, write to the Free Software
		Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
--]]

INFORMANT_VERSION = "3.5.0.0912"
if (INFORMANT_VERSION == "<".."%version%>") then
	INFORMANT_VERSION = "3.7.DEV"
end

-- GLOBAL FUNCTION PROTOTYPES:

local getItem--(itemID);     itemID is the first value in a blizzard hyperlink id
--                           this pattern would extract the id you need:
--                             "item:(%d+):%d+:%d+:%d+"



-- LOCAL FUNCTION PROTOTYPES:
local addLine				-- addLine(text, color)
local clear					-- clear()
local frameActive			-- frameActive(isActive)
local frameLoaded			-- frameLoaded()
local getCatName			-- getCatName(catID)
local getFilter				-- getFilter(filter)
local getFilterVal			-- getFilterVal(type)
local getItem				-- getItem(itemID)
local getRowCount			-- getRowCount()
local nilSafeString			-- nilSafeString(String)
local onEvent				-- onEvent(event)
local onLoad				-- onLoad()
local onVariablesLoaded		-- onVariablesLoaded()
local onQuit				-- onQuit()
local scrollUpdate			-- scrollUpdate(offset)
local setDatabase			-- setDatabase(database)
local setFilter				-- setFilter(key, value)
local setFilterDefaults		-- setFilterDefaults()
local setRequirements		-- setRequirements(requirements)
local setSkills				-- setSkills(skills)
local setVendors			-- setVendors(vendors)
local showHideInfo			-- showHideInfo()
local skillToName			-- skillToName(userSkill)
local split					-- split(str, at)
local tooltipHandler		-- tooltipHandler(funcVars, retVal, frame, name, link, quality, count, price)
local getKeyBindProfile		-- getKeyBindProfile()
local whitespace			-- whitespace(length)

-- LOCAL VARIABLES

local self = {}
local lines = {}
local itemInfo = nil

-- GLOBAL VARIABLES

BINDING_HEADER_INFORMANT_HEADER = _INFM('BindingHeader')
BINDING_NAME_INFORMANT_POPUPDOWN = _INFM('BindingTitle')

InformantConfig = {}

-- LOCAL DEFINES

CLASS_TO_CATEGORY_MAP = {
	[2]  = 1,
	[4]  = 2,
	[1]  = 3,
	[0]  = 4,
	[7]  = 5,
	[6]  = 6,
	[11] = 7,
	[9]  = 8,
	[5]  = 9,
	[15] = 10,
}

local filterDefaults = {
		['all'] = 'on',
		['embed'] = 'off',
		['locale'] = 'default',
		['show-vendor'] = 'on',
		['show-vendor-buy'] = 'on',
		['show-vendor-sell'] = 'on',
		['show-usage'] = 'on',
		['show-stack'] = 'on',
		['show-merchant'] = 'on',
		['show-quest'] = 'on',
		['show-icon'] = 'on',
	}

-- FUNCTION DEFINITIONS

function split(str, at)
	local splut = {}

	if (type(str) ~= "string") then return nil end
	if (not str) then str = "" end
	if (not at)
		then table.insert(splut, str)
	else
		for n, c in string.gfind(str, '([^%'..at..']*)(%'..at..'?)') do
			table.insert(splut, n)
			if (c == '') then break end
		end
	end
	return splut
end

function skillToName(userSkill)
	local skillName = self.skills[tonumber(userSkill)]
	local localized = "Unknown"
	if (skillName) then
		if (_INFM("Skill"..skillName)) then
			localized = _INFM("Skill"..skillName)
		else
			localized = "Unknown:"..skillName
		end
	end
	return localized, skillName
end

function getItem(itemID)
	local baseData = self.database[itemID]
	if (not baseData) then 
		return getItemBasic(itemID)
	end

	local _, _, _, iLevel, sType, _, iCount, _, sTexture = GetItemInfo(itemID)

	local baseSplit = split(baseData, ":")
	local buy = tonumber(baseSplit[1])
	local sell = tonumber(baseSplit[2])
	local class = tonumber(baseSplit[3])
	local quality = tonumber(baseSplit[4])
	local stack = tonumber(iCount) or tonumber(baseSplit[5])
	local additional = baseSplit[6]
	local usedby = baseSplit[7]
	local quantity = baseSplit[8]
	local limited = baseSplit[9]
	local merchantlist = baseSplit[10]
	local cat = CLASS_TO_CATEGORY_MAP[class]

	local dataItem = {
		['buy'] = buy,
		['sell'] = sell,
		['class'] = class,
		['cat'] = cat,
		['quality'] = quality,
		['stack'] = stack,
		['additional'] = additional,
		['usedby'] = usedby,
		['quantity'] = quantity,
		['limited'] = limited,
		['texture'] = sTexture,
		['fullData'] = true,
	}

	local addition = ""
	if (additional ~= "") then
		addition = " - ".._INFM("Addit"..additional)
	end
	local catName = getCatName(cat)
	if (not catName) then
		if (sType) then
			dataItem.classText = sType..addition
		else
			dataItem.classText = "Unknown"..addition
		end
	else
		dataItem.classText = catName..addition
	end

	if (usedby ~= '') then
		local usedList = split(usedby, ",")
		local skillName, localized, localeString
		local usage = ""
		dataItem.usedList = {}
		if (usedList) then
			for pos, userSkill in pairs(usedList) do
				localized = skillToName(userSkill)
				if (usage == "") then
					usage = localized
				else
					usage = usage .. ", " .. localized
				end
				table.insert(dataItem.usedList, localized)
			end
		end
		dataItem.usageText = usage
	end

	local reqSkill = 0
	local reqLevel = 0
	local skillName = ""

	local skillsRequired = self.requirements[itemID]
	if (skillsRequired) then
		local skillSplit = split(skillsRequired, ":")
		reqSkill = skillSplit[1]
		reqLevel = skillSplit[2]
		skillName = skillToName(reqSkill)
	end

	dataItem.isPlayerMade = (reqSkill ~= 0)
	dataItem.reqSkill = reqSkill
	dataItem.reqSkillName = skillName
	dataItem.reqLevel = iLevel or reqLevel

	if (merchantlist ~= '') then
		local merchList = split(merchantlist, ",")
		local vendName
		local vendList = {}
		if (merchList) then
			for pos, merchID in pairs(merchList) do
				vendName = self.vendors[tonumber(merchID)]
				if (vendName) then
					table.insert(vendList, vendName)
				end
			end
		end
		dataItem.vendors = vendList
	end

	dataItem.quests = {}
	dataItem.questCount = 0
	local questItemUse = InformantQuests.usage[itemID]
	if (questItemUse) then
		local questData = split(questItemUse, ",")
		local questInfoSplit, questID, questCount
		for pos, questInfo in pairs(questData) do
			questInfoSplit = split(questInfo, ":")
			questID = tonumber(questInfoSplit[1])
			questCount = tonumber(questInfoSplit[2])
			if (not dataItem.quests[questID]) then
				questName = Babylonian.GetString(InformantQuests.names, questID)
				dataItem.quests[questID] = {
					['count'] = questCount,
					['name'] = questName,
					['level'] = tonumber(InformantQuests.levels[questID])
				}
				dataItem.questCount = dataItem.questCount + 1
			end
		end
	end

	return dataItem
end

function getItemBasic(itemID)
	if (not itemID) then return end
	local sName, sLink, iQuality, iLevel, sType, sSubType, iCount, sEquipLoc, sTexture = GetItemInfo(tonumber(itemID))

	if (sName) then
		local dataItem = {
			['classText'] = sType,
			['quality'] = iQuality,
			['stack'] = iCount,
			['texture'] = sTexture,
			['reqLevel'] = iLevel,
			['fullData'] = false,
		}
	return dataItem
	end
end

function setSkills(skills)
	self.skills = skills
	Informant.SetSkills = nil -- Set only once
end

function setRequirements(requirements)
	self.requirements = requirements
	Informant.SetRequirements = nil -- Set only once
end

function setVendors(vendors)
	self.vendors = vendors
	Informant.SetVendors = nil -- Set only once
end

function setDatabase(database)
	self.database = database
	Informant.SetDatabase = nil -- Set only once
end


function setFilter(key, value)
	if (not InformantConfig.filters) then
		InformantConfig.filters = {};
		setFilterDefaults()
	end
	if (type(value) == "boolean") then
		if (value) then
			InformantConfig.filters[key] = 'on';
		else
			InformantConfig.filters[key] = 'off';
		end
	else
		InformantConfig.filters[key] = value;
	end
end

function getFilterVal(type)
	if (not InformantConfig.filters) then
		InformantConfig.filters = {}
		setFilterDefaults()
	end
	return InformantConfig.filters[type]
end

function getFilter(filter)
	value = getFilterVal(filter)
	if ((value == _INFM('CmdOn')) or (value == "on")) then return true
	elseif ((value == _INFM('CmdOff')) or (value == "off")) then return false end
	return true
end

function getLocale()
	local locale = Informant.GetFilterVal('locale');
	if (locale ~= 'on') and (locale ~= 'off') and (locale ~= 'default') then
		return locale;
	end
	return GetLocale();
end

local categories
function getCatName(catID)
	if (not categories) then categories = {GetAuctionItemClasses()} end
	for cat, name in categories do
		if (cat == catID) then return name end
	end
end

function tooltipHandler(funcVars, retVal, frame, name, link, quality, count, price)
	-- nothing to do, if informant is disabled
	if (not getFilter('all')) then
		return;
	end;

	if EnhTooltip.LinkType(link) ~= "item" then return end

	local quant = 0
	local sell = 0
	local buy = 0
	local stacks = 1

	local itemID, randomProp, enchant, uniqID, lame = EnhTooltip.BreakLink(link)
	if (itemID and itemID > 0) and (Informant) then
		itemInfo = getItem(itemID)
	end
	if (not itemInfo) then return end

	itemInfo.itemName = name
	itemInfo.itemLink = link
	itemInfo.itemCount = count
	itemInfo.itemQuality = quality

	stacks = itemInfo.stack
	if (not stacks) then stacks = 1 end

	buy = tonumber(itemInfo.buy) or 0
	sell = tonumber(itemInfo.sell) or 0
	quant = tonumber(itemInfo.quantity) or 0

	if (quant == 0) and (sell > 0) then
		local ratio = buy / sell
		if ((ratio > 3) and (ratio < 6)) then
			quant = 1
		else
			ratio = buy / (sell * 5)
			if ((ratio > 3) and (ratio < 6)) then
				quant = 5
			end
		end
	end
	if (quant == 0) then quant = 1 end

	buy = buy/quant

	itemInfo.itemBuy = buy
	itemInfo.itemSell = sell
	itemInfo.itemQuant = quant

	local embedded = getFilter('embed')

	if (getFilter('show-icon')) then
		if (itemInfo.texture) then
			EnhTooltip.SetIcon(itemInfo.texture)
		end
	end

	if (getFilter('show-vendor')) then
		if ((buy > 0) or (sell > 0)) then
			local bgsc = EnhTooltip.GetTextGSC(buy, true)
			local sgsc = EnhTooltip.GetTextGSC(sell, true)

			if (count and (count > 1)) then
				if (getFilter('show-vendor-buy')) then
					EnhTooltip.AddLine(string.format(_INFM('FrmtInfoBuymult'), count, bgsc), buy*count, embedded, true)
					EnhTooltip.LineColor(0.8, 0.5, 0.1)
				end
				if (getFilter('show-vendor-sell')) then
					EnhTooltip.AddLine(string.format(_INFM('FrmtInfoSellmult'), count, sgsc), sell*count, embedded, true)
					EnhTooltip.LineColor(0.8, 0.5, 0.1)
				end
			else
				if (getFilter('show-vendor-buy')) then
					EnhTooltip.AddLine(string.format(_INFM('FrmtInfoBuy')), buy, embedded, true)
					EnhTooltip.LineColor(0.8, 0.5, 0.1)
				end
				if (getFilter('show-vendor-sell')) then
					EnhTooltip.AddLine(string.format(_INFM('FrmtInfoSell')), sell, embedded, true)
					EnhTooltip.LineColor(0.8, 0.5, 0.1)
				end
			end
		end
	end

	if (getFilter('show-stack')) then
		if (stacks > 1) then
			EnhTooltip.AddLine(string.format(_INFM('FrmtInfoStx'), stacks), nil, embedded)
		end
	end
	if (getFilter('show-merchant')) then
		if (itemInfo.vendors) then
			local merchantCount = table.getn(itemInfo.vendors)
			if (merchantCount > 0) then
				EnhTooltip.AddLine(string.format(_INFM('FrmtInfoMerchants'), merchantCount), nil, embedded)
				EnhTooltip.LineColor(0.5, 0.8, 0.5)
			end
		end
	end
	if (getFilter('show-usage')) then
		local reagentInfo = ""
		if (itemInfo.classText) then
			reagentInfo = string.format(_INFM('FrmtInfoClass'), itemInfo.classText)
			EnhTooltip.AddLine(reagentInfo, nil, embedded)
			EnhTooltip.LineColor(0.6, 0.4, 0.8)
		end
		if (itemInfo.usedList and itemInfo.usageText) then
			if (table.getn(itemInfo.usedList) > 2) then

				local currentUseLine = nilSafeString(itemInfo.usedList[1])..", "..nilSafeString(itemInfo.usedList[2])..","
				reagentInfo = string.format(_INFM('FrmtInfoUse'), currentUseLine)
				EnhTooltip.AddLine(reagentInfo, nil, embedded)
				EnhTooltip.LineColor(0.6, 0.4, 0.8)

				for index = 3, table.getn(itemInfo.usedList), 2 do
					if (itemInfo.usedList[index+1]) then
						reagentInfo = whitespace(string.len(_INFM('FrmtInfoUse')) + 3)..nilSafeString(itemInfo.usedList[index])..", "..nilSafeString(itemInfo.usedList[index+1])..","
						EnhTooltip.AddLine(reagentInfo, nil, embedded)
						EnhTooltip.LineColor(0.6, 0.4, 0.8)
					else
						reagentInfo = whitespace(string.len(_INFM('FrmtInfoUse')) + 3)..nilSafeString(itemInfo.usedList[index])
						EnhTooltip.AddLine(reagentInfo, nil, embedded)
						EnhTooltip.LineColor(0.6, 0.4, 0.8)
					end
				end
			else
				reagentInfo = string.format(_INFM('FrmtInfoUse'), itemInfo.usageText)
				EnhTooltip.AddLine(reagentInfo, nil, embedded)
				EnhTooltip.LineColor(0.6, 0.4, 0.8)
			end
		end
	end
	if (getFilter('show-quest')) then
		if (itemInfo.quests) then
			local questCount = itemInfo.questCount
			if (questCount > 0) then
				EnhTooltip.AddLine(string.format(_INFM('FrmtInfoQuest'), questCount), nil, embedded)
				EnhTooltip.LineColor(0.5, 0.5, 0.8)
			end
		end
	end
end

function nilSafeString(str)
	if (not str) then str = "" end
	return str;
end

function whitespace(length)
	local spaces = ""
	while length ~= 0 do
		spaces = spaces.." "
		length = length - 1
	end
	return spaces
end

function showHideInfo()
	if (InformantFrame:IsVisible()) then
		InformantFrame:Hide()
	elseif (itemInfo) then
		InformantFrameTitle:SetText(_INFM('FrameTitle'))

		-- Woohoo! We need to provide any information we can from the item currently in itemInfo
		local quality = itemInfo.itemQuality or itemInfo.quality or 0

		local color = "ffffff"
		if (quality == 4) then color = "a335ee"
		elseif (quality == 3) then color = "0070dd"
		elseif (quality == 2) then color = "1eff00"
		elseif (quality == 0) then color = "9d9d9d"
		end

		clear()
		addLine(string.format(_INFM('InfoHeader'), color, itemInfo.itemName))

		local buy = itemInfo.itemBuy or itemInfo.buy or 0
		local sell = itemInfo.itemSell or itemInfo.sell or 0
		local quant = itemInfo.itemQuant or itemInfo.quantity or 0
		local count = itemInfo.itemCount or 1

		if ((buy > 0) or (sell > 0)) then
			local bgsc = EnhTooltip.GetTextGSC(buy, true)
			local sgsc = EnhTooltip.GetTextGSC(sell, true)

			if (count and (count > 1)) then
				local bqgsc = EnhTooltip.GetTextGSC(buy*count, true)
				local sqgsc = EnhTooltip.GetTextGSC(sell*count, true)
				addLine(string.format(_INFM('FrmtInfoBuymult'), count, bgsc)..": "..bqgsc, "ee8822")
				addLine(string.format(_INFM('FrmtInfoSellmult'), count, sgsc)..": "..sqgsc, "ee8822")
			else
				addLine(string.format(_INFM('FrmtInfoBuy'))..": "..bgsc, "ee8822")
				addLine(string.format(_INFM('FrmtInfoSell'))..": "..sgsc, "ee8822")
			end
		end

		if (itemInfo.stack > 1) then
			addLine(string.format(_INFM('FrmtInfoStx'), itemInfo.stack))
		end

		local reagentInfo = ""
		if (itemInfo.classText) then
			reagentInfo = string.format(_INFM('FrmtInfoClass'), itemInfo.classText)
			addLine(reagentInfo, "aa66ee")
		end
		if (itemInfo.usageText) then
			reagentInfo = string.format(_INFM('FrmtInfoUse'), itemInfo.usageText)
			addLine(reagentInfo, "aa66ee")
		end

		if (itemInfo.isPlayerMade) then
			addLine(string.format(_INFM('InfoPlayerMade'), itemInfo.reqLevel, itemInfo.reqSkillName), "5060ff")
		end

		if (itemInfo.quests) then
			local questCount = itemInfo.questCount
			if (questCount > 0) then
				addLine("")
				addLine(string.format(_INFM('FrmtInfoQuest'), questCount), nil, embed)
				addLine(string.format(_INFM('InfoQuestHeader'), questCount), "70ee90")
				for pos, quest in itemInfo.quests do
					addLine(string.format(_INFM('InfoQuestName'), quest.count, quest.name, quest.level), "80ee80")
				end
				addLine(string.format((_INFM('InfoQuestSource')).." WoWGuru.com"));
			end
		end

		if (itemInfo.vendors) then
			local vendorCount = table.getn(itemInfo.vendors)
			if (vendorCount > 0) then
				addLine("")
				addLine(string.format(_INFM('InfoVendorHeader'), vendorCount), "ddff40")
				for pos, merchant in itemInfo.vendors do
					addLine(string.format(" ".._INFM('InfoVendorName'), merchant), "eeee40")
				end
			end
		end
		InformantFrame:Show()
	else
		clear()
		addLine(_INFM('InfoNoItem'), "ff4010")
		InformantFrame:Show()
	end
end

function onQuit()
	if (not InformantConfig.position) then
		InformantConfig.position = { }
	end
	InformantConfig.position.x, InformantConfig.position.y = InformantFrame:GetCenter()
end

function onLoad()
	this:RegisterEvent("ADDON_LOADED")

	if (not InformantConfig) then
		InformantConfig = {}
		setFilterDefaults()
	end

	InformantFrameTitle:SetText(_INFM('FrameTitle'))
--	Informant.InitTrades();
end

local function frameLoaded()
	Stubby.RegisterEventHook("PLAYER_LEAVING_WORLD", "Informant", onQuit)
	Stubby.RegisterFunctionHook("EnhTooltip.AddTooltip", 300, tooltipHandler)

	onLoad()

	-- Setup the default for stubby to always load (people can override this on a
	-- per toon basis)
	Stubby.SetConfig("Informant", "LoadType", "always", true)

	-- Register our temporary command hook with stubby
	Stubby.RegisterBootCode("Informant", "CommandHandler", [[
		local function cmdHandler(msg)
			local i,j, cmd, param = string.find(string.lower(msg), "^([^ ]+) (.+)$")
			if (not cmd) then cmd = string.lower(msg) end
			if (not cmd) then cmd = "" end
			if (not param) then param = "" end
			if (cmd == "load") then
				if (param == "") then
					Stubby.Print("Manually loading Informant...")
					LoadAddOn("Informant")
				elseif (param == "always") then
					Stubby.Print("Setting Informant to always load for this character")
					Stubby.SetConfig("Informant", "LoadType", param)
					LoadAddOn("Informant")
				elseif (param == "never") then
					Stubby.Print("Setting Informant to never load automatically for this character (you may still load manually)")
					Stubby.SetConfig("Informant", "LoadType", param)
				else
					Stubby.Print("Your command was not understood")
				end
			else
				Stubby.Print("Informant is currently not loaded.")
				Stubby.Print("  You may load it now by typing |cffffffff/informant load|r")
				Stubby.Print("  You may also set your loading preferences for this character by using the following commands:")
				Stubby.Print("  |cffffffff/informant load always|r - Informant will always load for this character")
				Stubby.Print("  |cffffffff/informant load never|r - Informant will never load automatically for this character (you may still load it manually)")
			end
		end
		SLASH_INFORMANT1 = "/informant"
		SLASH_INFORMANT2 = "/inform"
		SLASH_INFORMANT3 = "/info"
		SLASH_INFORMANT4 = "/inf"
		SlashCmdList["INFORMANT"] = cmdHandler
	]]);
	Stubby.RegisterBootCode("Informant", "Triggers", [[
		local loadType = Stubby.GetConfig("Informant", "LoadType")
		if (loadType == "always") then
			LoadAddOn("Informant")
		else
			Stubby.Print("]].._INFM('MesgNotLoaded')..[[");
		end
	]]);
end

function onVariablesLoaded()
	setFilterDefaults()

	InformantFrameTitle:SetText(_INFM('FrameTitle'))

	if (InformantConfig.position) then
		InformantFrame:ClearAllPoints()
		InformantFrame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", InformantConfig.position.x, InformantConfig.position.y)
	end

	if (not InformantConfig.welcomed) then
		clear()
		addLine(_INFM('Welcome'))
		InformantConfig.welcomed = true
	end

	-- Restore key bindings
	-- This workaround is required for LoadOnDemand addons since their saved
	-- bindings are deleted upon login.
	local profile = getKeyBindProfile();
	if (InformantConfig and InformantConfig.bindings) then
		if (not	InformantConfig.bindings[profile]) then profile = 'global'; end
		if (InformantConfig.bindings[profile]) then
			for _,key in ipairs(InformantConfig.bindings[profile]) do
				SetBinding(key, 'INFORMANT_POPUPDOWN')
			end
		end
	end
	this:RegisterEvent("UPDATE_BINDINGS")	-- Monitor changes to bindings

	Informant.InitCommands()
end

function onEvent(event)
	if (event == "ADDON_LOADED" and string.lower(arg1) == "informant") then
		onVariablesLoaded()
		this:UnregisterEvent("ADDON_LOADED")
	elseif (event == "UPDATE_BINDINGS") then
		-- Store key bindings for Informant
		local key1, key2 = GetBindingKey('INFORMANT_POPUPDOWN');
		local profile = getKeyBindProfile();

		if (not InformantConfig.bindings) then InformantConfig.bindings = {}; end
		if (not InformantConfig.bindings[profile]) then InformantConfig.bindings[profile] = {}; end

		InformantConfig.bindings[profile][1] = key1;
		InformantConfig.bindings[profile][2] = key2;
	end
end

function frameActive(isActive)
	if (isActive) then
		scrollUpdate(0)
	end
end

function getRowCount()
	return table.getn(lines)
end

function scrollUpdate(offset)
	local numLines = getRowCount()
	if (numLines > 25) then
		if (not offset) then
			offset = FauxScrollFrame_GetOffset(InformantFrameScrollBar)
		else
			if (offset > numLines - 25) then offset = numLines - 25 end
			FauxScrollFrame_SetOffset(InformantFrameScrollBar, offset)
		end
	else
		offset = 0
	end
	local line
	for i=1, 25 do
		line = lines[i+offset]
		local f = getglobal("InformantFrameText"..i)
		if (line) then
			f:SetText(line)
			f:Show()
		else
			f:Hide()
		end
	end
	if (numLines > 25) then
		FauxScrollFrame_Update(InformantFrameScrollBar, numLines, 25, numLines)
		InformantFrameScrollBar:Show()
	else
		InformantFrameScrollBar:Hide()
	end
end

function testWrap(text)
	InformantFrameTextTest:SetText(text)
	if (InformantFrameTextTest:GetWidth() < InformantFrame:GetWidth() - 20) then
		return text, ""
	end

	local pos, test, best, rest
	best = text
	rest = nil
	pos = string.find(text, " ")
	while (pos) do
		test = string.sub(text, 1, pos-1)
		InformantFrameTextTest:SetText(test)
		if (InformantFrameTextTest:GetWidth() < InformantFrame:GetWidth() - 20) or (not rest) then
			best = test
			rest = string.sub(test, pos+1)
		else
			break
		end
		pos = string.find(text, " ", pos+1)
	end
	return best, rest
end

function addLine(text, color, level)
	if (text == nil) then return end
	if (not level) then level = 1 end
	if (level > 100) then
		return
	end

	if (type(text) == "table") then
		for pos, line in text do
			addLine(line, color, level)
		end
		return
	end

	if (not text) then
		table.insert(lines, "nil")
	else
		local best, rest = testWrap(text)
		if (color) then
			table.insert(lines, string.format("|cff%s%s|r", color, best))
		else
			table.insert(lines, best)
		end
		if (rest) and (rest ~= "") then
			addLine(rest, color, level+1)
		end
	end
	scrollUpdate()
end

function clear()
	lines = {}
	scrollUpdate()
end

function setFilterDefaults()
	if (not InformantConfig.filters) then InformantConfig.filters = {}; end
	for k,v in pairs(filterDefaults) do
		if (InformantConfig.filters[k] == nil) then
			InformantConfig.filters[k] = v;
		end
	end
end

-- Key binding helper functions

function getKeyBindProfile()
	if (IsAddOnLoaded("PerCharBinding")) then
		return GetRealmName() .. ":" .. UnitName("player")
	end
	return 'global'
end

-- GLOBAL OBJECT

Informant = {
	version = INFORMANT_VERSION,
	GetItem = getItem,
	GetRowCount = getRowCount,
	AddLine = addLine,
	Clear = clear,
	ShowHideInfo = showHideInfo,

	-- These functions are only meant for internal use.
	SetSkills = setSkills,
	SetRequirements = setRequirements,
	SetVendors = setVendors,
	SetDatabase = setDatabase,
	FrameActive = frameActive,
	FrameLoaded = frameLoaded,
	ScrollUpdate = scrollUpdate,
	GetFilter = getFilter,
	GetFilterVal = getFilterVal,
	GetLocale = getLocale,
	OnEvent = onEvent,
	SetFilter = setFilter,
	SetFilterDefaults = setFilterDefaults,
}

