--[[
Name: TableTop-1.0
Revision: $Rev: 1 $
Developed by: Antiarc
Documentation: 
SVN: http://svn.digitalsentience.com/svn/wow/trunk/TableTop-1.0
Description: Library to handle the creation and management of a table of resizable/sortable columns.
Dependencies: AceLibrary, Dewdrop-2.0
]]--

local MAJOR_VERSION = "TableTop-1.0"
local MINOR_VERSION = "$Revision: 1 $"
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
local TableTop = {}
local dewdrop = AceLibrary("Dewdrop-2.0")

local function TT_IsTBC()
	local version = GetBuildInfo();
	return string.find(version, "^2%.")
end

function TableTop:TabOnMouseDown(f)
	if arg1 == "LeftButton" then
		this:SetScript("OnUpdate", function() self:TabOnUpdate(this) end)
		this:LockHighlight()
		if this.OldOnMouseDown then
			this.OldOnMouseDown()
		end	
		local x,y = GetCursorPosition()
		this.isMoving = true
		this.mouseX = x
		this.mouseY = x
	end
end

function TableTop:TabOnMouseUp(f)
	if arg1 == "LeftButton" then
		this:UnlockHighlight()
		if this.OldOnMouseUp then
			this.OldOnMouseUp()
		end	
		this.isMoving = false
		this:SetScript("OnUpdate", nil)
		self:UpdateParentMinSize()
		self:SetupRows()
	end
end

function TableTop:TabOnUpdate(f)
	if this.OldOnUpdate then
		this.OldOnUpdate()
	end	
	if this.isMoving then
		local x,y = GetCursorPosition()
		local diff = x - this.mouseX
		this.mouseX = x
		this.mouseY = y
		if diff ~= 0 then
			self:ResizeTab(diff, this)
		end
	end
end

function TableTop:TabOnEnter()
	if this.tabdata.desc then
		GameTooltip:SetOwner(this, "ANCHOR_LEFT")
		GameTooltip:SetText(this.tabdata.desc, 0.6, 0.8, 1, 1, 1)
	end
end

function TableTop:TabOnLeave()
	GameTooltip:Hide()
end

function TableTop:TabOnClick(doSort)
	if arg1 == "LeftButton" and doSort and self.clickFunc then
		self.clickFunc()
	elseif arg1 == "RightButton" then
		-- Do the dew

		dewdrop:Open(this,
			'children', function()
					for k,v in ipairs(self.tabData) do
						dewdrop:AddLine(
							'text', v.name,
							'checked', (not v.hidden),
							'disabled', v.always_on,
							'func', function(c, s)
								s:ToggleColumn(c.key, not c.hidden)
								self.db.tabStates[c.key] = c.hidden
								self:UpdateParentMinSize()
							end,
							'arg1', v,
							'arg2', self,
							'closeWhenClicked', true
						)
					end
				end,
			'point', "TOP",
			'relativePoint', "BOTTOM"
		)
		
	end
end

function TableTop:Init(database, headerParent, dataParent)
	self.db = database
	self.db.tabStates = self.db.tabStates or {}
	self.db.tabWidths = self.db.tabWidths or {}
	self.offsets = self.db.offsets
	self.row_opts = {parent = dataParent}
	self.headerParent = headerParent
	self.dataParent = dataParent
	self.maxRows = 0
	self.inited = true
end

function TableTop:UpdateParentMinSize()
	local width = 0 + (self.offsets.data_left or 10) - (self.offsets.data_right or 10)
	for k,v in pairs(self.TabFrames) do
		if not v.tabdata.hidden then
			width = width + v.tabdata.minwidth
		end
	end
	local x,y = self.headerParent:GetMinResize()
	self.headerParent:SetMinResize(width, y)		
	if self.headerParent:GetWidth() < width then
		self.headerParent:SetWidth(width)
	end

	local x,y = self.dataParent:GetMinResize()
	self.dataParent:SetMinResize(width, y)	
	
	if self.dataParent:GetWidth() < width then
		self.dataParent:SetWidth(width)
	end
end

function TableTop:SetDataRowOffsets(opts)
	self.offsets = opts
	if self.leftAnchor then
		self.leftAnchor:SetPoint("LEFT", self.leftAnchor:GetParent(), "LEFT", self.offsets.tab_left or 0, 0)
		self.leftAnchor:SetPoint("TOP", self.leftAnchor:GetParent(), "TOP", 0, self.offsets.tab_top or 0)
		self.leftAnchor:SetPoint("BOTTOM", self.leftAnchor:GetParent())
	end
	if self.rightAnchor then
		self.rightAnchor:SetPoint("BOTTOM", self.rightAnchor:GetParent(), "BOTTOM", 0, self.offsets.tab_bottom or 0)
		self.rightAnchor:SetPoint("RIGHT", self.rightAnchor:GetParent(), "RIGHT", self.offsets.tab_right or 0, 0)
		self.rightAnchor:SetPoint("TOP", self.rightAnchor:GetParent())
	end
	self:UpdateParentMinSize()
end

function TableTop:LayoutTabs(tabs, template, clickFunc)
	if not self.inited then error("You must call TableTop:Init() first.") end
	local parent = self.headerParent
	local basename = parent:GetName() .. "Tab"
	local lastFrame = nil
	self.tabData = tabs
	self.offsets = self.offsets or {}

	self.leftAnchor = CreateFrame("Frame", basename .. "LeftAnchor", parent)
	self.leftAnchor:SetPoint("LEFT", parent, "LEFT", self.offsets.tab_left or 0, 0)
	self.leftAnchor:SetPoint("TOP", parent, "TOP", 0, self.offsets.tab_top or 0)
	self.leftAnchor:SetPoint("BOTTOM", parent)
	self.leftAnchor:SetWidth(1)

	self.rightAnchor = CreateFrame("Frame", basename .. "RightAnchor", parent)

	self.rightAnchor:SetPoint("BOTTOM", parent, "BOTTOM", 0, self.offsets.tab_bottom or 0)
	self.rightAnchor:SetPoint("RIGHT", parent, "RIGHT", self.offsets.tab_right or 0, 0)
	self.rightAnchor:SetPoint("TOP", parent)
	self.rightAnchor:SetWidth(1)

	for i=1, table.getn(tabs) do
		local tab = tabs[i]
		if not tab.key then
			tab.key = tab.name
		end
		self.DragHandleWidth = 10
		local name = basename .. i
		local dname = basename .. "Drag" .. i
		local f = CreateFrame("Button", name, parent, template)
		self.clickFunc = clickFunc
		f:SetScript("OnClick", function() self:TabOnClick(true) end)
		f:SetScript("OnEnter", function() self:TabOnEnter() end)
		f:SetScript("OnLeave", function() self:TabOnLeave() end)
		f.lib = self
		f:SetText(tab.name)
		if TT_IsTBC() then
			f:RegisterForClicks("AnyUp")
		else
			f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
		end
		self.updateFunc = updateFunc
		if not tab.x then
			tab.x = 0
		end
		f.tabdata = tab
		
		if not self.db.tabStates[tab.key] and tab.hidden then
			self.db.tabStates[tab.key] = true
		end
		
		f.isFirst = i == 1
		f.isLast = i == table.getn(tabs)
		tinsert(self.TabFrames, f)
		if not lastFrame then
			f:SetPoint("LEFT", self.leftAnchor, "LEFT", tab.x, 0)
		else			
			f:SetPoint("LEFT", lastFrame, "RIGHT", tab.x, 0)
		end
		if f.isLast then
			f:SetPoint("RIGHT", self.rightAnchor, "LEFT", tab.x, 0)
		end

		lastFrame = f
		
		if not f.isLast then
			if not tab.fixed then
				local df = CreateFrame("Button", dname, parent, template)
				if TT_IsTBC() then
					df:RegisterForClicks("AnyUp")
				else
					df:RegisterForClicks("LeftButtonUp", "RightButtonUp")
				end
				df.index = i
				df.lib = self
				df.OldOnMouseDown = df:GetScript("OnMouseDown")
				df.OldOnMouseUp = df:GetScript("OnMouseUp")
				df.OldOnUpdate = df:GetScript("OnUpdate")
				df:SetScript("OnMouseDown", function() self:TabOnMouseDown(this) end)
				df:SetScript("OnMouseUp", function() self:TabOnMouseUp(this) end)
				df:SetScript("OnClick", function() self:TabOnClick(false) end)
				-- df:SetScript("OnUpdate", function() self:TabOnUpdate(this) end)
				df:SetPoint("LEFT", f, "RIGHT", 0, 0)
				df:SetWidth(self.DragHandleWidth)
				df:SetText("|")
				df.parent = f
				lastFrame = df
				f.dragHandle = df
			end		
		end
		if tab.width then
			f:SetWidth(tab.width)
			f.tabdata.minwidth = tab.width
		elseif tab.minwidth then
			f:SetWidth(tab.minwidth)
		else
			f.tabdata.minwidth = 50
			f:SetWidth(tab.minwidth)
		end
	end
	self:UpdateParentMinSize()
end

function TableTop:LayoutInit()
	for k,v in pairs(self.TabFrames) do
		if self.db.tabStates[v.tabdata.key] then
			self:ToggleColumn(v.tabdata.key, true)
		end
		if self.db.tabWidths[v.tabdata.key] then
			v:SetWidth(self.db.tabWidths[v.tabdata.key])
		end
	end
	self:SetupRows()
	self:ResizeTabContainer()
end

-- To be called when the window is resized.
function TableTop:ResizeTabContainer()
	local index = 0
	for i = 0, table.getn(self.TabFrames)-1 do
		if not self.TabFrames[table.getn(self.TabFrames)-i].tabdata.hidden then
			index = table.getn(self.TabFrames)-i
			break
		end
	end
	local lastFrame = self.TabFrames[index]
	local secLastFrame = self.TabFrames[index - 1]
	if not lastFrame then return end
	if not secLastFrame then return end
	local dragger = secLastFrame.dragHandle
	local fw = self:GetFrameWidth(lastFrame)
	if fw < lastFrame.tabdata.minwidth then
		self:ResizeTab((fw - lastFrame.tabdata.minwidth), dragger, true)
	end
end

function TableTop.ShowRow(self)
	self.hidden = false
	self["old_show"](self)
end

function TableTop.HideRow(self)
	self.hidden = true
	self["old_hide"](self)
end

function TableTop.IsVisible(self)
	return not self.hidden
end

function TableTop:ResizeTab(amount, startTab, skipResize)
	if not self.inited then error("You must call TableTop:Init() first.") end
	local leftTab = self.TabFrames[startTab.index]
	local rightTab = self.TabFrames[startTab.index+1]
	amount = amount * (1/UIParent:GetEffectiveScale())
	
	local min = 0
	local max = 0
	local growTab = nil
	local rawamt = amount
	if amount <= 0 then
		min = 1
		max = startTab.index
		growTab = rightTab
	elseif amount > 0 then
		min = startTab.index + 1
		max = table.getn(self.TabFrames)
		growTab = leftTab
	else
		return
	end
	
	local totalMoved = 0
	local tab = nil
	for i = min,max do
		local doBreak = true
		if rawamt < 0 then
			for j = 1, table.getn(self.TabFrames) do
				tab = self.TabFrames[max-i+j]
				if not tab.tabdata.hidden then break end
			end
		else
			for j = 1, table.getn(self.TabFrames) do
				tab = self.TabFrames[i+j-1]
				if not tab.tabdata.hidden then break end
			end
		end
		if not tab.tabdata.fixed then
			local tabWidth = self:GetFrameWidth(tab)
			if tabWidth < tab.tabdata.minwidth then
				tab:SetWidth(tab.tabdata.minwidth)
				amount = amount + (tab.tabdata.minwidth - tabWidth)
				totalMoved = totalMoved + (tab.tabdata.minwidth - tabWidth)
				tabWidth = tab.tabdata.minwidth
			end
			local maxAvailable = tabWidth - tab.tabdata.minwidth
			local change = 0
			if math.abs(amount) >= maxAvailable + 0.5 then
  			tab:SetWidth(tab.tabdata.minwidth)
				if amount < 0 then
					amount = amount + maxAvailable
				else
					amount = amount - maxAvailable
				end
				totalMoved = totalMoved + maxAvailable
			else
				if tab.tabdata.name == "Owner" then
					
				end
				tab:SetWidth(tabWidth - math.abs(amount))
				totalMoved = totalMoved + amount
				amount = 0
				break
			end
		end
		if tab.isLast then break end
	end
	if amount == 0 then
		growTab:SetWidth(self:GetFrameWidth(growTab) + math.abs(rawamt))
	elseif not (growTab.isFirst or growTab.isLast) then
		growTab:SetWidth(self:GetFrameWidth(growTab) + math.abs(totalMoved))
	end
	self:SetupRows()
	if not skipResize then
		self:ResizeTabContainer()
	end
	for k,v in pairs(self.TabFrames) do
		self.db.tabWidths[v.tabdata.key] = self:GetFrameWidth(v)
	end
end

function TableTop:GetFrameWidth(v)
	local w = v:GetWidth()
	if not v.isLast then return w end
	return v:GetRight() - v:GetLeft()
end

function TableTop:RefreshColumns(forceReLayOut)
	if not self.inited then error("You must call TableTop:Init() first.") end
	local lastVis = nil
	local haveFirst = nil
	local haveLast = nil
	local firstFrame = nil
	local lastFrame = nil
	for k,v in pairs(self.tabData) do
		local f = self.TabFrames[k]
		if not lastVis then
			lastVis = f:GetParent()
		end
		if v.hidden then
			f:Hide()
			if f.dragHandle then
				f.dragHandle:Hide()
			end
			f:SetWidth(f.tabdata.minwidth)
			if f.isLast then
				if lastVis.dragHandle then
					lastVis.dragHandle:Show()
				end
			end
		else
			f:Show()
			f:ClearAllPoints()
			if f.dragHandle then
				f.dragHandle:Show()
			end
			firstFrame = firstFrame or f
			if haveFirst then f.isFirst = false end
			f.isLast = false
			if f.isFirst then
				haveFirst = true
				f:SetPoint("LEFT", self.leftAnchor, "LEFT")
			else
				if lastVis.dragHandle then
					f:SetPoint("LEFT", lastVis.dragHandle, "RIGHT")
				else
					f:SetPoint("LEFT", lastVis, "RIGHT")
				end
			end
			if f.isLast then
				haveLast = true
				f:SetPoint("RIGHT", self.rightAnchor, "LEFT")
			end
			lastVis = f
			lastFrame = f
			if f.dragHandle then
				lastVis = f.dragHandle
			end
		end
	end
	if lastVis.parent then		-- is a drag frame
		lastVis:Hide()
		lastVis = lastVis.parent
		lastFrame = lastVis
	end
	if not lastFrame then return end
	lastFrame.isLast = true
	if not haveFirst then
		firstFrame.isFirst = true
		firstFrame:SetPoint("LEFT", self.leftAnchor, "LEFT")
	end
	if not haveLast then
		lastFrame.isLast = true
		lastFrame:SetPoint("RIGHT", self.rightAnchor, "LEFT")
	end
	if forceReLayOut then
			self:ResizeTabContainer()
	end
	self:SetupRows()
end

--[[ Row management ]]--

function TableTop:SetupRows(opts)
	if not self.inited then error("You must call TableTop:Init() first.") end
	if not self.TabFrames then
		return
	end
	if not opts then opts = self.row_opts
	else self.rows_setup = true end
	if not self.rows_setup then return end
	if not opts.parent then return end
	parent = opts.parent
	num	= opts.num
	height = opts.height
	template = opts.template
		
	if not parent or not num or not height then 
		error("Can't find data needed for SetupRows")
	end
	self.row_opts = opts
	
	local name = parent:GetName() .. "RowItem"
	local lastItem = parent
	if not self.maxRows then self.maxRows = 0 end
	for i = 1, self.maxRows do
		getglobal(name .. i):old_hide()
	end
	if num > self.maxRows then
		self.maxRows = num
	end
	
	local data = nil
	for i = 1, num do
		local item = getglobal(name .. i)
		if not item then
			item = CreateFrame("Button", name .. i, parent, template)
			item.old_show = item.Show
			item.old_hide = item.Hide
			item.old_vis = item.IsVisible
			item.hidden = false
			item.Hide = self.HideRow
			item.Show = self.ShowRow
			item.IsVisible = self.IsVisible
			
			if not item:GetHighlightTexture() then
				item:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight")
				item:GetHighlightTexture():SetBlendMode("ADD")
				item:GetHighlightTexture():SetTexCoord(0.18, 0.85, 0.1, 0.9)
			end

			data = item:CreateFontString(name .. i .. "SECTION_HEADER", "ARTWORK", "GameFontHighlightSmallOutline")
			data:ClearAllPoints()
			data:SetPoint("TOPLEFT", item, "TOPLEFT", 20, 0)
			data:SetPoint("BOTTOMRIGHT", item)
			data:SetJustifyH("LEFT")
			data:Hide()
			for k,v in pairs(self.tabData) do
				local dragWidth = 0
				if self.TabFrames[k].dragHandle then
					dragWidth = self.DragHandleWidth
				end
				if not v.nocolumn then
					local data = nil
					if v.type == "text" or not v.type then
						local inherit = v.inherits
						if not v.inherits then
							inherit = "GameFontHighlightSmallOutline"
						end
						data = item:CreateFontString(name .. i .. v.key, "ARTWORK", inherit)
						data:ClearAllPoints()
						data:SetJustifyH("LEFT")
						if lastData == item then
							data:SetPoint("LEFT", item)
						else
							data:SetPoint("LEFT", lastData, "RIGHT")
						end
						data:SetHeight(21)
					elseif v.type == "button" then
						local inherit = v.inherits
						if not v.inherits then
							inherit = "GameFontHighlight"
						end
						data = CreateFrame("Button", name .. i .. v.key, item, inherit)
						data:SetFrameLevel(item:GetFrameLevel()+2)
						data:ClearAllPoints()
						data:SetPoint("TOP", item)
						data:SetPoint("BOTTOM", item)
						if not lastData then
							data:SetPoint("LEFT", item)
						else
							data:SetPoint("LEFT", lastData, "RIGHT")
						end
					end
					if v.hidden then
						data:Hide()
					else
						data:Show()
					end
					lastData = data;
				end
			end
		end
		local lastVis = nil
		for k,v in pairs(self.tabData) do
			local dragWidth = 0
			if not v.hidden then
				if self.TabFrames[k].dragHandle then
					dragWidth = self.DragHandleWidth
				end
		  	
				local data = getglobal(name .. i .. v.key)
				if data and not v.nocolumn then
					data:SetWidth(self:GetFrameWidth(self.TabFrames[k]) + dragWidth)
					if not lastVis then
						data:SetPoint("LEFT", item)
					else
						data:SetPoint("LEFT", lastVis, "RIGHT")
					end
					lastVis = data
				elseif lastVis then
					lastVis:SetWidth(self:GetFrameWidth(lastVis) + self:GetFrameWidth(self.TabFrames[k]) + dragWidth)
				end
			end
		end
		item:SetHeight(height)
		item:ClearAllPoints()

		local x = 0
		local y = 0
		if i == 1 then
			x = self.offsets.data_left or 0
			y = self.offsets.data_top or 0
		end
		if i == table.getn(self.tabData) then
			x = self.offsets.data_right or 0
			y = self.offsets.data_bottom or 0
		end

		if lastItem == parent then
			item:SetPoint("TOPLEFT", lastItem, "TOPLEFT", x, y)
		else
			item:SetPoint("TOPLEFT", lastItem, "BOTTOMLEFT", 0, 0)
		end
		item:SetPoint("RIGHT", parent, "RIGHT", x, y)
		lastItem = item
		if item:IsVisible() then 
			item:Show()
		end
	end
end

function TableTop:ToggleColumn(key, state, avoidCascade)
	if not self.inited then error("You must call TableTop:Init() first.") end
	local parent = self.row_opts.parent
	if not parent then error("You must call TableTop:SetupRows() first.") end
	local name = parent:GetName() .. "RowItem"
	local toggled = false            
	local stillHaveVisible = false
	for k,v in pairs(self.tabData) do
		if not v.hidden and v.key ~= key then
			stillHaveVisible = true
		end
	end
	if not stillHaveVisible then return end
	for k,v in pairs(self.tabData) do
		if toggled and not avoidCascade then
			if v.nocolumn and v.hidden ~= state then
				self:ToggleColumn(v.key, state, true)
			elseif not v.nocolumn then
				break
			end
		end
	
		if v.key == key then
			v.hidden = state
			for i = 1, self.maxRows do
				local data = getglobal(name .. i .. key)
				if data then
					if v.hidden then
						data:Hide()
					else
						data:Show()
					end
				end
			end
			toggled = true
		end
	end
	self:RefreshColumns(true)
end

function TableTop:GetColumn(name)
	if not self.inited then error("You must call TableTop:Init() first.") end
	for k,v in pairs(self.TabFrames) do
		if v.tabdata.key == name then
			return v
		end
	end
	return nil
end

function TableTop:GetRow(i)
	if not self.inited then error("You must call TableTop:Init() first.") end
	for k,v in pairs(self.tabData) do
		local f = getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. v.key)
		if f and not v.hidden then f:Show() end
	end
	getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. "SECTION_HEADER"):Hide()
	return getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i)
end

function TableTop:GetSectionHeader(i)
	if not self.inited then error("You must call TableTop:Init() first.") end
	for k,v in pairs(self.tabData) do
		local f = getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. v.key)
		if f then f:Hide() end
	end
	getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. "SECTION_HEADER"):Show()
	return getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. "SECTION_HEADER")
end

function TableTop:GetRowField(i, field)
	if not self.inited then error("You must call TableTop:Init() first.") end
	getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. "SECTION_HEADER"):Hide()
	local f = getglobal(self.row_opts.parent:GetName() .. "RowItem" .. i .. field)
	for k,v in pairs(self.tabData) do
		if v.key == field and not v.hidden then
			f:Show()
		elseif v.key == field then
			f:Hide()
		end
	end
	return f
end

--[[ Library stuff ]]--

local function activate(self, oldLib, oldDeactivate)
	self.TabFrames = {}
	if oldLib then
		self.TabFrames = oldLib.TabFrames
	end
	if oldDeactivate then
        oldDeactivate(oldLib)
    end	
end

AceLibrary:Register(TableTop, MAJOR_VERSION, MINOR_VERSION, activate)
TableTop = nil