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

local _, TSM = ...
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
local NUM_GT = 0
local ROW_HEIGHT = 14
local private = { groupTreeCollapsedStatus = {}, groupTreeSelectedGroupStatus = {} }


local function UpdateTree(self)
	self.statusText:SetText("")
	local rowData = {}
	local groupPathList, disabledGroupPaths = TSM.Groups:GetGroupPathList(self.module)

	for i, groupPath in ipairs(groupPathList) do
		if not disabledGroupPaths or not disabledGroupPaths[groupPath] then
			local pathParts = { TSM.CONST.GROUP_SEP:split(groupPath) }
			local leader = ""
			for i = 1, #pathParts - 1 do
				leader = leader .. "    "
			end
			local hasSubGroups = (groupPathList[i + 1] and (groupPathList[i + 1] == groupPath or strfind(groupPathList[i + 1], "^" .. TSMAPI.Util:StrEscape(groupPath) .. TSM.CONST.GROUP_SEP)))
			local parent = #pathParts > 1 and table.concat(pathParts, TSM.CONST.GROUP_SEP, 1, #pathParts - 1) or nil
			local index = #rowData + 1
			if self.selectedGroups[groupPath] == nil then
				-- select group by default
				self.selectedGroups[groupPath] = true
			end
			local groupNameText = TSM.Groups:ColorName(pathParts[#pathParts], #pathParts)
			rowData[index] = {
				value = leader .. format("%s %s%s|r", groupNameText, TSMAPI.Design:GetInlineColor("link"), hasSubGroups and (self.collapsed[groupPath] and "[+]" or "[-]") or ""),
				groupName = pathParts[#pathParts],
				parent = parent,
				groupPath = groupPath,
				hasSubGroups = hasSubGroups,
				index = index,
				isSelected = not self.isGroupBox and self.selectedGroups[groupPath], -- select all rows by default (unless it's for a GroupBox)
			}
		end
	end

	if #rowData == 0 then
		if #groupPathList == 0 then
			self.statusText:SetText(L["You currently don't have any groups setup. Type '/tsm' and click on the 'TradeSkillMaster Groups' button to setup TSM groups."])
		else
			self.statusText:SetText(format(L["None of your groups have %s operations assigned. Type '/tsm' and click on the 'TradeSkillMaster Groups' button to assign operations to your TSM groups."], self.module))
		end
	else
		self.statusText:SetText("")
	end

	self.rowData = rowData
	self:RefreshRows()
end

local function RedrawGroupTree(st)
	local width = st:GetWidth() - 14
	local height = st:GetHeight()
	st.NUM_ROWS = floor((st:GetParent():GetHeight() - (st.isGroupBox and 0 or 20)) / ROW_HEIGHT)

	-- add more rows if necessary
	while #st.rows < st.NUM_ROWS do
		st:AddRow()
	end

	st:RefreshRows()
end

local function SelectAll(self)
	for i = 1, #self.st.rowData do
		self.st.selectedGroups[self.st.rowData[i].groupPath] = true
		self.st.rowData[i].isSelected = true
	end
	self.st:RefreshRows()
	for _, row in ipairs(self.st.rows) do
		row.highlight:Show()
	end
end

local function DeselectAll(self)
	for i = 1, #self.st.rowData do
		self.st.selectedGroups[self.st.rowData[i].groupPath] = false
		self.st.rowData[i].isSelected = nil
	end
	self.st:RefreshRows()
	for _, row in ipairs(self.st.rows) do
		row.highlight:Hide()
	end
end

local defaultColScripts = {
	OnEnter = function(self)
		local tooltipLines = {}
		tinsert(tooltipLines, format(L["%sLeft-Click|r to select / deselect this group."], TSMAPI.Design:GetInlineColor("link")))
		if self.data.hasSubGroups then
			tinsert(tooltipLines, format(L["%sRight-Click|r to collapse / expand this group."], TSMAPI.Design:GetInlineColor("link")))
		end

		local operations = TSM.Groups:GetGroupOperations(self.data.groupPath, self.st.module)
		local operationLine = operations and table.concat(operations, ", ") or L["<No Operation>"]
		tinsert(tooltipLines, "")
		tinsert(tooltipLines, format(L["Operations: %s"], operationLine))

		GameTooltip:SetOwner(self, "ANCHOR_TOP")
		GameTooltip:AddLine(table.concat(tooltipLines, "\n"), 1, 1, 1)
		GameTooltip:Show()

		self.highlight:Show()
	end,
	OnLeave = function(self)
		GameTooltip:Hide()
		if not self.data.isSelected then
			self.highlight:Hide()
		end
	end,
	OnClick = function(self, button)
		if button == "RightButton" then
			self.st.collapsed[self.data.groupPath] = not self.st.collapsed[self.data.groupPath]
			self.st:RefreshRows()
			return
		end
		if self.st.isGroupBox then
			if self.data ~= self.st.groupBoxSelection then
				if self.st.groupBoxSelection then
					self.st.groupBoxSelection.isSelected = false
				end
				self.st.groupBoxSelection = self.data
			end
			self.data.isSelected = true
		else
			self.data.isSelected = not self.data.isSelected
			self.st.selectedGroups[self.data.groupPath] = self.data.isSelected or false
			if self.data.hasSubGroups then
				for i = 1, #self.st.rowData do
					if self.st.rowData[i].groupPath == self.data.groupPath or strfind(self.st.rowData[i].groupPath, "^" .. TSMAPI.Util:StrEscape(self.data.groupPath) .. TSM.CONST.GROUP_SEP) then
						self.st.selectedGroups[self.st.rowData[i].groupPath] = self.data.isSelected or false
						self.st.rowData[i].isSelected = self.data.isSelected
					end
				end
			end
		end
		self.st:RefreshRows()
		if self.data.isSelected then
			self.highlight:Show()
		else
			self.highlight:Hide()
		end
	end,
}

local methods = {
	AddRow = function(st)
		local row = CreateFrame("Button", nil, st.contentFrame)
		row:SetHeight(ROW_HEIGHT)
		row:RegisterForClicks("AnyUp")
		if #st.rows == 0 then
			row:SetPoint("TOPLEFT")
			row:SetPoint("TOPRIGHT")
		else
			row:SetPoint("TOPLEFT", st.rows[#st.rows], "BOTTOMLEFT")
			row:SetPoint("TOPRIGHT", st.rows[#st.rows], "BOTTOMRIGHT")
		end
		local highlight = row:CreateTexture()
		highlight:SetAllPoints()
		highlight:SetColorTexture(1, .9, 0, .2)
		highlight:Hide()
		row.highlight = highlight
		local text = row:CreateFontString()
		text:SetFont(TSMAPI.Design:GetContentFont("medium"))
		text:SetJustifyH("LEFT")
		text:SetJustifyV("CENTER")
		text:SetPoint("TOPLEFT", 1, -1)
		text:SetPoint("BOTTOMRIGHT", -1, 1)
		row:SetFontString(text)
		for name, func in pairs(defaultColScripts) do
			row:SetScript(name, func)
		end
		row.st = st
		tinsert(st.rows, row)
	end,
	GetRowIndex = function(self, value)
		for i, v in pairs(self.rowData) do
			if v.groupPath == value then
				return i
			end
		end
	end,
	RefreshRows = function(self)
		local offset = FauxScrollFrame_GetOffset(self.scrollFrame)
		self.offset = offset

		for i = #self.rowData, 1, -1 do
			local data = self.rowData[i]
			if not self.isGroupBox and not data.isSelected and data.parent then
				local index = self:GetRowIndex(data.parent)
				if index then
					self.rowData[index].isSelected = self.selectedGroups[self.rowData[index].groupPath]
				end
			end
		end

		local displayRows = {}
		for i = 1, #self.rowData do
			local pathParts = { TSM.CONST.GROUP_SEP:split(self.rowData[i].groupPath) }
			local isCollapsed = false
			for i = 1, #pathParts - 1 do
				local path = table.concat(pathParts, TSM.CONST.GROUP_SEP, 1, i)
				if self.collapsed[path] then
					isCollapsed = true
					break
				end
			end
			if not isCollapsed then
				if self.collapsed[self.rowData[i].groupPath] then
					self.rowData[i].value = gsub(self.rowData[i].value, TSMAPI.Util:StrEscape("[-]"), "[+]")
				else
					self.rowData[i].value = gsub(self.rowData[i].value, TSMAPI.Util:StrEscape("[+]"), "[-]")
				end
				tinsert(displayRows, self.rowData[i])
			end
		end
		FauxScrollFrame_Update(self.scrollFrame, #displayRows, self.NUM_ROWS, ROW_HEIGHT)

		for i = 1, #self.rows do
			if i > #displayRows or i > self.NUM_ROWS then
				self.rows[i]:Hide()
				self.rows[i].data = nil
			else
				self.rows[i]:Show()
				local data = displayRows[i + offset]
				if not data then return end
				self.rows[i].data = data

				if data.isSelected or self.rows[i]:IsMouseOver() then
					self.rows[i].highlight:Show()
				else
					self.rows[i].highlight:Hide()
				end
				self.rows[i]:SetText(data.value)
			end
		end
	end,
	SetSelection = function(self, rowNum, isSelected)
		self.selectedGroups[self.rowData[rowNum].groupPath] = isSelected or false
		self.rowData[rowNum].isSelected = isSelected
		self:RefreshRows()
	end,
	GetSelectedGroupInfo = function(self, rowNum)
		local groupInfo = {}
		for _, data in ipairs(self.rowData) do
			if data.isSelected then
				groupInfo[data.groupPath] = { operations = TSM.Groups:GetGroupOperations(data.groupPath, self.module), items = TSM.Groups:GetItems(data.groupPath) }
				if self.module and not groupInfo[data.groupPath].operations then
					groupInfo[data.groupPath] = nil
				end
			end
		end
		return groupInfo
	end,
	ClearSelection = function(self)
		for i = 1, #self.rowData do
			self.selectedGroups[self.rowData[i].groupPath] = false
			self.rowData[i].isSelected = nil
		end
		self.groupBoxSelection = nil
		self:RefreshRows()
	end,
	SetGropBoxSelection = function(self, groupPath)
		if self.groupBoxSelection then
			self.groupBoxSelection.isSelected = nil
			self.groupBoxSelection = nil
		end
		for i = 1, #self.rowData do
			if self.rowData[i].groupPath == groupPath then
				self.rowData[i].isSelected = true
				self.groupBoxSelection = self.rowData[i]
				break
			end
		end
	end,
	GetGroupBoxSelection = function(self)
		return self.groupBoxSelection and self.groupBoxSelection.groupPath
	end,
}

function TSM:CreateGroupTree(parent, module, label, isGroupBox)
	assert(type(parent) == "table", format(L["Invalid parent argument type. Expected table, got %s."], type(parent)))

	NUM_GT = NUM_GT + 1
	local st = CreateFrame("Frame", "TSMGroupTree"..NUM_GT, parent)
	st.isGroupBox = isGroupBox
	st.NUM_ROWS = floor((parent:GetHeight() - (st.isGroupBox and 0 or 20)) / ROW_HEIGHT)
	st.groupBoxSelection = nil
	st.module = module
	st.rows = {}
	for name, func in pairs(methods) do
		st[name] = func
	end
	if label or module then
		label = label or module
		if not private.groupTreeSelectedGroupStatus[label] then
			TSMAPI.Delay:AfterTime(0, function() SelectAll({st=st}) end)
		end
		private.groupTreeCollapsedStatus[label] = private.groupTreeCollapsedStatus[label] or {}
		private.groupTreeSelectedGroupStatus[label] = private.groupTreeSelectedGroupStatus[label] or {}
		st.collapsed = private.groupTreeCollapsedStatus[label]
		st.selectedGroups = private.groupTreeSelectedGroupStatus[label]
	else
		st.collapsed = {}
		st.selectedGroups = {}
	end

	local contentFrame = CreateFrame("Frame", nil, st)
	contentFrame:SetPoint("TOPLEFT")
	contentFrame:SetPoint("BOTTOMRIGHT", -15, isGroupBox and 0 or 18)
	st.contentFrame = contentFrame

	if not isGroupBox then
		local btn = TSM.GUI:CreateButton(st, 14)
		btn:SetPoint("BOTTOMLEFT", 0, 2)
		btn:SetPoint("BOTTOMRIGHT", st, "BOTTOM", -2, 2)
		btn:SetHeight(16)
		btn:SetText(L["Select All Groups"])
		btn:SetScript("OnClick", SelectAll)
		btn.st = st

		local btn = TSM.GUI:CreateButton(st, 14)
		btn:SetPoint("BOTTOMLEFT", st, "BOTTOM", 2, 2)
		btn:SetPoint("BOTTOMRIGHT", 0, 2)
		btn:SetHeight(16)
		btn:SetText(L["Deselect All Groups"])
		btn:SetScript("OnClick", DeselectAll)
		btn.st = st
	end

	-- frame to hold the rows
	local scrollFrame = CreateFrame("ScrollFrame", st:GetName().."ScrollFrame", st, "FauxScrollFrameTemplate")
	scrollFrame:SetScript("OnVerticalScroll", function(self, offset)
		FauxScrollFrame_OnVerticalScroll(self, offset, ROW_HEIGHT, function() st:RefreshRows() end)
	end)
	scrollFrame:SetAllPoints(contentFrame)
	st.scrollFrame = scrollFrame

	-- make the scroll bar consistent with the TSM theme
	local scrollBar = _G[scrollFrame:GetName().."ScrollBar"]
	scrollBar:ClearAllPoints()
	scrollBar:SetPoint("BOTTOMRIGHT", st, -1, isGroupBox and 1 or 19)
	scrollBar:SetPoint("TOPRIGHT", st, -1, -1)
	scrollBar:SetWidth(12)
	local thumbTex = scrollBar:GetThumbTexture()
	thumbTex:SetPoint("CENTER")
	TSMAPI.Design:SetContentColor(thumbTex)
	thumbTex:SetHeight(50)
	thumbTex:SetWidth(scrollBar:GetWidth())
	_G[scrollBar:GetName() .. "ScrollUpButton"]:Hide()
	_G[scrollBar:GetName() .. "ScrollDownButton"]:Hide()

	local text = st:CreateFontString()
	text:SetFont(TSMAPI.Design:GetContentFont("normal"))
	text:SetJustifyH("CENTER")
	text:SetJustifyV("CENTER")
	text:SetPoint("LEFT", 5, 0)
	text:SetPoint("RIGHT", -5, 0)
	text:SetHeight(100)
	text:SetNonSpaceWrap(true)
	st.statusText = text

	st:SetScript("OnShow", UpdateTree)
	st:SetScript("OnSizeChanged", RedrawGroupTree)
	st:SetAllPoints()
	UpdateTree(st)
	RedrawGroupTree(st)

	return st
end
