-- ------------------------------------------------------------------------------ --
--                                TradeSkillMaster                                --
--                http://www.curse.com/addons/wow/tradeskill-master               --
--                                                                                --
--             A TradeSkillMaster Addon (http://tradeskillmaster.com)             --
--    All Rights Reserved* - Detailed license information included with addon.    --
-- ------------------------------------------------------------------------------ --

-- This file contains all the code for the stuff that shows under the "Status" icon in the main TSM window.

local TSM = select(2, ...)
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
local AceGUI = LibStub("AceGUI-3.0") -- load the AceGUI libraries
local LSM = LibStub("LibSharedMedia-3.0")  -- load the SharedMedia library

local lib = TSMAPI
local private = {}
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Options_private")

local presetThemes = {
	light = { L["Light (by Ravanys - The Consortium)"], "inlineColors{link{49,56,133,1}link2{153,255,255,1}category{36,106,36,1}category2{85,180,8,1}}textColors{iconRegion{enabled{105,105,105,1}}title{enabled{49,56,85,1}}label{enabled{45,44,40,1}disabled{150,148,140,1}}text{enabled{245,244,240,1}disabled{95,98,90,1}}link{enabled{49,56,133,1}}}fontSizes{normal{15}medium{13}small{12}}edgeSize{1.5}frameColors{frameBG{backdrop{219,219,219,1}border{30,30,30,1}}content{backdrop{60,60,60,1}border{40,40,40,1}}frame{backdrop{228,228,228,1}border{199,199,199,1}}}" },
	goblineer = { L["Goblineer (by Sterling - The Consortium)"], "inlineColors{link{153,255,255,1}link2{153,255,255,1}category{36,106,36,1}category2{85,180,8,1}}textColors{iconRegion{enabled{249,255,247,1}}title{enabled{132,219,9,1}}label{enabled{216,225,211,1}disabled{150,148,140,1}}text{enabled{255,254,250,1}disabled{147,151,139,1}}link{enabled{49,56,133,1}}}fontSizes{normal{15}medium{13}small{12}}edgeSize{1.5}frameColors{frameBG{backdrop{24,24,24,0.93}border{30,30,30,1}}content{backdrop{42,42,42,1}border{0,0,0,0}}frame{backdrop{24,24,24,1}border{255,255,255,0.03}}}" },
	jaded = { L["Jaded (by Ravanys - The Consortium)"], "frameColors{frameBG{backdrop{0,0,0,0.6}border{0,0,0,0.4}}content{backdrop{62,62,62,1}border{72,72,72,1}}frame{backdrop{32,32,32,1}border{2,2,2,0.48}}}textColors{text{enabled{99,219,136,1}disabled{95,98,90,1}}iconRegion{enabled{43,255,156,1}}title{enabled{75,255,150,1}}label{enabled{99,219,136,1}disabled{177,176,168,1}}}edgeSize{1}fontSizes{normal{15}medium{13}small{12}}" },
	tsmdeck = { L["TSMDeck (by Jim Younkin - Power Word: Gold)"], "inlineColors{link2{153,255,255,1}category2{85,180,8,1}link{89,139,255,1}category{80,222,22,1}}textColors{iconRegion{enabled{117,117,122,1}}title{enabled{247,248,255,1}}label{enabled{238,249,237,1}disabled{110,110,110,1}}text{enabled{245,240,251,1}disabled{115,115,115,1}}link{enabled{49,56,133,1}}}fontSizes{normal{14}medium{13}small{12}}edgeSize{1}frameColors{frameBG{backdrop{29,29,29,1}border{20,20,20,1}}content{backdrop{27,27,27,1}border{67,67,65,1}}frame{backdrop{39,39,40,1}border{20,20,20,1}}}" },
	tsmclassic = { L["TSM Classic (by Jim Younkin - Power Word: Gold)"], "inlineColors{link{89,139,255,1}link2{153,255,255,1}category{80,222,22,1}category2{85,180,8,1}}textColors{text{enabled{245,240,251,1}disabled{115,115,115,1}}iconRegion{enabled{216,216,224,1}}title{enabled{247,248,255,1}}label{enabled{238,249,237,1}disabled{110,110,110,1}}}fontSizes{normal{14}medium{13}small{12}}edgeSize{1}frameColors{frameBG{backdrop{8,8,8,1}border{4,2,147,1}}content{backdrop{18,18,18,1}border{102,108,105,1}}frame{backdrop{2,2,2,1}border{4,2,147,1}}}" },
	functional = { L["The Functional Gold Maker (by Xsinthis - The Golden Crusade)"], "inlineColors{category{3,175,222,1}link2{153,255,255,1}tooltip{130,130,250}link{89,139,255,1}category2{6,24,180,1}}textColors{iconRegion{enabled{216,216,224,1}}title{enabled{247,248,255,1}}label{enabled{238,249,237,1}disabled{110,110,110,1}}text{enabled{245,240,251,1}disabled{115,115,115,1}}link{enabled{49,56,133,1}}}fontSizes{normal{14}small{12}}edgeSize{0.5}frameColors{frameBG{backdrop{28,28,28,1}border{74,5,0,1}}content{backdrop{18,18,18,0.64000001549721}border{84,7,3,1}}frame{backdrop{2,2,2,0.48000001907349}border{72,9,4,1}}}" },
}
local defaultTheme = presetThemes.goblineer

function private:LoadHelpPage(parent)
	local color = lib.Design:GetInlineColor("link")
	local moduleText = {
		TSMAPI.Design:ColorText("Accounting", "link") .. " - " .. L["Keeps track of all your sales and purchases from the auction house allowing you to easily track your income and expenditures and make sure you're turning a profit."] .. "\n",
		TSMAPI.Design:ColorText("Additions", "link") .. " - " .. L["Provides extra functionality that doesn't fit well in other modules."] .. "\n",
		TSMAPI.Design:ColorText("AuctionDB", "link") .. " - " .. L["Performs scans of the auction house and calculates the market value of items as well as the minimum buyout. This information can be shown in items' tooltips as well as used by other modules."] .. "\n",
		TSMAPI.Design:ColorText("Auctioning", "link") .. " - " .. L["Posts and cancels your auctions to / from the auction house according to pre-set rules. Also, this module can show you markets which are ripe for being reset for a profit."] .. "\n",
		TSMAPI.Design:ColorText("Crafting", "link") .. " - " .. L["Allows you to build a queue of crafts that will produce a profitable, see what materials you need to obtain, and actually craft the items."] .. "\n",
		TSMAPI.Design:ColorText("Destroying", "link") .. " - " .. L["Mills, prospects, and disenchants items at super speed!"] .. "\n",
		TSMAPI.Design:ColorText("ItemTracker", "link") .. " - " .. L["Tracks and manages your inventory across multiple characters including your bags, bank, and guild bank."] .. "\n",
		TSMAPI.Design:ColorText("Mailing", "link") .. " - " .. L["Allows you to quickly and easily empty your mailbox as well as automatically send items to other characters with the single click of a button."] .. "\n",
		TSMAPI.Design:ColorText("Shopping", "link") .. " - " .. L["Provides interfaces for efficiently searching for items on the auction house. When an item is found, it can easily be bought, canceled (if it's yours), or even posted from your bags."] .. "\n",
		TSMAPI.Design:ColorText("Warehousing", "link") .. " - " .. L["Manages your inventory by allowing you to easily move stuff between your bags, bank, and guild bank."] .. "\n",
		--TSMAPI.Design:ColorText("WoWuction", "link") .. " - " .. L["Allows you to use data from http://wowuction.com in other TSM modules and view its various price points in your item tooltips."] .. "\n",
	}

	local page = {
		{
			type = "ScrollFrame",
			layout = "flow",
			children = {
				-- {
					-- type = "InlineGroup",
					-- title = L["Resources:"],
					-- layout = "flow",
					-- relativeWidth = 1,
					-- noBorder = true,
					-- children = {
						-- {
							-- type = "Label",
							-- relativeWidth = .499,
							-- text = L["Using our website you can get help with TSM, suggest features, and give feedback."].."\n",
						-- },
						-- {
							-- type = "Image",
							-- sizeRatio = .15625,
							-- relativeWidth = .5,
							-- image = "Interface\\Addons\\TradeSkillMaster\\Media\\banner",
						-- },
						-- {
							-- type = "HeadingLine"
						-- },
						-- {
							-- type = "Image",
							-- sizeRatio = .15628,
							-- relativeWidth = 1,
							-- image = "Interface\\Addons\\TradeSkillMaster\\Media\\AppBanner",
						-- },
						-- {
							-- type = "Label",
							-- relativeWidth = 1,
							-- text = format("\n" .. L["Check out our completely free, desktop application which has tons of features including deal notification emails, automatic updating of AuctionDB and WoWuction prices, automatic TSM setting backup, and more! You can find this app by going to %s."], TSMAPI.Design:ColorText("http://tradeskillmaster.com/tsm_app", "link")),
						-- }
					-- },
				-- },
				-- {
					-- type = "Spacer",
				-- },
				{
					type = "InlineGroup",
					title = L["Module Information:"],
					layout = "List",
					relativeWidth = 1,
					noBorder = true,
					children = {},
				},
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["TradeSkillMaster Team"],
					children = {
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Lead Developer and Co-Founder:"], "link") .. " Sapu94 [US-Tichondrius(H)]",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Application and Addon Developer:"], "link") .. " Bart39 [EU-Darkspear(A)]",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Web Master:"], "link") .. " Drethic [US-Sentinels(A)]",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText("Logo / Graphic Designer:", "link") .. " Pwnstein",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Testers (Special Thanks):"], "link") .. " Cryan, GoblinRaset, Mithrildar, PhatLewts, WoWProfitz",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Past Contributors:"], "link") .. " Geemoney, Mischanix, Xubera, cduhn, cjo20",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Co-Founder:"], "link") .. " Cente [US-Illidan(H)]",
							relativeWidth = 1,
						},
						{
							type = "Label",
							text = TSMAPI.Design:ColorText(L["Wrath of the Lich King Revival:"], "link") .. " Gnomezilla [Warmane-Icecrown(A)], BlueAo [Warmane], andrew6180, Yoshiyuka, DimaSheiko, and other contributors...",
							relativeWidth = 1,
						},
					},
				},
			},
		},
	}

	for _, text in ipairs(moduleText) do
		tinsert(page[1].children[#page[1].children-1].children, {
			type = "Label",
			text = text,
			relativeWidth = 1,
		})
	end

	lib:BuildPage(parent, page)
end

local function GetSubStr(str)
	if not str then return end
	local startIndex, endIndex
	local balance = 0

	for i = 1, #str do
		local c = strsub(str, i, i)
		if c == '{' then
			if startIndex then
				balance = balance + 1
			else
				startIndex = i
			end
		elseif c == '}' then
			if balance > 0 then
				balance = balance - 1
			else
				endIndex = i
				break
			end
		end
	end

	if not startIndex or not endIndex then return end
	return strsub(str, startIndex + 1, endIndex - 1), startIndex, endIndex
end

local function StringToTable(data)
	local result = {}
	while true do
		local value, s, e = GetSubStr(data, { '{', '}' })
		if not value then return end
		local key = strsub(data, 1, s - 1)
		value = tonumber(value) or value

		if type(value) == "string" and strfind(value, "{") then
			value = StringToTable(value)
		elseif type(value) == "string" and strfind(value, ",") then
			value = { (","):split(value) }
			for i = 1, 4 do
				value[i] = tonumber(value[i])
			end
		end

		if type(value) == "nil" then
			return
		end

		result[key] = value
		if e + 1 > #data then
			break
		end
		data = strsub(data, e + 1, #data)
	end
	return result
end

function TSM:SetDesignDefaults(src, dest)
	for i, v in pairs(src) do
		if dest[i] then
			if type(v) == "table" then
				TSM:SetDesignDefaults(v, dest[i])
			end
		else
			if type(v) == "table" then
				dest[i] = CopyTable(v)
			else
				dest[i] = v
			end
		end
	end
end

local function DecodeAppearanceData(encodedData)
	if not encodedData then return end

	-- Decode the appearance-data string into a table.
	encodedData = gsub(encodedData, " ", "")
	local result = StringToTable(encodedData, 1)
	if not result then return TSM:Print(L["Invalid appearance data."]) end

	-- Preserve the user's current font choices and switch the theme.
	-- NOTE: The "design" table won't exist on first TSM launch, while we're
	-- loading the default design, so we also check the "design" table's existence.
	local preservedFonts = TSM.db.profile.design and TSM.db.profile.design.fonts or nil
	TSM.db.profile.design = result
	TSM.db.profile.design.fonts = preservedFonts  -- Is allowed to be nil.

	-- Use default values for any missing fields (such as fonts, if missing).
	TSM:SetDesignDefaults(TSM.designDefaults, TSM.db.profile.design)

	-- Apply the new design.
	TSMAPI:UpdateDesign()
end

function TSM:LoadDefaultDesign()
	DecodeAppearanceData(defaultTheme[2])
end

local function ShowImportFrame()
	local data

	local f = AceGUI:Create("TSMWindow")
	f:SetCallback("OnClose", function(self) AceGUI:Release(self) end)
	f:SetTitle("TradeSkillMaster - " .. L["Import Appearance Settings"])
	f:SetLayout("Flow")
	f:SetHeight(200)
	f:SetHeight(300)

	local spacer = AceGUI:Create("Label")
	spacer:SetFullWidth(true)
	spacer:SetText(" ")
	f:AddChild(spacer)

	local btn = AceGUI:Create("TSMButton")

	local eb = AceGUI:Create("MultiLineEditBox")
	eb:SetLabel(L["Appearance Data"])
	eb:SetFullWidth(true)
	eb:SetMaxLetters(0)
	eb:SetCallback("OnEnterPressed", function(_, _, val) btn:SetDisabled(false) data = val end)
	f:AddChild(eb)

	btn:SetDisabled(true)
	btn:SetText(L["Import Appearance Settings"])
	btn:SetFullWidth(true)
	btn:SetCallback("OnClick", function() DecodeAppearanceData(data) f:Hide() end)
	f:AddChild(btn)

	f.frame:SetFrameStrata("FULLSCREEN_DIALOG")
	f.frame:SetFrameLevel(100)
end

local function TblToStr(tbl)
	local tmp = {}
	for key, value in pairs(tbl) do
		tinsert(tmp, key .. "{")
		if tonumber(value) then
			tinsert(tmp, value)
		elseif #value == 0 then
			tinsert(tmp, TblToStr(value))
		else
			for _, colorVal in ipairs(value) do
				tinsert(tmp, tostring(colorVal))
				tinsert(tmp, ",")
			end
			tremove(tmp, #tmp)
		end
		tinsert(tmp, "}")
	end
	return table.concat(tmp, "")
end

local function EncodeAppearanceData()
	local keys = { "frameColors", "textColors", "inlineColors", "edgeSize", "fontSizes" }
	local testTbl = {}
	for _, key in ipairs(keys) do
		testTbl[key] = TSM.db.profile.design[key]
	end
	return TblToStr(testTbl)
end

local function ShowExportFrame()
	local f = AceGUI:Create("TSMWindow")
	f:SetCallback("OnClose", function(self) AceGUI:Release(self) end)
	f:SetTitle("TradeSkillMaster - " .. L["Export Appearance Settings"])
	f:SetLayout("Fill")
	f:SetHeight(300)

	local eb = AceGUI:Create("TSMMultiLineEditBox")
	eb:SetLabel(L["Appearance Data"])
	eb:SetMaxLetters(0)
	eb:SetText(EncodeAppearanceData())
	f:AddChild(eb)

	f.frame:SetFrameStrata("FULLSCREEN_DIALOG")
	f.frame:SetFrameLevel(100)
end

function private:LoadOptionsPage(parent)
	local presetThemeList = {}
	for key, tbl in pairs(presetThemes) do
		presetThemeList[key] = tbl[1]
	end
	local savedThemeList = {}
	for _, info in ipairs(TSM.db.profile.savedThemes) do
		tinsert(savedThemeList, info.name)
	end
	local themeName = ""

	local auctionTabs, auctionTabOrder
	if AuctionFrame and AuctionFrame.numTabs then
		auctionTabs, auctionTabOrder = {}, {}
		for i = 1, AuctionFrame.numTabs do
			local text = gsub(_G["AuctionFrameTab" .. i]:GetText(), "|r", "")
			text = gsub(text, "|c[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]", "")
			auctionTabs[text] = text
			tinsert(auctionTabOrder, text)
		end
	end

	local characterList = {}
	for character in pairs(TSMAPI:GetCharacters()) do
		tinsert(characterList, character)
	end
	
	local chatFrameList = {}
	local chatFrameValue, defaultValue
	for i=1, NUM_CHAT_WINDOWS do
		if DEFAULT_CHAT_FRAME == _G["ChatFrame"..i] then
			defaultValue = i
		end
		local name = strlower(GetChatWindowInfo(i) or "")
		if name ~= "" then
			if name == TSM.db.global.chatFrame then
				chatFrameValue = i
			end
			tinsert(chatFrameList, name)
		end
	end
	chatFrameValue = chatFrameValue or defaultValue

	local page = {
		{
			type = "ScrollFrame",
			layout = "flow",
			children = {
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["General Settings"],
					children = {
						{
							type = "CheckBox",
							label = L["Hide Minimap Icon"],
							settingInfo = { TSM.db.profile.minimapIcon, "hide" },
							relativeWidth = 0.5,
							callback = function(_, _, value)
								if value then
									TSM.LDBIcon:Hide("TradeSkillMaster")
								else
									TSM.LDBIcon:Show("TradeSkillMaster")
								end
							end,
						},
						{
							type = "CheckBox",
							label = L["Color Group Names by Depth"],
							settingInfo = { TSM.db.profile, "colorGroupName" },
							relativeWidth = 0.49,
							tooltip = L["If checked, group names will be colored based on their subgroup depth in group trees."],
						},
						{
							type = "CheckBox",
							label = L["Store Operations Globally"],
							value = TSM.db.global.globalOperations,
							relativeWidth = 0.5,
							callback = function(_, _, value)
								StaticPopupDialogs["TSM_GLOBAL_OPERATIONS"] = StaticPopupDialogs["TSM_GLOBAL_OPERATIONS"] or {
									text = L["If you have multiple profile set up with operations, enabling this will cause all but the current profile's operations to be irreversibly lost. Are you sure you want to continue?"],
									button1 = YES,
									button2 = CANCEL,
									timeout = 0,
									hideOnEscape = true,
									OnAccept = function()
										TSM.db.global.globalOperations = value
										if TSM.db.global.globalOperations then
											-- move current profile to global
											TSM.db.global.operations = CopyTable(TSM.db.profile.operations)
											-- clear out old operations
											for profile in TSMAPI:GetTSMProfileIterator() do
												TSM.db.profile.operations = nil
											end
										else
											-- move global to all profiles
											for profile in TSMAPI:GetTSMProfileIterator() do
												TSM.db.profile.operations = CopyTable(TSM.db.global.operations)
											end
											-- clear out old operations
											TSM.db.global.operations = nil
										end
										TSM:UpdateModuleProfiles()
										if parent.frame:IsVisible() then
											parent:ReloadTab()
										end
									end,
									preferredIndex = 3,
								}
								parent:ReloadTab()
								TSMAPI:ShowStaticPopupDialog("TSM_GLOBAL_OPERATIONS")
							end,
							tooltip = L["If checked, operations will be stored globally rather than by profile. TSM groups are always stored by profile. Note that if you have multiple profiles setup already with separate operation information, changing this will cause all but the current profile's operations to be lost."],
						},
						{
							type = "Dropdown",
							label = L["Forget Characters:"],
							list = characterList,
							relativeWidth = 0.49,
							callback = function(_, _, value)
								local name = characterList[value]
								TSM.db.realm.characters[name] = nil
								TSM:Printf("%s removed.", name)
								parent:ReloadTab()
							end,
							tooltip = L["If you delete, rename, or transfer a character off the current faction/realm, you should remove it from TSM's list of characters using this dropdown."],
						},
						{
							type = "Dropdown",
							label = L["Default BankUI Tab"],
							list = TSM:getBankTabs(),
							settingInfo = { TSM.db.global, "bankUITab" },
							relativeWidth = 0.5,
							tooltip = L["The default tab shown in the 'BankUI' frame."],
						},
						{
							type = "Dropdown",
							label = L["Chat Tab"],
							list = chatFrameList,
							value = chatFrameValue,
							callback = function(_, _, value)
								TSM.db.global.chatFrame = chatFrameList[value]
							end,
							relativeWidth = 0.49,
							tooltip = L["This option sets which tab TSM and its modules will use for printing chat messages."],
						},
					},
				},
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["Multi-Account Settings"],
					children = {
						{
							type = "Label",
							relativeWidth = 1,
							text = L["Various modules can sync their data between multiple accounts automatically whenever you're logged into both accounts."],
						},
						{
							type = "Spacer",
						},
						{
							type = "Label",
							relativeWidth = 1,
							text = L["First, log into a character on the same realm (and faction) on both accounts. Type the name of the OTHER character you are logged into in the box below. Once you have done this on both accounts, TSM will do the rest automatically. Once setup, syncing will automatically happen between the two accounts while on any character on the account (not only the one you entered during this setup)."],
						},
						{
							type = "EditBox",
							relativeWidth = 1,
							label = L["Character Name on Other Account"],
							callback = function(_, _, value)
								TSM:DoSyncSetup(value:trim())
							end,
							tooltip = L["See instructions above this editbox."],
						},
						{
							type = "HeadingLine",
						}
					},
				},
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["Auction House Tab Settings"],
					children = {
						{
							type = "Dropdown",
							label = auctionTabs and L["Default Tab"] or L["Default Tab (Open Auction House to Enable)"],
							list = auctionTabs or {},
							order = auctionTabOrder,
							disabled = not auctionTabs,
							settingInfo = { TSM.db.profile, "defaultAuctionTab" },
							relativeWidth = 0.5,
						},
						{
							type = "CheckBox",
							label = L["Open All Bags with Auction House"],
							settingInfo = { TSM.db.profile, "openAllBags" },
							relativeWidth = 0.5,
							tooltip = L["If checked, your bags will be automatically opened when you open the auction house."],
						},
						{
							type = "HeadingLine",
						},
						{
							type = "CheckBox",
							label = L["Show Bids in Auction Results Table (Requires Reload)"],
							settingInfo = { TSM.db.profile, "showBids" },
							tooltip = L["If checked, all tables listing auctions will display the bid as well as the buyout of the auctions. This will not take effect immediately and may require a reload."],
						},
						{
							type = "Slider",
							label = L["Number of Auction Result Rows (Requires Reload)"],
							settingInfo = { TSM.db.profile, "auctionResultRows" },
							relativeWidth = 0.5,
							min = 8,
							max = 25,
							step = 1,
							tooltip = L["Changes how many rows are shown in the auction results tables."],
						},
						{
							type = "HeadingLine",
						},
						{
							type = "CheckBox",
							label = L["Make Auction Frame Movable"],
							settingInfo = { TSM.db.profile, "auctionFrameMovable" },
							callback = function(_, _, value)
								if AuctionFrame then
									AuctionFrame:SetMovable(value)
								end
							end,
						},
						{
							type = "Slider",
							label = L["Auction Frame Scale"],
							settingInfo = { TSM.db.profile, "auctionFrameScale" },
							isPercent = true,
							relativeWidth = 0.5,
							min = 0.1,
							max = 2,
							step = 0.05,
							callback = function(_, _, value) if AuctionFrame then AuctionFrame:SetScale(value) end end,
							tooltip = L["Changes the size of the auction frame. The size of the detached TSM auction frame will always be the same as the main auction frame."],
						},
					},
				},
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["TSM Appearance Options"],
					children = {
						{
							type = "Label",
							text = L["Use the options below to change and tweak the appearance of TSM."],
							relativeWidth = 1,
						},
						{
							type = "Dropdown",
							label = L["Import Preset TSM Theme"],
							list = presetThemeList,
							relativeWidth = 1,
							callback = function(_, _, key)
								if presetThemes[key] then
									DecodeAppearanceData(presetThemes[key][2])
								end
							end,
							tooltip = L["Select a theme from this dropdown to import one of the preset TSM themes."],
						},
						{
							type = "Dropdown",
							label = L["Load Saved Theme"],
							list = savedThemeList,
							relativeWidth = 1,
							callback = function(_, _, index)
								DecodeAppearanceData(TSM.db.profile.savedThemes[index].theme)
							end,
							tooltip = L["Select a theme from this dropdown to import one of your saved TSM themes."],
						},
						{
							type = "EditBox",
							label = L["Theme Name"],
							relativeWidth = 0.5,
							callback = function(_, _, value) themeName = value:trim() end,
						},
						{
							type = "Button",
							text = L["Save Theme"],
							relativeWidth = 0.5,
							callback = function(_, _, value)
								if themeName == "" then
									return TSM:Print(L["Theme name is empty."])
								end
								TSM:Printf(L["Saved theme: %s."], themeName)
								tinsert(TSM.db.profile.savedThemes, { name = themeName, theme = EncodeAppearanceData() })
								parent:ReloadTab()
							end,
						},
						{
							type = "Button",
							text = L["Restore Default Colors"],
							relativeWidth = 1,
							callback = function() TSM:LoadDefaultDesign() parent:ReloadTab() end,
							tooltip = L["Restores all the color settings below to their default values."],
						},
						{
							type = "Button",
							text = L["Import Appearance Settings"],
							relativeWidth = 0.5,
							callback = ShowImportFrame,
							tooltip = L["This allows you to import appearance settings which other people have exported."],
						},
						{
							type = "Button",
							text = L["Export Appearance Settings"],
							relativeWidth = 0.5,
							callback = ShowExportFrame,
							tooltip = L["This allows you to export your appearance settings to share with others."],
						},
						{
							type = "HeadingLine"
						},
					},
				},
			},
		},
	}

	-- extra multi-account syncing widgets
	for account, players in pairs(TSM.db.realm.syncAccounts) do
		local playerList = {}
		for player in pairs(players) do
			tinsert(playerList, player)
		end
		local widgets = {
			{
				type = "Button",
				text = DELETE,
				relativeWidth = 0.2,
				callback = function()
					TSM.db.realm.syncAccounts[account] = nil
					parent:ReloadTab()
				end,
			},
			{
				type = "Label",
				relativeWidth = 0.79,
				text = table.concat(playerList, ", "),
			}
		}
		for _, widget in ipairs(widgets) do
			tinsert(page[1].children[2].children, widget)
		end
	end


	local function expandColor(tbl)
		return { tbl[1] / 255, tbl[2] / 255, tbl[3] / 255, tbl[4] }
	end

	local function compressColor(r, g, b, a)
		return { r * 255, g * 255, b * 255, a }
	end

	local frameColorOptions = {
		{ L["Frame Background - Backdrop"], "frameBG", "backdrop" },
		{ L["Frame Background - Border"], "frameBG", "border" },
		{ L["Region - Backdrop"], "frame", "backdrop" },
		{ L["Region - Border"], "frame", "border" },
		{ L["Content - Backdrop"], "content", "backdrop" },
		{ L["Content - Border"], "content", "border" },
	}
	for _, optionInfo in ipairs(frameColorOptions) do
		local label, key, subKey = unpack(optionInfo)

		local widget = {
			type = "ColorPicker",
			label = label,
			relativeWidth = 0.5,
			hasAlpha = true,
			value = expandColor(TSM.db.profile.design.frameColors[key][subKey]),
			callback = function(_, _, ...)
				TSM.db.profile.design.frameColors[key][subKey] = compressColor(...)
				TSMAPI:UpdateDesign()
			end,
		}
		tinsert(page[1].children[4].children, widget)
	end

	tinsert(page[1].children[4].children, { type = "HeadingLine" })

	local textColorOptions = {
		{ L["Icon Region"], "iconRegion", "enabled" },
		{ L["Title"], "title", "enabled" },
		{ L["Label Text - Enabled"], "label", "enabled" },
		{ L["Label Text - Disabled"], "label", "disabled" },
		{ L["Content Text - Enabled"], "text", "enabled" },
		{ L["Content Text - Disabled"], "text", "disabled" },
	}
	for _, optionInfo in ipairs(textColorOptions) do
		local label, key, subKey = unpack(optionInfo)

		local widget = {
			type = "ColorPicker",
			label = label,
			relativeWidth = 0.5,
			hasAlpha = true,
			value = expandColor(TSM.db.profile.design.textColors[key][subKey]),
			callback = function(_, _, ...)
				TSM.db.profile.design.textColors[key][subKey] = compressColor(...)
				TSMAPI:UpdateDesign()
			end,
		}
		tinsert(page[1].children[4].children, widget)
	end

	tinsert(page[1].children[4].children, { type = "HeadingLine" })

	local inlineColorOptions = {
		{ L["Link Text (Requires Reload)"], "link" },
		{ L["Link Text 2 (Requires Reload)"], "link2" },
		{ L["Category Text (Requires Reload)"], "category" },
		{ L["Category Text 2 (Requires Reload)"], "category2" },
		{ L["Item Tooltip Text"], "tooltip" },
		{ L["Advanced Option Text"], "advanced" },
	}
	for _, optionInfo in ipairs(inlineColorOptions) do
		local label, key = unpack(optionInfo)

		local widget = {
			type = "ColorPicker",
			label = label,
			relativeWidth = 0.5,
			hasAlpha = true,
			value = expandColor(TSM.db.profile.design.inlineColors[key]),
			callback = function(_, _, ...)
				TSM.db.profile.design.inlineColors[key] = compressColor(...)
				TSMAPI:UpdateDesign()
			end,
		}
		tinsert(page[1].children[4].children, widget)
	end

	tinsert(page[1].children[4].children, { type = "HeadingLine" })

	-- Figure out which fonts to automatically select in the dropdown.
	-- NOTE: We visually fall back to our defaults if the stored names are
	-- invalid, to avoid confusion if user-fonts are temporarily unavailable.
	local availableFontTitlesList = LSM:List("font")
	local selectedContentFontIndex = -1
	local selectedBoldFontIndex = -1
	local arialNarrowFontIndex = 1
	local tsmDroidBoldFontIndex = 1
	for i,v in ipairs(availableFontTitlesList) do
		if v == "Arial Narrow" then
			arialNarrowFontIndex = i
		elseif v == "TSM Droid Sans Bold" then
			tsmDroidBoldFontIndex = i
		end

		if v == TSM.db.profile.design.fonts.content then
			selectedContentFontIndex = i
		elseif v == TSM.db.profile.design.fonts.bold then
			selectedBoldFontIndex = i
		end
	end
	if selectedContentFontIndex == -1 then
		selectedContentFontIndex = arialNarrowFontIndex
	end
	if selectedBoldFontIndex == -1 then
		selectedBoldFontIndex = tsmDroidBoldFontIndex
	end

	local miscWidgets = {
		{
			type = "Dropdown",
			relativeWidth = 0.5,
			label = L["Normal Font (Requires Reload)"],
			list = availableFontTitlesList,
			value = selectedContentFontIndex,
			callback = function(_, _, index)
				local fontTitle = availableFontTitlesList[index]
				TSM.db.profile.design.fonts.content = fontTitle
			end,
			tooltip = format(L["Default: \"%s\"."], "Arial Narrow"),
		},
		{
			type = "Dropdown",
			relativeWidth = 0.5,
			label = L["Header Font (Requires Reload)"],
			list = availableFontTitlesList,
			value = selectedBoldFontIndex,
			callback = function(_, _, index)
				local fontTitle = availableFontTitlesList[index]
				TSM.db.profile.design.fonts.bold = fontTitle
			end,
			tooltip = format(L["Default: \"%s\"."], "TSM Droid Sans Bold"),
		},
		{
			type = "Slider",
			relativeWidth = 0.5,
			label = L["Small Text Size (Requires Reload)"],
			min = 6,
			max = 30,
			step = 1,
			settingInfo = { TSM.db.profile.design.fontSizes, "small" },
		},
		{
			type = "Slider",
			relativeWidth = 0.5,
			label = L["Medium Text Size (Requires Reload)"],
			min = 6,
			max = 30,
			step = 1,
			settingInfo = { TSM.db.profile.design.fontSizes, "medium" },
		},
		{
			type = "Slider",
			relativeWidth = 0.5,
			label = L["Normal Text Size (Requires Reload)"],
			min = 6,
			max = 30,
			step = 1,
			settingInfo = { TSM.db.profile.design.fontSizes, "normal" },
		},
		{
			type = "Slider",
			relativeWidth = 0.5,
			label = L["Border Thickness (Requires Reload)"],
			min = 0,
			max = 3,
			step = .1,
			settingInfo = { TSM.db.profile.design, "edgeSize" },
		},
	}
	for _, widget in ipairs(miscWidgets) do
		tinsert(page[1].children[4].children, widget)
	end

	lib:BuildPage(parent, page)
end

function private:LoadProfilesPage(container)
	-- Popup Confirmation Window used in this module
	StaticPopupDialogs["TSMDeleteConfirm"] = StaticPopupDialogs["TSMDeleteConfirm"] or {
		text = L["Are you sure you want to delete the selected profile?"],
		button1 = ACCEPT,
		button2 = CANCEL,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		OnCancel = false,
		-- OnAccept defined later
	}
	StaticPopupDialogs["TSMCopyProfileConfirm"] = StaticPopupDialogs["TSMCopyProfileConfirm"] or {
		text = L["Are you sure you want to overwrite the current profile with the selected profile?"],
		button1 = ACCEPT,
		button2 = CANCEL,
		timeout = 0,
		whileDead = true,
		hideOnEscape = true,
		OnCancel = false,
		-- OnAccept defined later
	}

	-- profiles page
	local text = {
		default = L["Default"],
		intro = L["You can change the active database profile, so you can have different settings for every character."],
		reset_desc = L["Reset the current profile back to its default values, in case your configuration is broken, or you simply want to start over."],
		reset = L["Reset Profile"],
		choose_desc = L["You can either create a new profile by entering a name in the editbox, or choose one of the already exisiting profiles."],
		new = L["New"],
		new_sub = L["Create a new empty profile."],
		choose = L["Existing Profiles"],
		copy_desc = L["Copy the settings from one existing profile into the currently active profile."],
		copy = L["Copy From"],
		delete_desc = L["Delete existing and unused profiles from the database to save space, and cleanup the SavedVariables file."],
		delete = L["Delete a Profile"],
		profiles = L["Profiles"],
		current = L["Current Profile:"] .. " " .. TSMAPI.Design:ColorText(TSM.db:GetCurrentProfile(), "link"),
	}

	-- Returns a list of all the current profiles with common and nocurrent modifiers.
	-- This code taken from AceDBOptions-3.0.lua
	local function GetProfileList(db, common, nocurrent)
		local profiles = {}
		local tmpprofiles = {}
		local defaultProfiles = { ["Default"] = "Default" }

		-- copy existing profiles into the table
		local currentProfile = db:GetCurrentProfile()
		for i, v in pairs(db:GetProfiles(tmpprofiles)) do
			if not (nocurrent and v == currentProfile) then
				profiles[v] = v
			end
		end

		-- add our default profiles to choose from ( or rename existing profiles)
		for k, v in pairs(defaultProfiles) do
			if (common or profiles[k]) and not (nocurrent and k == currentProfile) then
				profiles[k] = v
			end
		end

		return profiles
	end

	local page = {
		{
			-- scroll frame to contain everything
			type = "ScrollFrame",
			layout = "Flow",
			children = {
				{
					type = "Label",
					text = text["intro"] .. "\n\n",
					relativeWidth = 1,
				},
				{
					type = "Label",
					text = text["reset_desc"],
					relativeWidth = 1,
				},
				{
					type = "Button",
					text = text["reset"],
					relativeWidth = .5,
					callback = function() TSM.db:ResetProfile() end,
				},
				{
					type = "Label",
					text = text["current"],
					relativeWidth = .49,
				},
				{
					type = "HeadingLine",
				},
				{
					type = "Label",
					text = text["choose_desc"],
					relativeWidth = 1,
				},
				{
					type = "EditBox",
					label = text["new"],
					value = "",
					relativeWidth = .5,
					callback = function(_, _, value)
						value = value:trim()
						if value == "" then
							return TSM:Print(L["You cannot create a profile with an empty name."])
						end
						TSM.db:SetProfile(value)
						container:ReloadTab()
					end,
				},
				{
					type = "Dropdown",
					label = text["choose"],
					list = GetProfileList(TSM.db, true, nil),
					value = TSM.db:GetCurrentProfile(),
					relativeWidth = .49,
					callback = function(_, _, value)
						if value ~= TSM.db:GetCurrentProfile() then
							TSM.db:SetProfile(value)
							container:ReloadTab()
						end
					end,
				},
				{
					type = "HeadingLine",
				},
				{
					type = "Label",
					text = text["copy_desc"],
					relativeWidth = 1,
				},
				{
					type = "Dropdown",
					label = text["copy"],
					list = GetProfileList(TSM.db, true, nil),
					value = "",
					disabled = not GetProfileList(TSM.db, true, nil) and true,
					callback = function(_, _, value)
						if value == TSM.db:GetCurrentProfile() then return end
						StaticPopupDialogs["TSMCopyProfileConfirm"].OnAccept = function()
							TSM.db:CopyProfile(value)
							container:ReloadTab()
						end
						TSMAPI:ShowStaticPopupDialog("TSMCopyProfileConfirm")
					end,
				},
				{
					type = "HeadingLine",
				},
				{
					type = "Label",
					text = text["delete_desc"],
					relativeWidth = 1,
				},
				{
					type = "Dropdown",
					label = text["delete"],
					list = GetProfileList(TSM.db, true, nil),
					value = "",
					disabled = not GetProfileList(TSM.db, true, nil) and true,
					callback = function(_, _, value)
						if TSM.db:GetCurrentProfile() == value then
							TSM:Print(L["Cannot delete currently active profile!"])
							return
						end
						StaticPopupDialogs["TSMDeleteConfirm"].OnAccept = function()
							TSM.db:DeleteProfile(value)
							container:ReloadTab()
						end
						TSMAPI:ShowStaticPopupDialog("TSMDeleteConfirm")
					end,
				},
			},
		},
	}

	TSMAPI:BuildPage(container, page)
end

local treeGroup
function private:LoadCustomPriceSources(parent)
	private.treeGroup = AceGUI:Create("TSMTreeGroup")
	private.treeGroup:SetLayout("Fill")
	private.treeGroup:SetCallback("OnGroupSelected", private.SelectTree)
	private.treeGroup:SetStatusTable(TSM.db.profile.customPriceSourceTreeStatus)
	parent:AddChild(private.treeGroup)
	
	private:UpdateTree()
	private.treeGroup:SelectByPath(1)
end

function private:UpdateTree()
	if not private.treeGroup then return end
	
	local children = {}
	for name in pairs(TSM.db.global.customPriceSources) do
		tinsert(children, {value=name, text=name})
	end
	sort(children, function(a, b) return strlower(a.value) < strlower(b.value) end)
	private.treeGroup:SetTree({{value=1, text=L["Sources"], children=children}})
end

function private.SelectTree(treeGroup, _, selection)
	treeGroup:ReleaseChildren()
	
	selection = {("\001"):split(selection)}
	if #selection == 1 then
		private:DrawNewCustomPriceSource(treeGroup)
	else
		local name = selection[#selection]
		private:DrawCustomPriceSourceOptions(treeGroup, name)
	end
end

function private:DrawNewCustomPriceSource(container)
	local page = {
		{	-- scroll frame to contain everything
			type = "ScrollFrame",
			layout = "List",
			children = {
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["New Custom Price Source"],
					children = {
						{
							type = "Label",
							relativeWidth = 1,
							text = L["Custom price sources allow you to create more advanced custom prices throughout all of the TSM modules. Just as you can use the built-in price sources such as 'vendorsell' and 'vendorbuy' in your custom prices, you can use ones you make here (which themselves are custom prices)."],
						},
						{
							type = "HeadingLine",
						},
						{
							type = "EditBox",
							label = L["Custom Price Source Name"],
							relativeWidth = 0.8,
							callback = function(self,_,value)
								value = strlower((value or ""):trim())
								if value == "" then return end
								if gsub(value, "([a-z]+)", "") ~= "" then
									return TSM:Print(L["The name can ONLY contain letters. No spaces, numbers, or special characters."])
								end
								if TSM.db.global.customPriceSources[value] then
									return TSM:Printf(L["Error creating custom price source. Custom price source with name '%s' already exists."], value)
								end
								TSM.db.global.customPriceSources[value] = ""
								private:UpdateTree()
								if TSM.db.profile.gotoNewCustomPriceSource then
									private.treeGroup:SelectByPath(1, value)
								else
									self:SetText()
									self:SetFocus()
								end
							end,
							tooltip = L["Give your new custom price source a name. This is what you will type in to custom prices and is case insensitive (everything will be saved as lower case)."].."\n\n"..TSMAPI.Design:ColorText(L["The name can ONLY contain letters. No spaces, numbers, or special characters."], "link"),
						},
						{
							type = "CheckBox",
							label = L["Switch to New Custom Price Source After Creation"],
							relativeWidth = 1,
							settingInfo = {TSM.db.profile, "gotoNewCustomPriceSource"},
						},
					},
				},
			},
		},
	}
	TSMAPI:BuildPage(container, page)
end

function private:DrawCustomPriceSourceOptions(container, customPriceName)
	local page = {
		{	-- scroll frame to contain everything
			type = "ScrollFrame",
			layout = "List",
			children = {
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["Custom Price Source"],
					children = {
						{
							type = "Label",
							relativeWidth = 1,
							text = L["Below, set the custom price that will be evaluated for this custom price source."],
						},
						{
							type = "HeadingLine",
						},
						{
							type = "EditBox",
							label = L["Custom Price for this Source"],
							settingInfo = {TSM.db.global.customPriceSources, customPriceName},
							relativeWidth = 0.49,
							acceptCustom = true,
							tooltip = "",
						},
					},
				},
				{
					type = "InlineGroup",
					layout = "flow",
					title = L["Management"],
					children = {
						{
							type = "EditBox",
							label = L["Rename Custom Price Source"],
							value = operationName,
							relativeWidth = 0.5,
							callback = function(self,_,name)
								name = strlower((name or ""):trim())
								if name == "" then return end
								if gsub(name, "([a-z]+)", "") ~= "" then
									return TSM:Print(L["The name can ONLY contain letters. No spaces, numbers, or special characters."])
								end
								if TSM.db.global.customPriceSources[name] then
									return TSM:Printf(L["Error renaming custom price source. Custom price source with name '%s' already exists."], name)
								end
								TSM.db.global.customPriceSources[name] = TSM.db.global.customPriceSources[customPriceName]
								TSM.db.global.customPriceSources[customPriceName] = nil
								private:UpdateTree()
								private.treeGroup:SelectByPath(1, name)
							end,
							tooltip = L["Give your new custom price source a name. This is what you will type in to custom prices and is case insensitive (everything will be saved as lower case)."].."\n\n"..TSMAPI.Design:ColorText(L["The name can ONLY contain letters. No spaces, numbers, or special characters."], "link"),
						},
						{
							type = "Button",
							text = L["Delete Custom Price Source"],
							relativeWidth = 0.5,
							callback = function()
								TSM.db.global.customPriceSources[customPriceName] = nil
								private:UpdateTree()
								private.treeGroup:SelectByPath(1)
								TSM:Printf(L["Removed '%s' as a custom price source. Be sure to update any custom prices that were using this source."], customPriceName)
							end,
						},
					},
				},
			},
		},
	}
	TSMAPI:BuildPage(container, page)
end


function TSM:LoadOptions(parent)
	local tg = AceGUI:Create("TSMTabGroup")
	tg:SetLayout("Fill")
	tg:SetFullWidth(true)
	tg:SetFullHeight(true)
	tg:SetTabs({ { value = 1, text = L["TSM Info / Help"] }, { value = 2, text = L["Options"] }, { value = 3, text = L["Profiles"] }, { value = 4, text = TSMAPI.Design:ColorText(L["Custom Price Sources"], "advanced") } })
	tg:SetCallback("OnGroupSelected", function(self, _, value)
		tg:ReleaseChildren()
		StaticPopup_Hide("TSM_GLOBAL_OPERATIONS")

		if value == 1 then
			private:LoadHelpPage(self)
		elseif value == 2 then
			private:LoadOptionsPage(self)
		elseif value == 3 then
			private:LoadProfilesPage(self)
		elseif value == 4 then
			private:LoadCustomPriceSources(self)
		end
	end)
	parent:AddChild(tg)
	tg:SelectTab(1)
end