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

--[[-----------------------------------------------------------------------------
Group Item List Widget
Provides two scroll lists with buttons to move selected items from one list to the other.
-------------------------------------------------------------------------------]]
local TSM = select(2, ...)
local Type, Version = "TSMGroupItemList", 1
local L = LibStub("AceLocale-3.0"):GetLocale("TradeSkillMaster") -- loads the localization table
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end

local ROW_HEIGHT = 16


--[[-----------------------------------------------------------------------------
Scripts
-------------------------------------------------------------------------------]]

local function ShowIcon(row)
	row.iconFrame:Show()
	row.label:SetPoint("TOPLEFT", 20, 0)
	row.label:SetPoint("BOTTOMRIGHT")
end

local function HideIcon(row)
	row.iconFrame:Hide()
	row.label:SetPoint("TOPLEFT", 0, 0)
	row.label:SetPoint("BOTTOMRIGHT")
end

local function UpdateScrollFrame(self)
	local parent = self:GetParent()
	if not parent.obj.GetListCallback then return end
	parent.items = parent.obj.GetListCallback(parent == parent.obj.leftFrame and "left" or "right")
	if not parent.list then
		parent.list = {}
		local usedItems = {}
		for _, itemLink in ipairs(parent.items) do
			local itemString = TSMAPI:GetItemString(itemLink)
			local name, link, _, _, _, _, _, _, _, texture = TSMAPI:GetSafeItemInfo(itemString)
			if itemString and name and texture and not usedItems[itemString] then
				usedItems[itemString] = true
				tinsert(parent.list, {value=itemString, link=link, icon=texture, sortText=strlower(name)})
			end
		end
		sort(parent.list, function(a, b) return a.sortText < b.sortText end)
	end

	local rows = self.rows
	-- clear all the rows
	for _, v in pairs(rows) do
		v.value = nil
		v.label:SetText("")
		v.iconFrame.icon:SetTexture("")
		v:Hide()
	end
	
	local rowData = {}
	for _, data in ipairs(self:GetParent().list) do
		if not data.filtered then
			tinsert(rowData, data)
		end
	end
	
	local maxRows = floor((self.height-5)/(ROW_HEIGHT+2))
	FauxScrollFrame_Update(self, #(rowData), maxRows-1, ROW_HEIGHT)
	local offset = FauxScrollFrame_GetOffset(self)
	local displayIndex = 0
	
	-- make the rows bigger if the scroller isn't showing
	if self:IsVisible() then
		rows[1]:SetPoint("TOPRIGHT", self:GetParent(), -26, 0)
	else
		rows[1]:SetPoint("TOPRIGHT", self:GetParent(), -10, 0)
	end
	
	for index, data in ipairs(rowData) do
		if index >= offset and displayIndex < maxRows then
			displayIndex = displayIndex + 1
			local row = rows[displayIndex]
			
			row.label:SetText(data.link)
			row.value = data.value
			row.data = data
			
			if data.selected then
				row:LockHighlight()
			else
				row:UnlockHighlight()
			end
			
			if data.icon then
				row.iconFrame.icon:SetTexture(data.icon)
				ShowIcon(row)
			else
				HideIcon(row)
			end
			row:Show()
		end
	end
end

local function UpdateRows(parent)
	local numRows = floor((parent.height-5)/(ROW_HEIGHT+2))
	parent.rows = parent.rows or {}
	for i=1, numRows do
		if not parent.rows[i] then
			local row = CreateFrame("Button", parent:GetName().."Row"..i, parent:GetParent())
			row:SetHeight(ROW_HEIGHT)
			row:SetScript("OnClick", function(self)
				self.data.selected = not self.data.selected
				if self.data.selected then
					self:LockHighlight()
				else
					self:UnlockHighlight()
				end
			end)
			row:SetScript("OnEnter", function(self)
				GameTooltip:SetOwner(self, "ANCHOR_NONE")
				GameTooltip:SetPoint("LEFT", parent:GetParent():GetParent(), "RIGHT")
				TSMAPI:SafeTooltipLink(self.data.link)
				GameTooltip:Show()
			end)
			-- row:SetScript("OnLeave", function() GameTooltip:Hide() BattlePetTooltip:Hide() end)
			row:SetScript("OnLeave", function() GameTooltip:Hide() end)
			
			if i > 1 then
				row:SetPoint("TOPLEFT", parent.rows[i-1], "BOTTOMLEFT", 0, -2)
				row:SetPoint("TOPRIGHT", parent.rows[i-1], "BOTTOMRIGHT", 0, -2)
			else
				row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
				row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 4, 0)
			end
			
			-- highlight / selection texture for the row
			local highlightTex = row:CreateTexture()
			highlightTex:SetTexture("Interface\\Buttons\\UI-Listbox-Highlight")
			highlightTex:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, 0)
			highlightTex:SetPoint("BOTTOMLEFT")
			highlightTex:SetAlpha(0.7)
			row:SetHighlightTexture(highlightTex)
			
			-- icon that goes to the left of the text
			local iconFrame = CreateFrame("Frame", nil, row)
			iconFrame:SetHeight(ROW_HEIGHT-2)
			iconFrame:SetWidth(ROW_HEIGHT-2)
			iconFrame:SetPoint("TOPLEFT")
			row.iconFrame = iconFrame
			
			-- texture that goes inside the iconFrame
			local iconTexture = iconFrame:CreateTexture(nil, "BACKGROUND")
			iconTexture:SetAllPoints(iconFrame)
			iconTexture:SetVertexColor(1, 1, 1)
			iconFrame.icon = iconTexture
			
			local label = row:CreateFontString(nil, "OVERLAY")
			label:SetFont(TSMAPI.Design:GetContentFont("normal"))
			label:SetJustifyH("LEFT")
			label:SetJustifyV("CENTER")
			label:SetPoint("TOPLEFT", 20, 0)
			label:SetPoint("BOTTOMRIGHT", 10, 0)
			TSMAPI.Design:SetWidgetTextColor(label)
			row.label = label
			
			parent.rows[i] = row
		end
	end
	UpdateScrollFrame(parent)
end

local function OnButtonClick(self)
	local selected = {}
	local rows, rowData
	
	if self.type == "Add" then
		rows = self.obj.leftFrame.scrollFrame.rows
		rowData = self.obj.leftFrame.list
	elseif self.type == "Remove" then
		rows = self.obj.rightFrame.scrollFrame.rows
		rowData = self.obj.rightFrame.list
	end
	if not rows then error("Invalid type") end
	
	local temp = {}
	for _, row in pairs(rows) do
		if row.data and row.data.selected and row.value then
			row.data.selected = false
			row:UnlockHighlight()
			temp[row.value] = true
			tinsert(selected, row.value)
		end
	end
	
	for _, data in pairs(rowData) do
		if data.selected and data.value and not temp[data.value] then
			data.selected = false
			tinsert(selected, data.value)
		end
	end

	self.obj:Fire("On"..self.type.."Clicked", selected)
end

local function OnFilterSet(self)
	self:ClearFocus()
	local text = strlower(TSMAPI:StrEscape(self:GetText():trim()))
	
	local filterStr, minLevel, maxLevel, minILevel, maxILevel
	for _, part in ipairs({("/"):split(text)}) do
		part = part:trim()
		if part ~= "" then
			local lvl = tonumber(part)
			local ilvl = gsub(part, "^i", "")
			ilvl = tonumber(ilvl)
			if lvl then
				if not minLevel then
					minLevel = lvl
				elseif not maxLevel then
					maxLevel = lvl
				else
					return TSM:Print(L["Invalid filter."])
				end
			elseif ilvl then
				if not minILevel then
					minILevel = ilvl
				elseif not maxILevel then
					maxILevel = ilvl
				else
					return TSM:Print(L["Invalid filter."])
				end
			else
				if filterStr then
					return TSM:Print(L["Invalid filter."])
				end
				filterStr = part
			end
		end
	end
	filterStr = filterStr or ""
	minLevel = minLevel or 0
	maxLevel = maxLevel or math.huge
	minILevel = minILevel or 0
	maxILevel = maxILevel or math.huge
	
	for _, info in ipairs(self.obj.leftFrame.list) do
		local name, _, _, ilvl, lvl = TSMAPI:GetSafeItemInfo(info.link)
		local selected = (strfind(strlower(name), filterStr) and ilvl >= minILevel and ilvl <= maxILevel and lvl >= minLevel and lvl <= maxLevel)
		info.selected = selected
		info.filtered = not selected
	end
	for _, info in ipairs(self.obj.rightFrame.list) do
		local name, _, _, ilvl, lvl = TSMAPI:GetSafeItemInfo(info.link)
		local selected = (strfind(strlower(name), filterStr) and ilvl >= minILevel and ilvl <= maxILevel and lvl >= minLevel and lvl <= maxLevel)
		info.selected = selected
		info.filtered = not selected
	end
	FauxScrollFrame_SetOffset(self.obj.leftFrame.scrollFrame, 0)
	FauxScrollFrame_SetOffset(self.obj.rightFrame.scrollFrame, 0)
	UpdateScrollFrame(self.obj.leftFrame.scrollFrame)
	UpdateScrollFrame(self.obj.rightFrame.scrollFrame)
end

local function OnClearButtonClicked(self)
	for _, info in ipairs(self.obj.leftFrame.list) do
		info.selected = false
	end
	for _, info in ipairs(self.obj.rightFrame.list) do
		info.selected = false
	end
	UpdateScrollFrame(self.obj.leftFrame.scrollFrame)
	UpdateScrollFrame(self.obj.rightFrame.scrollFrame)
end

local function OnIgnoreChanged(self, _, value)
	TSM.db.global.ignoreRandomEnchants = value
	self.obj.leftFrame.list = nil
	UpdateScrollFrame(self.obj.leftFrame.scrollFrame)
end


--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]

local methods = {
	["OnAcquire"] = function(self)
		-- restore default values
		self:SetHeight(550)
		TSMAPI:CreateTimeDelay(0.05, function() self.parent:DoLayout() end)
		self.filter:SetText("")
		self.ignoreCheckBox:SetValue(TSM.db.global.ignoreRandomEnchants)
	end,

	["OnRelease"] = function(self)
		-- clear any points / other values
		wipe(self.leftFrame.list)
		wipe(self.rightFrame.list)
		self.frame.leftTitle:SetText("")
		self.frame.rightTitle:SetText("")
	end,
	
	["OnHeightSet"] = function(self, height)
		if height == 100 then return end
		self.leftScrollFrame.height = self.frame:GetHeight() - 85
		self.rightScrollFrame.height = self.frame:GetHeight() - 85
		UpdateRows(self.leftScrollFrame)
		UpdateRows(self.rightScrollFrame)
	end,
	
	["SetListCallback"] = function(self, callback)
		self.GetListCallback = callback
		self.leftFrame.list = nil
		self.rightFrame.list = nil
		UpdateScrollFrame(self.leftScrollFrame)
		UpdateScrollFrame(self.rightScrollFrame)
	end,
	
	["SetTitle"] = function(self, side, title)
		if strlower(side) == "left" then
			self.frame.leftTitle:SetText(title)
		elseif strlower(side) == "right" then
			self.frame.rightTitle:SetText(title)
		elseif title then
			error("Invalid side passed. Expected 'left' or 'right'")
		end
	end,
	
	["SetIgnoreVisible"] = function(self, shown)
		if shown then
			self.ignoreCheckBox.frame:Show()
		else
			self.ignoreCheckBox.frame:Hide()
		end
	end,
}


--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]

local function Constructor()
	local borderColor = TSM.db.profile.frameBackdropColor
	local name = "TSMGroupItemList" .. AceGUI:GetNextWidgetNum(Type)
	local frame = CreateFrame("Frame", name, UIParent)
	frame:Hide()
	
	local leftFrame = CreateFrame("Frame", name.."LeftFrame", frame)
	leftFrame:SetPoint("TOPLEFT", 0, -80)
	leftFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOM", -7, 0)
	TSMAPI.Design:SetContentColor(leftFrame)
	leftFrame.list = {}
	frame.leftFrame = leftFrame
	
	local leftTitle = frame:CreateFontString(nil, "OVERLAY")
	leftTitle:SetFont(TSMAPI.Design:GetContentFont("normal"))
	TSMAPI.Design:SetTitleTextColor(leftTitle)
	leftTitle:SetJustifyH("LEFT")
	leftTitle:SetJustifyV("BOTTOM")
	leftTitle:SetHeight(15)
	leftTitle:SetPoint("BOTTOMLEFT", leftFrame, "TOPLEFT", 8, 0)
	leftTitle:SetPoint("BOTTOMRIGHT", leftFrame, "TOPRIGHT", -8, 0)
	frame.leftTitle = leftTitle
	
	local leftSF = CreateFrame("ScrollFrame", name.."LeftFrameScrollFrame", leftFrame, "FauxScrollFrameTemplate")
	leftSF:SetPoint("TOPLEFT", 5, -5)
	leftSF:SetPoint("BOTTOMRIGHT", -5, 5)
	leftSF:SetScript("OnVerticalScroll", function(self, offset)
		FauxScrollFrame_OnVerticalScroll(self, offset, ROW_HEIGHT, function() UpdateScrollFrame(self) end) 
	end)
	leftFrame.scrollFrame = leftSF
	
	local leftScrollBar = _G[leftSF:GetName().."ScrollBar"]
	leftScrollBar:ClearAllPoints()
	leftScrollBar:SetPoint("BOTTOMRIGHT")
	leftScrollBar:SetPoint("TOPRIGHT")
	leftScrollBar:SetWidth(12)
	
	local thumbTex = leftScrollBar:GetThumbTexture()
	thumbTex:SetPoint("CENTER")
	TSMAPI.Design:SetFrameColor(thumbTex)
	thumbTex:SetHeight(150)
	thumbTex:SetWidth(leftScrollBar:GetWidth())
	_G[leftScrollBar:GetName().."ScrollUpButton"]:Hide()
	_G[leftScrollBar:GetName().."ScrollDownButton"]:Hide()
	
	local rightFrame = CreateFrame("Frame", name.."RightFrame", frame)
	rightFrame:SetPoint("TOPLEFT", frame, "TOP", 7, -80)
	rightFrame:SetPoint("BOTTOMRIGHT", 0, 0)
	TSMAPI.Design:SetContentColor(rightFrame)
	rightFrame.list = {}
	frame.rightFrame = rightFrame
	
	local rightTitle = frame:CreateFontString(nil, "OVERLAY")
	rightTitle:SetFont(TSMAPI.Design:GetContentFont("normal"))
	TSMAPI.Design:SetTitleTextColor(rightTitle)
	rightTitle:SetJustifyH("LEFT")
	rightTitle:SetJustifyV("BOTTOM")
	rightTitle:SetHeight(15)
	rightTitle:SetPoint("BOTTOMLEFT", rightFrame, "TOPLEFT", 8, 0)
	rightTitle:SetPoint("BOTTOMRIGHT", rightFrame, "TOPRIGHT", -8, 0)
	frame.rightTitle = rightTitle
	
	local rightSF = CreateFrame("ScrollFrame", name.."RightFrameScrollFrame", rightFrame, "FauxScrollFrameTemplate")
	rightSF:SetPoint("TOPLEFT", 5, -5)
	rightSF:SetPoint("BOTTOMRIGHT", -5, 5)
	rightSF:SetScript("OnVerticalScroll", function(self, offset)
		FauxScrollFrame_OnVerticalScroll(self, offset, ROW_HEIGHT, function() UpdateScrollFrame(self) end) 
	end)
	rightFrame.scrollFrame = rightSF
	
	local rightScrollBar = _G[rightSF:GetName().."ScrollBar"]
	rightScrollBar:ClearAllPoints()
	rightScrollBar:SetPoint("BOTTOMRIGHT")
	rightScrollBar:SetPoint("TOPRIGHT")
	rightScrollBar:SetWidth(12)
	
	local thumbTex = rightScrollBar:GetThumbTexture()
	thumbTex:SetPoint("CENTER")
	TSMAPI.Design:SetFrameColor(thumbTex)
	thumbTex:SetHeight(150)
	thumbTex:SetWidth(rightScrollBar:GetWidth())
	_G[rightScrollBar:GetName().."ScrollUpButton"]:Hide()
	_G[rightScrollBar:GetName().."ScrollDownButton"]:Hide()
	
	
	
	local label = TSMAPI.GUI:CreateLabel(frame, "normal")
	label:SetText("Filter:")
	label:SetPoint("TOPLEFT", 0, -5)
	label:SetHeight(20)
	label:SetJustifyV("CENTER")
	
	local filter = TSMAPI.GUI:CreateInputBox(frame)
	filter:SetPoint("BOTTOMLEFT", label, "BOTTOMRIGHT", 2, 0)
	filter:SetHeight(20)
	filter:SetWidth(150)
	filter:SetScript("OnEnterPressed", OnFilterSet)
	filter.tooltip = L["All items with names containing the specified filter will be selected. This makes it easier to add/remove multiple items at a time."]
	
	local line = TSMAPI.GUI:CreateHorizontalLine(frame, 0)
	line:SetPoint("TOPLEFT", 0, -58)
	line:SetPoint("TOPRIGHT", 0, -58)
	local line = TSMAPI.GUI:CreateVerticalLine(frame, 0)
	line:ClearAllPoints()
	line:SetPoint("TOP", 0, -60)
	line:SetPoint("BOTTOM")

	local ignoreCheckBox = TSMAPI.GUI:CreateCheckBox(frame, L["When checked, random enchants will be ignored for ungrouped items.\n\nNB: This will not affect parent group items that were already added with random enchants\n\nIf you have this checked when adding an ungrouped randomly enchanted item, it will act as all possible random enchants of that item."])
	ignoreCheckBox:SetLabel(L["Ignore Random Enchants on Ungrouped Items"])
	ignoreCheckBox:SetPoint("BOTTOMLEFT", filter, "BOTTOMRIGHT", 20, 5)
	ignoreCheckBox:SetPoint("TOPRIGHT", 0, -2)
	ignoreCheckBox:SetCallback("OnValueChanged", OnIgnoreChanged)
	
	local addBtn = TSMAPI.GUI:CreateButton(frame, 18)
	addBtn:SetPoint("TOPLEFT", 0, -33)
	addBtn:SetWidth(170)
	addBtn:SetHeight(20)
	addBtn:SetText(L["Add >>>"])
	addBtn.type = "Add"
	addBtn:SetScript("OnClick", OnButtonClick)
	
	local removeBtn = TSMAPI.GUI:CreateButton(frame, 18)
	removeBtn:SetPoint("TOPRIGHT", 0, -33)
	removeBtn:SetWidth(170)
	removeBtn:SetHeight(20)
	removeBtn:SetText(L["<<< Remove"])
	removeBtn.type = "Remove"
	removeBtn:SetScript("OnClick", OnButtonClick)
	removeBtn.tooltip = L["You can hold shift while clicking this button to remove the items from ALL groups rather than keeping them in the parent group (if one exists)."]
	
	local clearBtn = TSMAPI.GUI:CreateButton(frame, 16)
	clearBtn:SetPoint("BOTTOMLEFT", addBtn, "BOTTOMRIGHT", 15, 0)
	clearBtn:SetPoint("BOTTOMRIGHT", removeBtn, "BOTTOMLEFT", -15, 0)
	clearBtn:SetHeight(20)
	clearBtn:SetText(L["Clear Selection"])
	clearBtn:SetScript("OnClick", OnClearButtonClicked)
	clearBtn.tooltip = L["Deselects all items in both columns."]
	

	local widget = {
		leftFrame = leftFrame,
		leftScrollFrame = leftSF,
		rightFrame = rightFrame,
		rightScrollFrame = rightSF,
		ignoreCheckBox = ignoreCheckBox,
		filter = filter,
		clearBtn = clearBtn,
		frame = frame,
		type  = Type
	}
	for method, func in pairs(methods) do
		widget[method] = func
	end
	
	addBtn.obj = widget
	removeBtn.obj = widget
	widget.ignoreCheckBox.obj = widget
	widget.filter.obj = widget
	widget.clearBtn.obj = widget
	widget.leftFrame.obj = widget
	widget.rightFrame.obj = widget
	widget.frame.obj = widget

	return AceGUI:RegisterAsWidget(widget)
end

AceGUI:RegisterWidgetType(Type, Constructor, Version)