--[[
Name: LibGraph-2.0
Revision: $Rev: 62 $
Author(s): Cryect (cryect@gmail.com), Xinhuan
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/wiki/GraphLib
SVN: http://svn.wowace.com/root/trunk/GraphLib/
Description: Allows for easy creation of graphs
]]

--Thanks to Nelson Minar for catching several errors where width was being used instead of height (damn copy and paste >_>)

local major = "LibGraph-2.0"
local minor = 90000 + tonumber(("$Revision: 62 $"):match("(%d+)"))


--Search for just Addon\\ at the front since the interface part often gets trimmed
--Do this before anything else, so if it errors, any existing loaded copy of LibGraph-2.0
--doesn't get modified with a newer revision (this one)
local TextureDirectory
do
	local path = string.match(debugstack(1, 1, 0), "AddOns[\\/](.+)LibGraph%-2%.0%.lua")

	if path then
		TextureDirectory = path
	else
		error(major.." cannot determine the folder it is located in because the path is too long and got truncated in the debugstack(1, 1, 0) function call")
	end
end

if not LibStub then error(major .. " requires LibStub") end

local lib, oldLibMinor = LibStub:NewLibrary(major, minor)
if not lib then return end

--manually set the path in case want to use custom textures
function lib:SetTextureDirectory(path)
	assert(type(path) == "string", "Usage: lib:SetTextureDirectory(string: path)")
	TextureDirectory = path
end

local GraphFunctions = {}

local gsub = gsub
local ipairs = ipairs
local pairs = pairs
local sqrt = sqrt
local table = table
local tinsert = tinsert
local tremove = tremove
local type = type
local math_max = math.max
local math_min = math.min
local math_ceil = math.ceil
local math_pi = math.pi
local math_floor = math.floor
local math_pow = math.pow
local math_random = math.random
local math_cos = math.cos
local math_sin = math.sin
local math_deg = math.deg
local math_atan = math.atan
local math_abs = math.abs
local math_fmod = math.fmod
local math_huge = math.huge

local CreateFrame = CreateFrame
local GetCursorPosition = GetCursorPosition
local GetTime = GetTime
local MouseIsOver = MouseIsOver
local UnitHealth = UnitHealth

local UIParent = UIParent

local DEFAULT_CHAT_FRAME = DEFAULT_CHAT_FRAME

-- lib upgrade stuff
lib.RegisteredGraphRealtime		= lib.RegisteredGraphRealtime or {}
lib.RegisteredGraphLine			= lib.RegisteredGraphLine or {}
lib.RegisteredGraphScatterPlot	= lib.RegisteredGraphScatterPlot or {}
lib.RegisteredGraphPieChart		= lib.RegisteredGraphPieChart or {}

local function SetTextureGradient(texture, orientation, minR, minG, minB, minA, maxR, maxG, maxB, maxA)
	if texture.SetGradientAlpha then
		texture:SetGradientAlpha(orientation, minR, minG, minB, minA, maxR, maxG, maxB, maxA)
	else
		local minColor = { r = minR, g = minG, b = minB, a = minA }
		local maxColor = { r = maxR, g = maxG, b = maxB, a = maxA }
		texture:SetGradient(orientation, minColor, maxColor)
	end
end

--------------------------------------------------------------------------------
--Graph Creation Functions
--------------------------------------------------------------------------------

--Realtime Graph
local function SetupGraphRealtimeFunctions(graph, upgrade)
	local self = lib

	--Set the various functions
	graph.SetXAxis = GraphFunctions.SetXAxis
	graph.SetYMax = GraphFunctions.SetYMax
	graph.AddTimeData = GraphFunctions.AddTimeData
	graph.OnUpdate = GraphFunctions.OnUpdateGraphRealtime
	graph.CreateGridlines = GraphFunctions.CreateGridlines
	graph.RefreshGraph = GraphFunctions.RefreshRealtimeGraph
	graph.SetAxisDrawing = GraphFunctions.SetAxisDrawing
	graph.SetGridSpacing = GraphFunctions.SetGridSpacing
	graph.SetAxisColor = GraphFunctions.SetAxisColor
	graph.SetGridColor = GraphFunctions.SetGridColor
	graph.SetGridColorSecondary = GraphFunctions.SetGridColorSecondary
	graph.SetGridSecondaryMultiple = GraphFunctions.SetGridSecondaryMultiple
	graph.SetFilterRadius = GraphFunctions.SetFilterRadius
	--graph.SetAutoscaleYAxis = GraphFunctions.SetAutoscaleYAxis
	graph.SetBarColors = GraphFunctions.SetBarColors
	graph.SetMode = GraphFunctions.SetMode
	graph.SetAutoScale = GraphFunctions.SetAutoScale
	if not upgrade then
		-- This is the original frame:SetWidth() and frame:SetHeight()
		-- standard frame functions
		graph.OldSetWidth = graph.SetWidth
		graph.OldSetHeight = graph.SetHeight
	end
	graph.SetWidth = GraphFunctions.RealtimeSetWidth
	graph.SetHeight = GraphFunctions.RealtimeSetHeight
	graph.SetBarColors = GraphFunctions.RealtimeSetColors
	graph.GetMaxValue = GraphFunctions.GetMaxValue
	graph.GetValue = GraphFunctions.RealtimeGetValue
	graph.SetUpdateLimit = GraphFunctions.SetUpdateLimit
	graph.SetDecay = GraphFunctions.SetDecay
	graph.SetMinMaxY = GraphFunctions.SetMinMaxY
	graph.AddBar = GraphFunctions.AddBar
	graph.SetYLabels = GraphFunctions.SetYLabels


	graph.DrawLine = self.DrawLine
	graph.DrawHLine = self.DrawHLine
	graph.DrawVLine = self.DrawVLine
	graph.HideLines = self.HideLines
	graph.HideFontStrings = GraphFunctions.HideFontStrings
	graph.FindFontString = GraphFunctions.FindFontString
	graph.SetBars = GraphFunctions.SetBars


	--Set the update function
	graph:SetScript("OnUpdate", graph.OnUpdate)
end

function lib:CreateGraphRealtime(name, parent, relative, relativeTo, offsetX, offsetY, Width, Height)
	local graph
	local i
	graph = CreateFrame("Frame", name, parent, BackdropTemplateMixin and "BackdropTemplate")

	Width = math_floor(Width)


	graph:SetPoint(relative, parent, relativeTo, offsetX, offsetY)
	graph:SetWidth(Width)
	graph:SetHeight(Height)
	graph:Show()

	--Create the bars
	graph.Bars = {}
	graph.BarsUsing = {}
	graph.BarNum = Width
	graph.Height = Height
	for i = 1, Width do
		local bar
		bar = CreateFrame("StatusBar", name.."Bar"..i, graph, BackdropTemplateMixin and "BackdropTemplate")--graph:CreateTexture(nil, "ARTWORK")
		bar:SetPoint("BOTTOMLEFT", graph, "BOTTOMLEFT", i - 1, 0)
		bar:SetHeight(Height)
		bar:SetWidth(1)
		bar:SetOrientation("VERTICAL")
		bar:SetMinMaxValues(0, 1)
		bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
		bar:GetStatusBarTexture():SetHorizTile(false)
		bar:GetStatusBarTexture():SetVertTile(false)

		local t = bar:GetStatusBarTexture()
		SetTextureGradient(t, "VERTICAL", 0.2, 0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 1.0)

		bar:Show()
		tinsert(graph.Bars, bar)
		tinsert(graph.BarsUsing, bar)
	end


	SetupGraphRealtimeFunctions(graph)


	--Initialize Data
	graph.GraphType = "REALTIME"
	graph.YMax = 60
	graph.YMin = 0
	graph.XMax = -0.75
	graph.XMin = -10
	graph.TimeRadius = 0.5
	graph.Mode = "FAST"
	graph.Filter = "RECT"
	graph.AxisColor = {1.0, 1.0, 1.0, 1.0}
	graph.GridColor = {0.5, 0.5, 0.5, 0.5}
	graph.BarColorTop = {1.0, 0.0, 0.0, 1.0}
	graph.BarColorBot = {0.2, 0.0, 0.0, 0.5}
	graph.AutoScale = false
	graph.Data = {}
	graph.MinMaxY = 0
	graph.CurVal = 0
	graph.LastDataTime = GetTime()

	graph.Textures = {}
	graph.TexturesUsed = {}

	graph.LimitUpdates = 0
	graph.NextUpdate = 0

	graph.BarHeight = {}
	graph.LastShift = GetTime()
	graph.BarWidth = (graph.XMax - graph.XMin) / graph.BarNum
	graph.DecaySet = 0.8
	graph.Decay = math_pow(graph.DecaySet, graph.BarWidth)
	graph.ExpNorm = 1 / (1 - graph.Decay)

	graph.FilterOverlap = math_max(math_ceil((graph.TimeRadius + graph.XMax) / graph.BarWidth), 0)
	for i = 1, graph.BarNum do
		graph.BarHeight[i] = 0
	end

	graph.TextFrame = CreateFrame("Frame", nil, graph)
	graph.TextFrame:SetAllPoints(graph)
	graph.TextFrame:SetFrameLevel(graph:GetFrameLevel() + 2)

	tinsert(self.RegisteredGraphRealtime, graph)
	return graph
end

--Line Graph
local function SetupGraphLineFunctions(graph)
	local self = lib

	--Set the various functions
	graph.SetXAxis = GraphFunctions.SetXAxis
	graph.SetYAxis = GraphFunctions.SetYAxis
	graph.AddDataSeries = GraphFunctions.AddDataSeries
	graph.AddFilledDataSeries = GraphFunctions.AddFilledDataSeries
	graph.ResetData = GraphFunctions.ResetData
	graph.RefreshGraph = GraphFunctions.RefreshLineGraph
	graph.CreateGridlines = GraphFunctions.CreateGridlines
	graph.SetAxisDrawing = GraphFunctions.SetAxisDrawing
	graph.SetGridSpacing = GraphFunctions.SetGridSpacing
	graph.SetAxisColor = GraphFunctions.SetAxisColor
	graph.SetGridColor = GraphFunctions.SetGridColor
	graph.SetGridColorSecondary = GraphFunctions.SetGridColorSecondary
	graph.SetGridSecondaryMultiple = GraphFunctions.SetGridSecondaryMultiple
	graph.SetAutoScale = GraphFunctions.SetAutoScale
	graph.SetYLabels = GraphFunctions.SetYLabels
	graph.OnUpdate = GraphFunctions.OnUpdateGraph

	graph.SetLineTexture = GraphFunctions.SetLineTexture
	graph.SetBorderSize = GraphFunctions.SetBorderSize
	
	graph.LockXMin = GraphFunctions.LockXMin
	graph.LockXMax = GraphFunctions.LockXMax
	graph.LockYMin = GraphFunctions.LockYMin
	graph.LockYMax = GraphFunctions.LockYMax


	graph.DrawLine = self.DrawLine
	graph.DrawHLine = self.DrawHLine
	graph.DrawVLine = self.DrawVLine
	graph.HideLines = self.HideLines
	graph.DrawBar = self.DrawBar
	graph.HideBars = self.HideBars
	graph.HideFontStrings = GraphFunctions.HideFontStrings
	graph.FindFontString = GraphFunctions.FindFontString

	--Set the update function
	graph:SetScript("OnUpdate", graph.OnUpdate)
end

--TODO: Clip lines with the bounds
function lib:CreateGraphLine(name, parent, relative, relativeTo, offsetX, offsetY, Width, Height)
	local graph
	local i
	graph = CreateFrame("Frame", name, parent, BackdropTemplateMixin and "BackdropTemplate")


	graph:SetPoint(relative, parent, relativeTo, offsetX, offsetY)
	graph:SetWidth(Width)
	graph:SetHeight(Height)
	graph:Show()


	SetupGraphLineFunctions(graph)


	graph.NeedsUpdate = false


	--Initialize Data
	graph.GraphType = "LINE"
	graph.YMax = 1
	graph.YMin = -1
	graph.XMax = 1
	graph.XMin = -1
	graph.AxisColor = {1.0, 1.0, 1.0, 1.0}
	graph.GridColor = {0.5, 0.5, 0.5, 0.5}
	graph.XGridInterval = 0.25
	graph.YGridInterval = 0.25
	graph.XAxisDrawn = true
	graph.YAxisDrawn = true

	graph.LockOnXMin = false
	graph.LockOnXMax = false
	graph.LockOnYMin = false
	graph.LockOnYMax = false
	graph.Data = {}
	graph.FilledData = {}
	graph.Textures = {}
	graph.TexturesUsed = {}
	graph.TextFrame = CreateFrame("Frame", nil, graph)
	graph.TextFrame:SetAllPoints(graph)


	tinsert(self.RegisteredGraphLine, graph)
	return graph
end


--Scatter Plot
local function SetupGraphScatterPlotFunctions(graph)
	local self = lib

	--Set the various functions
	graph.SetXAxis = GraphFunctions.SetXAxis
	graph.SetYAxis = GraphFunctions.SetYAxis
	graph.AddDataSeries = GraphFunctions.AddDataSeries
	graph.ResetData = GraphFunctions.ResetData
	graph.RefreshGraph = GraphFunctions.RefreshScatterPlot
	graph.CreateGridlines = GraphFunctions.CreateGridlines
	graph.OnUpdate = GraphFunctions.OnUpdateGraph

	graph.LinearRegression = GraphFunctions.LinearRegression
	graph.SetAxisDrawing = GraphFunctions.SetAxisDrawing
	graph.SetGridSpacing = GraphFunctions.SetGridSpacing
	graph.SetAxisColor = GraphFunctions.SetAxisColor
	graph.SetGridColor = GraphFunctions.SetGridColor
	graph.SetGridColorSecondary = GraphFunctions.SetGridColorSecondary
	graph.SetGridSecondaryMultiple = GraphFunctions.SetGridSecondaryMultiple
	graph.SetLinearFit = GraphFunctions.SetLinearFit
	graph.SetAutoScale = GraphFunctions.SetAutoScale
	graph.SetYLabels = GraphFunctions.SetYLabels

	graph.LockXMin = GraphFunctions.LockXMin
	graph.LockXMax = GraphFunctions.LockXMax
	graph.LockYMin = GraphFunctions.LockYMin
	graph.LockYMax = GraphFunctions.LockYMax

	graph.DrawLine = self.DrawLine
	graph.DrawHLine = self.DrawHLine
	graph.DrawVLine = self.DrawVLine
	graph.HideLines = self.HideLines
	graph.HideTextures = GraphFunctions.HideTextures
	graph.FindTexture = GraphFunctions.FindTexture
	graph.HideFontStrings = GraphFunctions.HideFontStrings
	graph.FindFontString = GraphFunctions.FindFontString

	--Set the update function
	graph:SetScript("OnUpdate", graph.OnUpdate)
end

function lib:CreateGraphScatterPlot(name, parent, relative, relativeTo, offsetX, offsetY, Width, Height)
	local graph
	local i
	graph = CreateFrame("Frame",name, parent, BackdropTemplateMixin and "BackdropTemplate")


	graph:SetPoint(relative, parent, relativeTo, offsetX, offsetY)
	graph:SetWidth(Width)
	graph:SetHeight(Height)
	graph:Show()


	SetupGraphScatterPlotFunctions(graph)


	graph.NeedsUpdate = false

	--Initialize Data
	graph.GraphType = "SCATTER"
	graph.YMax = 1
	graph.YMin = -1
	graph.XMax = 1
	graph.XMin = -1
	graph.AxisColor = {1.0, 1.0, 1.0, 1.0}
	graph.GridColor = {0.5, 0.5, 0.5, 0.5}
	graph.XGridInterval = 0.25
	graph.YGridInterval = 0.25
	graph.XAxisDrawn = true
	graph.YAxisDrawn = true
	graph.AutoScale = false
	graph.LinearFit = false
	graph.LockOnXMin = false
	graph.LockOnXMax = false
	graph.LockOnYMin = false
	graph.LockOnYMax = false
	graph.Data = {}
	graph.Textures = {}
	graph.TexturesUsed = {}

	graph.TextFrame = CreateFrame("Frame", nil, graph)
	graph.TextFrame:SetAllPoints(graph)

	tinsert(self.RegisteredGraphScatterPlot, graph)
	return graph
end

--Pie Chart
local function SetupGraphPieChartFunctions(graph)
	local self = lib

	--Set the various functions
	graph.AddPie = GraphFunctions.AddPie
	graph.CompletePie = GraphFunctions.CompletePie
	graph.ResetPie = GraphFunctions.ResetPie

	graph.DrawLine = self.DrawLine
	graph.DrawHLine = self.DrawHLine
	graph.DrawVLine = self.DrawVLine
	graph.DrawLinePie = GraphFunctions.DrawLinePie
	graph.HideLines = self.HideLines
	graph.HideTextures = GraphFunctions.HideTextures
	graph.FindTexture = GraphFunctions.FindTexture
	graph.OnUpdate = GraphFunctions.PieChart_OnUpdate
	graph.SetSelectionFunc = GraphFunctions.SetSelectionFunc

	graph:SetScript("OnUpdate", graph.OnUpdate)
end

function lib:CreateGraphPieChart(name, parent, relative, relativeTo, offsetX, offsetY, Width, Height)
	local graph
	local i
	graph = CreateFrame("Frame",name, parent, BackdropTemplateMixin and "BackdropTemplate")


	graph:SetPoint(relative, parent, relativeTo, offsetX, offsetY)
	graph:SetWidth(Width)
	graph:SetHeight(Height)
	graph:Show()


	SetupGraphPieChartFunctions(graph)


	--Initialize Data
	graph.GraphType = "PIE"
	graph.PieUsed = 0
	graph.PercentOn = 0
	graph.Remaining = 0
	graph.Textures = {}
	graph.Ratio = Width / Height
	graph.Radius = 0.88 * (Width / 2)
	graph.Radius = graph.Radius * graph.Radius
	graph.Sections = {}
	graph.Textures = {}
	graph.TexturesUsed = {}
	graph.LastSection = nil
	graph.onColor = 1
	graph.TotalSections = 0

	tinsert(self.RegisteredGraphPieChart, graph)
	return graph
end


-------------------------------------------------------------------------------
--Functions for Realtime Graphs
-------------------------------------------------------------------------------

--AddTimeData - Adds a data value to the realtime graph at this moment in time
function GraphFunctions:AddTimeData(value)
	if type(value) ~= "number" then
		return
	end

	local t = {}
	t.Time = GetTime()
	self.LastDataTime = t.Time
	t.Value = value
	tinsert(self.Data, t)
end

--RefreshRealtimeGraph - Refreshes the gridlines for the realtime graph
function GraphFunctions:RefreshRealtimeGraph()
	self:HideLines(self)
	self:CreateGridlines()
end

--SetFilterRadius - controls the radius of the filter
function GraphFunctions:SetFilterRadius(radius)
	self.TimeRadius = radius
end

--SetAutoscaleYAxis - If enabled the maximum y axis is adjusted to be 25% more than the max value
function GraphFunctions:SetAutoscaleYAxis(scale)
	self.AutoScale = scale
end

--SetBarColors - 
function GraphFunctions:SetBarColors(BotColor, TopColor)
	local Temp
	if BotColor.r then
		Temp = BotColor
		BotColor = {Temp.r, Temp.g, Temp.b, Temp.a}
	end
	if TopColor.r then
		Temp = TopColor
		TopColor = {Temp.r, Temp.g, Temp.b, Temp.a}
	end
	for i = 1, self.BarNum do
		local t = self.Bars[i]:GetStatusBarTexture()
		SetTextureGradient(t, "VERTICAL", BotColor[1], BotColor[2], BotColor[3], BotColor[4], TopColor[1], TopColor[2], TopColor[3], TopColor[4])
	end
end

function GraphFunctions:SetMode(mode)
	self.Mode = mode

	if mode ~= "SLOW" then
		self.LastShift = GetTime() + self.XMin
	end
end

function GraphFunctions:RealtimeSetColors(BotColor, TopColor)
	local Temp
	if BotColor.r then
		Temp = BotColor
		BotColor = {Temp.r, Temp.g, Temp.b, Temp.a}
	end
	if TopColor.r then
		Temp = TopColor
		TopColor = {Temp.r, Temp.g, Temp.b, Temp.a}
	end
	self.BarColorBot = BotColor
	self.BarColorTop = TopColor
	for _, v in pairs(self.Bars) do
		SetTextureGradient(v:GetStatusBarTexture(), "VERTICAL", self.BarColorBot[1], self.BarColorBot[2], self.BarColorBot[3], self.BarColorBot[4], self.BarColorTop[1], self.BarColorTop[2], self.BarColorTop[3], self.BarColorTop[4])
	end
end

function GraphFunctions:RealtimeSetWidth(Width)
	Width = math_floor(Width)

	if Width == self.BarNum then
		return
	end

	self.BarNum = Width
	for i = 1, Width do
		if type(self.Bars[i]) == "nil" then
			local bar
			bar = CreateFrame("StatusBar", self:GetName().."Bar"..i, self)
			bar:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", i - 1, 0)
			bar:SetHeight(self.Height)
			bar:SetWidth(1)
			bar:SetOrientation("VERTICAL")
			bar:SetMinMaxValues(0, 1)
			bar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
			bar:GetStatusBarTexture():SetHorizTile(false)
			bar:GetStatusBarTexture():SetVertTile(false)

			local t = bar:GetStatusBarTexture()
			SetTextureGradient(t, "VERTICAL", self.BarColorBot[1], self.BarColorBot[2], self.BarColorBot[3], self.BarColorBot[4], self.BarColorTop[1], self.BarColorTop[2], self.BarColorTop[3], self.BarColorTop[4])

			tinsert(self.Bars, bar)
		else
			self.Bars[i]:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", i - 1, 0)
		end
		self.BarHeight[i] = 0
	end

	local SizeOfBarsUsed = table.maxn(self.BarsUsing)

	if Width > SizeOfBarsUsed then
		for i = SizeOfBarsUsed + 1, Width do
			tinsert(self.BarsUsing, self.Bars[i])
			self.Bars[i]:Show()
		end
	elseif Width < SizeOfBarsUsed then
		for i = Width + 1, SizeOfBarsUsed do
			tremove(self.BarsUsing, Width + 1)
			self.Bars[i]:Hide()
		end
	end

	self.BarWidth = (self.XMax - self.XMin) / self.BarNum
	self.Decay = math_pow(self.DecaySet, self.BarWidth)
	self.ExpNorm = 1 / (1 - self.Decay) / 0.95 --Actually a finite geometric series


	self:OldSetWidth(Width)
	self:RefreshGraph()
end

function GraphFunctions:RealtimeSetHeight(Height)
	self.Height = Height

	for i = 1, self.BarNum do
		--self.Bars[i]:Hide()
		self.Bars[i]:SetValue(0)
		self.Bars[i]:SetHeight(self.Height)
	end

	self:OldSetHeight(Height)
	self:RefreshGraph()
end

function GraphFunctions:GetMaxValue()
	--Is there any data that could possibly be not zero?
	if self.LastDataTime < (self.LastShift + self.XMin - self.TimeRadius) then
		return 0
	end

	local MaxY = 0

	for i = 1, self.BarNum do
		MaxY = math_max(MaxY, self.BarHeight[i])
	end

	return MaxY
end



function GraphFunctions:RealtimeGetValue(Time)
	local Bar
	if Time < self.XMin or Time > self.XMax then
		return 0
	end

	Bar = math_min(math_max(math_floor(self.BarNum * (Time - self.XMin) / (self.XMax - self.XMin) + 0.5), 1), self.BarNum)

	return self.BarHeight[Bar]
end

function GraphFunctions:SetUpdateLimit(Time)
	self.LimitUpdates = Time
end

function GraphFunctions:SetDecay(decay) 
	self.DecaySet = decay
	self.Decay = math_pow(self.DecaySet, self.BarWidth)
	self.ExpNorm = 1 / (1 - self.Decay) / 0.95 --Actually a finite geometric series (divide 0.96 instead of 1 since seems doesn't quite work right)
end

function GraphFunctions:AddBar(value)
	for i = 1, self.BarNum - 1 do
		self.BarHeight[i] = self.BarHeight[i + 1]
	end
	self.BarHeight[self.BarNum] = value
	self.AddedBar = true
end

function GraphFunctions:SetBars()
	local YHeight = self.YMax - self.YMin

	for i, bar in pairs(self.BarsUsing) do
		local h
		h = (self.BarHeight[i] - self.YMin) / YHeight

		bar:SetValue(h)
	end
end


-------------------------------------------------------------------------------
--Functions for Line Graph Data
-------------------------------------------------------------------------------

function GraphFunctions:AddDataSeries(points, color, n2, linetexture)
	local data
	--Make sure there is data points
	if not points then
		return
	end

	data = points
	if n2 == nil then
		n2 = false
	end
	if n2 or (table.getn(points) == 2 and table.getn(points[1]) ~= 2) then
		data = {}
		for k, v in ipairs(points[1]) do
			tinsert(data, {v, points[2][k]})
		end
	end

	if linetexture then
		if not linetexture:find ("\\") and not linetexture:find ("//") then 
			linetexture = TextureDirectory..linetexture
		end
	end
	
	tinsert(self.Data,{Points = data; Color = color; LineTexture=linetexture})

	self.NeedsUpdate = true
end

function GraphFunctions:AddFilledDataSeries(points, color, n2)
	local data
	--Make sure there is data points
	if not points or #points == 0 then
		return
	end

	data = points
	if n2 == nil then
		n2 = false
	end

	if n2 or (table.getn(points) == 2 and table.getn(points[1]) ~= 2) then
		data = {}
		for k, v in ipairs(points[1]) do
			tinsert(data, {v, points[2][k]})
		end
	end

	tinsert(self.FilledData, {Points = data; Color = color})

	self.NeedsUpdate = true
end


function GraphFunctions:ResetData()
	self.Data = {}

	if self.FilledData then
		self.FilledData = {}
	end

	self.NeedsUpdate = true
end

function GraphFunctions:SetLinearFit(fit)
	self.LinearFit = fit

	self.NeedsUpdate = true
end



function GraphFunctions:HideTextures()
	local k = #self.TexturesUsed
	while k > 0 do
		self.Textures[#self.Textures + 1] = self.TexturesUsed[k]
		self.TexturesUsed[k]:Hide()
		self.TexturesUsed[k] = nil
		k = k - 1
	end
end

--Make sure to show a texture after you grab it or its free for anyone else to grab
function GraphFunctions:FindTexture()
	local t
	if #self.Textures > 0 then
		t = self.Textures[#self.Textures]
		self.TexturesUsed[#self.TexturesUsed + 1] = t
		self.Textures[#self.Textures] = nil
		return t
	end
	local g = self:CreateTexture(nil, "BACKGROUND")
	self.TexturesUsed[#self.TexturesUsed + 1] = g
	return g
end

function GraphFunctions:HideFontStrings()
	if not self.FontStrings then
		self.FontStrings = {}
	end
	for k, t in pairs(self.FontStrings) do
		t:Hide()
	end
end

--Make sure to show a fontstring after you grab it or its free for anyone else to grab
function GraphFunctions:FindFontString()
	for k, t in pairs(self.FontStrings) do
		if not t:IsShown() then
			return t
		end
	end
	local g

	if self.TextFrame then
		g = self.TextFrame:CreateFontString(nil, "OVERLAY")
	else
		g = self:CreateFontString(nil, "OVERLAY")
	end
	tinsert(self.FontStrings, g)
	return g
end

--Linear Regression via Least Squares
function GraphFunctions:LinearRegression(data)
	local alpha, beta
	local n, SX, SY, SXX, SXY = 0, 0, 0, 0, 0

	for k, v in pairs(data) do
		n = n + 1

		SX = SX + v[1]
		SXX = SXX + v[1] * v[1]
		SY = SY + v[2]
		SXY = SXY + v[1] * v[2]
	end

	beta = (n * SXY - SX * SY) / (n * SXX - SX * SX)
	alpha = (SY - beta * SX) / n

	return alpha, beta
end


-------------------------------------------------------------------------------
--Functions for Pie Chart
-------------------------------------------------------------------------------

local PiePieces = {
	"1-2",
	"1-4",
	"1-8",
	"1-16",
	"1-32",
	"1-64",
	"1-128"
}

--26 Colors
local ColorTable = {
	{0.9, 0.1, 0.1},
	{0.1, 0.9, 0.1},
	{0.1, 0.1, 0.9},
	{0.9, 0.9, 0.1},
	{0.9, 0.1, 0.9},
	{0.1, 0.9, 0.9},
	{0.9, 0.9, 0.9},
	{0.5, 0.1, 0.1},
	{0.1, 0.5, 0.1},
	{0.1, 0.1, 0.5},
	{0.5, 0.5, 0.1},
	{0.5, 0.1, 0.5},
	{0.1, 0.5, 0.5},
	{0.5, 0.5, 0.5},
	{0.75, 0.15, 0.15},
	{0.15, 0.75, 0.15},
	{0.15, 0.15, 0.75},
	{0.75, 0.75, 0.15},
	{0.75, 0.15, 0.75},
	{0.15, 0.75, 0.75},
	{0.9, 0.5, 0.1},
	{0.1, 0.5, 0.9},
	{0.9, 0.1, 0.5},
	{0.5, 0.9, 0.1},
	{0.5, 0.1, 0.9},
	{0.1, 0.9, 0.5},
}

function GraphFunctions:AddPie(Percent, Color)
	local PiePercent = self.PercentOn

	local CurPiece = 50
	local Angle = 180
	local CurAngle = PiePercent * 360 / 100

	self.TotalSections = self.TotalSections + 1
	if type(self.Sections[self.TotalSections]) ~= "table" then
		self.Sections[self.TotalSections] = {}
	end

	local Section = self.Sections[self.TotalSections]
	Section.Textures = {}

	if type(Color) ~= "table" then
		if self.onColor <= table.maxn(ColorTable) then
			Color = ColorTable[self.onColor]
		else
			Color = {math_random(), math_random(), math_random()}
		end
		self.onColor = self.onColor + 1
	end

	if PiePercent == 0 then
		self:DrawLinePie(0)
	end

	Percent = Percent + self.Remaining
	local LastPiece = 0
	for k, v in pairs(PiePieces) do
		if (Percent + 0.1) > CurPiece then
			local t = self:FindTexture()
			t:SetTexture(TextureDirectory..v)
			t:ClearAllPoints()
			t:SetPoint("CENTER", self, "CENTER", 0, 0)
			t:SetHeight(self:GetHeight())
			t:SetWidth(self:GetWidth())
			GraphFunctions:RotateTexture(t, CurAngle)
			t:Show()

			t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
			Percent = Percent - CurPiece
			PiePercent = PiePercent + CurPiece
			CurAngle = CurAngle + Angle

			tinsert(Section.Textures, t)

			if k == 7 then
				LastPiece = 0.09
			end
		end
		CurPiece = CurPiece / 2
		Angle = Angle / 2
	end

	--Finish adding section data
	Section.Color = Color
	Section.Angle = CurAngle

	self:DrawLinePie((PiePercent + LastPiece) * 360 / 100)
	self.PercentOn = PiePercent
	self.Remaining = Percent

	return Color
end

function GraphFunctions:CompletePie(Color)
	local Percent = 100 - self.PercentOn
	local PiePercent = self.PercentOn

	local CurPiece = 50
	local Angle = 180
	local CurAngle = PiePercent * 360 / 100

	self.TotalSections = self.TotalSections + 1
	if not self.Sections[self.TotalSections] then
		self.Sections[self.TotalSections] = {}
	end

	local Section = self.Sections[self.TotalSections]
	Section.Textures = {}

	if type(Color) ~= "table" then
		if self.onColor <= table.maxn(ColorTable) then
			Color = ColorTable[self.onColor]
		else
			Color = {math_random(), math_random(), math_random()}
		end
		self.onColor = self.onColor + 1
	end

	Percent = Percent + self.Remaining
	if PiePercent ~= 0 then
		for k, v in pairs(PiePieces) do
			if (Percent + 0.1) > CurPiece then
				local t = self:FindTexture()
				t:SetTexture(TextureDirectory..v)
				t:ClearAllPoints()
				t:SetPoint("CENTER", self, "CENTER", 0, 0)
				t:SetHeight(self:GetHeight())
				t:SetWidth(self:GetWidth())
				GraphFunctions:RotateTexture(t, CurAngle)
				t:Show()

				t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
				Percent = Percent - CurPiece
				PiePercent = PiePercent + CurPiece
				CurAngle = CurAngle + Angle

				tinsert(Section.Textures, t)
			end
			CurPiece = CurPiece / 2
			Angle = Angle / 2
		end
	else--Special case if its by itself
		local t = self:FindTexture()
		t:SetTexture(TextureDirectory.."1-1")
		t:ClearAllPoints()
		t:SetPoint("CENTER", self, "CENTER", 0, 0)
		t:SetHeight(self:GetHeight())
		t:SetWidth(self:GetWidth())
		GraphFunctions:RotateTexture(t, CurAngle)
		t:Show()

		t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
		tinsert(Section.Textures, t)
	end

	--Finish adding section data
	Section.Color = Color
	Section.Angle = 360

	self.PercentOn = PiePercent
	self.Remaining = Percent

	return Color
end

function GraphFunctions:ResetPie()
	self:HideTextures()
	self:HideLines(self)

	self.PieUsed = 0
	self.PercentOn = 0
	self.Remaining = 0
	self.onColor = 1
	self.LastSection = nil
	self.TotalSections = 0
	--self.Sections = {}
end

function GraphFunctions:DrawLinePie(angle)
	local sx, sy, ex, ey
	local Radian = math_pi * (90 - angle) / 180
	local w, h
	w = self:GetWidth() / 2
	h = self:GetHeight() / 2


	sx = w
	sy = h

	ex = sx + 0.88 * w * math_cos(Radian)
	ey = sx + 0.88 * h * math_sin(Radian)

	self:DrawLine(self, sx, sy, ex, ey, 34, {0.0, 0.0, 0.0, 1.0}, "OVERLAY")
end

--Used to rotate the pie slices
function GraphFunctions:RotateTexture(texture, angle)
	local Radian = math_pi * (45 - angle) / 180
	local Radian2 = math_pi * (45 + 90 - angle) / 180
	local Radius = 0.70710678118654752440084436210485

	local tx, ty, tx2, ty2
	tx = Radius * math_cos(Radian)
	ty = Radius * math_sin(Radian)
	tx2 = -ty
	ty2 = tx

	texture:SetTexCoord(0.5 - tx, 0.5 - ty, 0.5 + tx2, 0.5 + ty2, 0.5 - tx2, 0.5 - ty2, 0.5 + tx, 0.5 + ty)
end

function GraphFunctions:SetSelectionFunc(f)
	self.SelectionFunc = f
end

--TODO: Pie chart pieces need to be clickable
function GraphFunctions:PieChart_OnUpdate()
	if (MouseIsOver(self)) then
		local sX, sY = self:GetCenter()
		local Scale = self:GetEffectiveScale()
		local mX, mY = GetCursorPosition()
		local dX, dY

		dX = mX / Scale - sX
		dY = mY / Scale - sY

		local Angle = 90-math_deg(math_atan(dY / dX))
		dY = dY * self.Ratio
		local Dist = dX * dX + dY * dY

		if dX < 0 then
			Angle = Angle + 180
		end

		--Are we on the Pie Chart?
		if Dist < self.Radius then
			--What section are we on?
			for k = 1, self.TotalSections do
				local v = self.Sections[k]
				if Angle < v.Angle then
					local Color
					if k ~= self.LastSection then
						if self.LastSection then
							local Section = self.Sections[self.LastSection]
							for _, t in pairs(Section.Textures) do
								Color = Section.Color
								t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
							end
						end

						if self.SelectionFunc then
							self:SelectionFunc(k)
						end
					end

					local ColorAdd = 0.15 * math_abs(math_fmod(GetTime(), 3) - 1.5) - 0.1125

					Color = {}
					Color[1] = v.Color[1]+ColorAdd
					Color[2] = v.Color[2]+ColorAdd
					Color[3] = v.Color[3]+ColorAdd

					for _, t in pairs(v.Textures) do
						t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
					end

					self.LastSection = k

					return
				end
			end
		elseif self.LastSection then
			local Section = self.Sections[self.LastSection]
			for _, t in pairs(Section.Textures) do
				local Color = Section.Color
				t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
			end
			self.LastSection = nil
			if self.SelectionFunc then
				self:SelectionFunc(nil)
			end
		end
	else
		if self.LastSection then
			local Section = self.Sections[self.LastSection]
			for _, t in pairs(Section.Textures) do
				local Color = Section.Color
				t:SetVertexColor(Color[1], Color[2], Color[3], 1.0)
			end
			self.LastSection = nil
			if self.SelectionFunc then
				self:SelectionFunc(nil)
			end
		end
	end
end


-------------------------------------------------------------------------------
--Axis Setting Functions
-------------------------------------------------------------------------------

function GraphFunctions:SetYMax(ymax)
	if ymax == self.YMax then
		return
	end

	self.YMax = ymax

	self.NeedsUpdate = true
end

function GraphFunctions:SetYAxis(ymin, ymax)
	if self.YMin == ymin and self.YMax == ymax then
		return
	end

	self.YMin = ymin
	self.YMax = ymax

	self.NeedsUpdate = true
end

function GraphFunctions:SetMinMaxY(val)
	if self.MinMaxY == val then
		return
	end

	self.MinMaxY = val
	self.NeedsUpdate = true
end

function GraphFunctions:SetXAxis(xmin, xmax)
	if self.XMin == xmin and self.XMax == xmax then
		return
	end

	self.XMin = xmin
	self.XMax = xmax

	self.NeedsUpdate = true

	if self.GraphType == "REALTIME" then
		self.BarWidth = (xmax - xmin) / self.BarNum
		self.Decay = math_pow(self.DecaySet, self.BarWidth)
		self.FilterOverlap = math_max(math_ceil((self.TimeRadius + xmax) / self.BarWidth), 0)
		self.LastShift = GetTime() + xmin
	end
end

function GraphFunctions:SetAutoScale(auto)
	self.AutoScale = auto

	self.NeedsUpdate = true
end

--The various Lock Functions let you use Autoscale but holds the locked points in place
function GraphFunctions:LockXMin(state)
	if state == nil then
		self.LockOnXMin = not self.LockOnXMin
		return
	end
	self.LockOnXMin = state
end

function GraphFunctions:LockXMax(state)
	if state == nil then
		self.LockOnXMax = not self.LockOnXMax
		return
	end
	self.LockOnXMax = state
end

function GraphFunctions:LockYMin(state)
	if state == nil then
		self.LockOnYMin = not self.LockOnYMin
		return
	end
	self.LockOnYMin = state
end

function GraphFunctions:LockYMax(state)
	if state == nil then
		self.LockOnYMax = not self.LockOnYMax
		return
	end
	self.LockOnYMax = state
end


-------------------------------------------------------------------------------
--Grid & Axis Drawing Functions 
-------------------------------------------------------------------------------

function GraphFunctions:SetAxisDrawing(xaxis, yaxis)
	if xaxis == self.XAxisDrawn and self.YAxisDrawn == yaxis then
		return
	end

	self.XAxisDrawn = xaxis
	self.YAxisDrawn = yaxis

	self.NeedsUpdate = true
end

function GraphFunctions:SetGridSpacing(xspacing, yspacing)
	if xspacing == self.XGridInterval and self.YGridInterval == yspacing then
		return
	end

	self.XGridInterval = xspacing
	self.YGridInterval = yspacing

	self.NeedsUpdate = true
end

function GraphFunctions:SetAxisColor(color)
	if self.AxisColor[1] == color[1] and self.AxisColor[2] == color[2] and self.AxisColor[3] == color[3] and self.AxisColor[4] == color[4] then
		return
	end

	self.AxisColor = color

	self.NeedsUpdate = true
end

function GraphFunctions:SetGridColor(color)
	if self.GridColor[1] == color[1] and self.GridColor[2] == color[2] and self.GridColor[3] == color[3] and self.GridColor[4] == color[4] then
		return
	end

	self.GridColor = color

	self.NeedsUpdate = true
end

function GraphFunctions:SetGridColorSecondary(color)
	if self.GridColorSecondary ~= nil and self.GridColorSecondary[1] == color[1] and self.GridColorSecondary[2] == color[2] and self.GridColorSecondary[3] == color[3] and self.GridColorSecondary[4] == color[4] then
		return
	end

	self.GridColorSecondary = color

	self.NeedsUpdate = true
end

function GraphFunctions:SetGridSecondaryMultiple(XAxis, YAxis)
	if type(XAxis) ~= "number" then
		XAxis = 1
	end
	if type(YAxis) ~= "number" then
		YAxis = 1
	end
	self.GridSecondaryX = XAxis
	self.GridSecondaryY = YAxis

	self.NeedsUpdate = true
end

function GraphFunctions:SetYLabels(Left, Right)
	self.YLabelsLeft = Left
	self.YLabelsRight = Right
end

function GraphFunctions:SetLineTexture(texture)
	if (type (texture) ~= "string") then
		return assert (false, "Parameter 1 for SetLineTexture must be a string")
	end

	--> full path
	if (texture:find ("\\") or texture:find ("//")) then 
		self.CustomLine = texture
	--> using an image inside lib-graph folder
	else 
		self.CustomLine = TextureDirectory..texture
	end
end

function GraphFunctions:SetBorderSize(border, size)
	border = string.lower (border)
	
	if (type (size) ~= "number") then
		return assert (false, "Parameter 2 for SetBorderSize must be a number")
	end
	
	if (border == "left") then
		self.CustomLeftBorder = size
		return true
	elseif (border == "right") then
		self.CustomRightBorder = size
		return true
	elseif (border == "top") then
		self.CustomTopBorder = size
		return true
	elseif (border == "bottom") then
		self.CustomBottomBorder = size
		return true
	end
	
	return assert (false, "Usage: GraphObject:SetBorderSize (LEFT RIGHT TOP BOTTOM, SIZE)")
end

function GraphFunctions:CreateGridlines()
	local Width = self:GetWidth()
	local Height = self:GetHeight()
	local NoSecondary = (self.GridSecondaryY == nil) or (self.GridSecondaryX == nil) or (type(self.GridColorSecondary) ~= "table")
	local F
	self:HideLines(self)
	self:HideFontStrings()

	if self.YGridInterval then
		local LowerYGridLine, UpperYGridLine, TopSpace
		LowerYGridLine = self.YMin / self.YGridInterval
		LowerYGridLine = math_max(math_floor(LowerYGridLine), math_ceil(LowerYGridLine))
		UpperYGridLine = self.YMax / self.YGridInterval
		UpperYGridLine = math_min(math_floor(UpperYGridLine), math_ceil(UpperYGridLine))
		--UpperYGridLine = math_min(UpperYGridLine, self.YGridMax or 16)
		TopSpace = Height * (1 - (UpperYGridLine * self.YGridInterval - self.YMin) / (self.YMax - self.YMin))

		for i = LowerYGridLine, UpperYGridLine do
			if i ~= 0 or not self.YAxisDrawn then
				local YPos, T
				YPos = Height * (i * self.YGridInterval - self.YMin) / (self.YMax - self.YMin)
				if NoSecondary or math_fmod(i, self.GridSecondaryY) == 0 then
					T = self:DrawLine(self, 0, YPos, Width, YPos, 24, self.GridColor, "BACKGROUND")
				else
					T = self:DrawLine(self, 0, YPos, Width, YPos, 24, self.GridColorSecondary, "BACKGROUND")
				end

				if ((i ~= UpperYGridLine) or (TopSpace > 12)) and (NoSecondary or math_fmod(i, self.GridSecondaryY) == 0) then
					if self.YLabelsLeft then
						F = self:FindFontString()
						F:SetFontObject("GameFontHighlightSmall")
						F:SetTextColor(1, 1, 1)
						F:ClearAllPoints()
						F:SetPoint("BOTTOMLEFT", T, "LEFT", 2, 2)
						F:SetText(i * self.YGridInterval)
						F:Show()
					end

					if self.YLabelsRight then
						F = self:FindFontString()
						F:SetFontObject("GameFontHighlightSmall")
						F:SetTextColor(1, 1, 1)
						F:ClearAllPoints()
						F:SetPoint("BOTTOMRIGHT", T, "RIGHT", -2, 2)
						F:SetText(i * self.YGridInterval)
						F:Show()
					end
				end
			end
		end
	end

	if self.XGridInterval then
		local LowerXGridLine, UpperXGridLine
		LowerXGridLine = self.XMin / self.XGridInterval
		LowerXGridLine = math_max(math_floor(LowerXGridLine), math_ceil(LowerXGridLine))
		UpperXGridLine = self.XMax / self.XGridInterval
		UpperXGridLine = math_min(math_floor(UpperXGridLine), math_ceil(UpperXGridLine))
		--UpperXGridLine = math_min(UpperXGridLine, self.XGridMax or 16)

		for i = LowerXGridLine, UpperXGridLine do
			if i ~= 0 or not self.XAxisDrawn then
				local XPos
				XPos = Width * (i * self.XGridInterval - self.XMin) / (self.XMax - self.XMin)
				if NoSecondary or math_fmod(i, self.GridSecondaryX) == 0 then
					self:DrawLine(self, XPos, 0, XPos, Height, 24, self.GridColor, "BACKGROUND")
				else
					self:DrawLine(self, XPos, 0, XPos, Height, 24, self.GridColorSecondary, "BACKGROUND")
				end
			end
		end
	end

	if self.YAxisDrawn and self.YMax >= 0 and self.YMin <= 0 then
		local YPos, T

		YPos = Height * (-self.YMin) / (self.YMax - self.YMin)
		T = self:DrawLine(self, 0, YPos, Width, YPos, 24, self.AxisColor, "BACKGROUND")

		if self.YLabelsLeft then
			F = self:FindFontString()
			F:SetFontObject("GameFontHighlightSmall")
			F:SetTextColor(1, 1, 1)
			F:ClearAllPoints()
			F:SetPoint("BOTTOMLEFT", T, "LEFT", 2, 2)
			F:SetText(0)
			F:Show()
		end
		if self.YLabelsRight then
			F = self:FindFontString()
			F:SetFontObject("GameFontHighlightSmall")
			F:SetTextColor(1, 1, 1)
			F:ClearAllPoints()
			F:SetPoint("BOTTOMRIGHT", T, "RIGHT", -2, 2)
			F:SetText(0)
			F:Show()
		end
	end

	if self.XAxisDrawn and self.XMax >= 0 and self.XMin <= 0 then
		local XPos

		XPos = Width * (-self.XMin) / (self.XMax - self.XMin)
		self:DrawLine(self, XPos, 0, XPos, Height, 24, self.AxisColor, "BACKGROUND")
	end
end


--------------------------------------------------------------------------------
--Refresh functions
--------------------------------------------------------------------------------

function GraphFunctions:OnUpdateGraph()
	if self.NeedsUpdate and self.RefreshGraph then
		self:RefreshGraph()
		self.NeedsUpdate = false
	end
end

--Performs a convolution in realtime allowing to graph Framerate, DPS, or any other data you want graphed in realtime
function GraphFunctions:OnUpdateGraphRealtime()
	local CurTime = GetTime()
	local BarsChanged

	if self.NextUpdate > CurTime or (self.Mode == "RAW" and not (self.NeedsUpdate or self.AddedBar)) then
		return
	end

	self.NextUpdate = CurTime + self.LimitUpdates

	--Slow Mode performs an entire convolution every frame
	if self.Mode == "SLOW" then
		--Initialize Bar Data
		self.BarHeight = {}
		for i = 1, self.BarNum do
			self.BarHeight[i] = 0
		end
		local k, v
		local BarTimeRadius = (self.XMax - self.XMin) / self.BarNum
		local DataValue = 1 / (2 * self.TimeRadius)

		if self.Filter == "RECT" then
			--Take the convolution of the dataset on to the bars wtih a rectangular filter
			local DataValue = 1 / (2 * self.TimeRadius)
			for k, v in pairs(self.Data) do
				if v.Time < (CurTime + self.XMin - self.TimeRadius) then
					tremove(self.Data, k)
				else
					local DataTime = v.Time - CurTime
					local LowestBar = math_max(math_floor((DataTime - self.XMin - self.TimeRadius) / BarTimeRadius), 1)
					local HighestBar = math_min(math_ceil((DataTime - self.XMin + self.TimeRadius) / BarTimeRadius), self.BarNum)
					for i = LowestBar, HighestBar do
						self.BarHeight[i] = self.BarHeight[i] + v.Value * DataValue
					end
				end
			end
		elseif self.Filter == "TRI" then
			--Needs optimization badly
			--Take the convolution of the dataset on to the bars wtih a triangular filter
			local DataValue = 1 / (self.TimeRadius)
			for k, v in pairs(self.Data) do
				local Temp
				if v.Time < (CurTime + self.XMin - self.TimeRadius) then
					tremove(self.Data, k)
				else
					local DataTime = v.Time - CurTime
					local LowestBar = math_max(math_floor((DataTime - self.XMin - self.TimeRadius) / BarTimeRadius), 1)
					local HighestBar = math_min(math_ceil((DataTime - self.XMin + self.TimeRadius) / BarTimeRadius), self.BarNum)

					for i = LowestBar, HighestBar do
						self.BarHeight[i] = self.BarHeight[i] + v.Value * DataValue * math_abs(BarTimeRadius * i + self.XMin - DataTime)
					end
				end
			end
		end
		BarsChanged = true
	elseif self.Mode == "FAST" then
		local ShiftBars = math_floor((CurTime - self.LastShift) / self.BarWidth)

		if ShiftBars > 0 and not (self.LastDataTime < (self.LastShift + self.XMin - self.TimeRadius * 2)) then
			local RecalcBars = self.BarNum - (ShiftBars + self.FilterOverlap) + 1

			for i = 1, self.BarNum do
				if i < RecalcBars then
					self.BarHeight[i] = self.BarHeight[i + ShiftBars]
				else
					self.BarHeight[i] = 0
				end
			end

			local BarTimeRadius = (self.XMax - self.XMin) / self.BarNum
			local DataValue = 1 / (2 * self.TimeRadius)
			local TimeDiff = CurTime-self.LastShift

			CurTime = self.LastShift + ShiftBars * self.BarWidth
			self.LastShift = CurTime

			if self.Filter == "RECT" then
				--Take the convolution of the dataset on to the bars wtih a rectangular filter
				local DataValue = 1 / (2 * self.TimeRadius)
				for k, v in pairs(self.Data) do
					if v.Time < (CurTime + self.XMax - self.TimeRadius - TimeDiff) then
						tremove(self.Data, k)
					else
						local DataTime = v.Time - CurTime
						local LowestBar = math_max(math_max(math_floor((DataTime - self.XMin - self.TimeRadius) / BarTimeRadius), RecalcBars), 1)
						local HighestBar = math_min(math_ceil((DataTime - self.XMin + self.TimeRadius) / BarTimeRadius), self.BarNum)
						if LowestBar <= HighestBar then
							for i = LowestBar, HighestBar do
								self.BarHeight[i] = self.BarHeight[i] + v.Value * DataValue
							end
						end
					end
				end
			end
			BarsChanged = true
		else
			CurTime = self.LastShift + ShiftBars * self.BarWidth
			self.LastShift = CurTime
		end
	elseif self.Mode == "EXP" then
		local ShiftBars = math_floor((CurTime - self.LastShift) / self.BarWidth)

		if ShiftBars > 0 then
			local RecalcBars = self.BarNum - ShiftBars + 1

			for i = 1, self.BarNum do
				if i < RecalcBars then
					self.BarHeight[i] = self.BarHeight[i + ShiftBars]
				end
			end

			--Now to calculate the new bars
			local Total
			local Weight = 1 / self.TimeRadius / self.ExpNorm

			for i = RecalcBars, self.BarNum do
				Total = 0

				--Implement an EXPFAST which does this only once instead of for each bar
				for k, v in pairs(self.Data) do
					Total = Total + v.Value * Weight
					if v.Time < (self.LastShift - self.TimeRadius) then
						tremove(self.Data, k)
					end
				end

				self.CurVal = self.Decay * self.CurVal + Total

				self.BarHeight[i] = self.CurVal

				self.LastShift = self.LastShift + self.BarWidth
			end

			if self.CurVal < 0.1 then
				self.CurVal = 0
			end
			BarsChanged = true
		else
			self.LastShift = self.LastShift + self.BarWidth * ShiftBars
		end
	elseif self.Mode == "EXPFAST" then
		local ShiftBars = math_floor((CurTime - self.LastShift) / self.BarWidth)
		local RecalcBars = self.BarNum - ShiftBars + 1

		if ShiftBars > 0 and not (self.LastDataTime < (self.LastShift + self.XMin-self.TimeRadius)) then
			for i = 1, self.BarNum do
				if i < RecalcBars then
					self.BarHeight[i] = self.BarHeight[i + ShiftBars]
				end
			end

			--Now to calculate the new bars
			local Total
			local Weight = 1 / self.TimeRadius / self.ExpNorm

			Total = 0

			--Implement an EXPFAST which does this only once instead of for each bar
			for k, v in pairs(self.Data) do
				Total = Total + v.Value * Weight
				if v.Time < (self.LastShift - self.TimeRadius) then
					tremove(self.Data, k)
				end
			end

			--self.LastShift = self.LastShift+self.BarWidth*ShiftBars

			if self.CurVal ~= 0 or Total ~= 0 then
				for i = RecalcBars, self.BarNum do
					self.CurVal = self.Decay * self.CurVal + Total
					self.BarHeight[i] = self.CurVal
				end
				self.LastDataTime = self.LastShift + self.BarWidth * ShiftBars
			else
				for i = RecalcBars, self.BarNum do
					self.BarHeight[i] = 0
				end
			end

			if self.CurVal < 0.1 then
				self.CurVal = 0
			end
			BarsChanged = true
		end
		self.LastShift = self.LastShift + self.BarWidth * ShiftBars
	elseif self.Mode == "RAW" then
		--Do nothing really
		--Using .AddedBar so we cut down on updating the grid
		self.AddedBar = false
		BarsChanged = true
	end


	if BarsChanged then
		if self.AutoScale then
			local MaxY = 0

			for i = 1, self.BarNum do
				MaxY = math_max(MaxY, self.BarHeight[i])
			end
			MaxY = 1.25 * MaxY

			MaxY = math_max(MaxY, self.MinMaxY)

			if MaxY ~= 0 and math_abs(self.YMax - MaxY) > 0.01 then
				self.YMax = MaxY
				self.NeedsUpdate = true

				local Spacing
				if self.YMax < 25 then
					Spacing = -1
				else
					Spacing = math.log(self.YMax / 100) / math.log(2)
				end

				self.YGridInterval = 25 * math.pow(2, math.floor(Spacing))
			end
		end
		self:SetBars()
	end

	if self.NeedsUpdate then
		self.NeedsUpdate = false
		self:RefreshGraph()
	end
end

--Line Graph
function GraphFunctions:RefreshLineGraph()
	self:HideLines(self)
	self:HideBars(self)

	if self.AutoScale and self.Data then
		local MinX, MaxX, MinY, MaxY = math_huge, -math_huge, math_huge, -math_huge
		--Go through line data first
		for k1, series in pairs(self.Data) do
			for k2, point in pairs(series.Points) do
				MinX = math_min(point[1], MinX)
				MaxX = math_max(point[1], MaxX)
				MinY = math_min(point[2], MinY)
				MaxY = math_max(point[2], MaxY)
			end
		end

		--Now through the Filled Lines
		for k1, series in pairs(self.FilledData) do
			for k2, point in pairs(series.Points) do
				MinX = math_min(point[1], MinX)
				MaxX = math_max(point[1], MaxX)
				MinY = math_min(point[2], MinY)
				MaxY = math_max(point[2], MaxY)
			end
		end

		local XBorder, YBorder

		XBorder = 0.1 * (MaxX - MinX)
		YBorder = 0.1 * (MaxY - MinY)

		if not self.LockOnXMin then
			if (self.CustomLeftBorder) then
				self.XMin = MinX + self.CustomLeftBorder --> custom size of left border
			else
				self.XMin = MinX - XBorder
			end
		end
		
		if not self.LockOnXMax then
			if (self.CustomRightBorder) then
				self.XMax = MaxX + self.CustomRightBorder --> custom size of right border
			else
				self.XMax = MaxX + XBorder
			end
		end
		
		if not self.LockOnYMin then
			if (self.CustomBottomBorder) then
				self.YMin = MinY + self.CustomBottomBorder --> custom size of bottom border
			else
				self.YMin = MinY - YBorder
			end
		end
		
		if not self.LockOnYMax then
			if (self.CustomTopBorder) then
				self.YMax = MaxY + self.CustomTopBorder --> custom size of top border
			else
				self.YMax = MaxY + YBorder
			end
		end

	end

	self:CreateGridlines()

	local Width = self:GetWidth()
	local Height = self:GetHeight()

	for k1, series in pairs(self.Data) do
		local LastPoint
		LastPoint = nil
		
		for k2, point in pairs(series.Points) do
			if LastPoint then
				local TPoint = {x = point[1]; y = point[2]}

				TPoint.x = Width * (TPoint.x - self.XMin) / (self.XMax - self.XMin)
				TPoint.y = Height * (TPoint.y - self.YMin) / (self.YMax - self.YMin)

				--tercioo: send the data index to DrawLine so custom draw functions know what they are drawing
				self:DrawLine(self, LastPoint.x, LastPoint.y, TPoint.x, TPoint.y, 32, series.Color, nil, series.LineTexture, k1)

				LastPoint = TPoint
			else
				LastPoint = {x = point[1]; y = point[2]}
				LastPoint.x = Width * (LastPoint.x - self.XMin) / (self.XMax - self.XMin)
				LastPoint.y = Height * (LastPoint.y - self.YMin) / (self.YMax - self.YMin)
			end
		end
	end

	--Filled Line Graphs
	for k1, series in pairs(self.FilledData) do
		local LastPoint
		LastPoint = nil

		for k2, point in pairs(series.Points) do
			if LastPoint then
				local TPoint = {x = point[1]; y = point[2]}

				TPoint.x = Width * (TPoint.x - self.XMin) / (self.XMax - self.XMin)
				TPoint.y = Height * (TPoint.y - self.YMin ) /(self.YMax - self.YMin)

				self:DrawBar(self, LastPoint.x, LastPoint.y, TPoint.x, TPoint.y, series.Color, k1)

				LastPoint = TPoint
			else
				LastPoint = {x = point[1]; y = point[2]}
				LastPoint.x = Width * (LastPoint.x - self.XMin) / (self.XMax - self.XMin)
				LastPoint.y = Height * (LastPoint.y - self.YMin) / (self.YMax - self.YMin)
			end
		end
	end
end

--Scatter Plot Refresh
function GraphFunctions:RefreshScatterPlot()
	self:HideLines(self)

	if self.AutoScale and self.Data then
		local MinX, MaxX, MinY, MaxY = math_huge, -math_huge, math_huge, -math_huge
		for k1, series in pairs(self.Data) do
			for k2, point in pairs(series.Points) do
				MinX = math_min(point[1], MinX)
				MaxX = math_max(point[1], MaxX)
				MinY = math_min(point[2], MinY)
				MaxY = math_max(point[2], MaxY)
			end
		end

		local XBorder, YBorder

		XBorder = 0.1 * (MaxX - MinX)
		YBorder = 0.1 * (MaxY - MinY)

		if not self.LockOnXMin then
			self.XMin = MinX - XBorder
		end
		if not self.LockOnXMax then
			self.XMax = MaxX + XBorder
		end
		if not self.LockOnYMin then
			self.YMin = MinY - YBorder
		end
		if not self.LockOnYMax then
			self.YMax = MaxY + YBorder
		end
	end

	self:CreateGridlines()

	local Width = self:GetWidth()
	local Height = self:GetHeight()

	self:HideTextures()
	for k1, series in pairs(self.Data) do
		local MinX, MaxX = self.XMax, self.XMin
		for k2, point in pairs(series.Points) do
			local x, y
			MinX = math_min(point[1],MinX)
			MaxX = math_max(point[1],MaxX)
			x = Width * (point[1] - self.XMin) / (self.XMax - self.XMin)
			y = Height * (point[2] - self.YMin) / (self.YMax - self.YMin)

			local g = self:FindTexture()
			g:SetTexture("Spells\\GENERICGLOW2_64")
			g:SetWidth(6)
			g:SetHeight(6)
			g:ClearAllPoints()
			g:SetPoint("CENTER", self, "BOTTOMLEFT", x, y)
			g:SetVertexColor(series.Color[1], series.Color[2], series.Color[3], series.Color[4])
			g:Show()
		end

		if self.LinearFit then
			local alpha, beta = self:LinearRegression(series.Points)
			local sx, sy, ex, ey

			sx = MinX
			sy = beta * sx + alpha
			ex = MaxX
			ey = beta*ex+alpha

			sx = Width * (sx - self.XMin) / (self.XMax - self.XMin)
			sy = Height * (sy - self.YMin) / (self.YMax - self.YMin)
			ex = Width * (ex - self.XMin) / (self.XMax - self.XMin)
			ey = Height * (ey - self.YMin) / (self.YMax - self.YMin)

			self:DrawLine(self, sx, sy, ex, ey, 32, series.Color)
		end
	end
end

--Copied from Blizzard's TaxiFrame code and modifed for IMBA then remodified for GraphLib

-- The following function is used with permission from Daniel Stephens <iriel@vigilance-committee.org>
local TAXIROUTE_LINEFACTOR = 128 / 126 -- Multiplying factor for texture coordinates
local TAXIROUTE_LINEFACTOR_2 = TAXIROUTE_LINEFACTOR / 2 -- Half of that

-- T		- Texture
-- C		- Canvas Frame (for anchoring)
-- sx, sy	- Coordinate of start of line
-- ex, ey	- Coordinate of end of line
-- w		- Width of line
-- relPoint	- Relative point on canvas to interpret coords (Default BOTTOMLEFT)
function lib:DrawLine(C, sx, sy, ex, ey, w, color, layer, linetexture)
	local relPoint = "BOTTOMLEFT"

	if sx == ex then
		if sy == ey then
			return
		else
			return self:DrawVLine(C, sx, sy, ey, w, color, layer)
		end
	elseif sy == ey then
		return self:DrawHLine(C, sx, ex, sy, w, color, layer)
	end

	if not C.GraphLib_Lines then
		C.GraphLib_Lines = {}
		C.GraphLib_Lines_Used = {}
	end

	local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
	
	if linetexture then --> this data series texture
		T:SetTexture(linetexture)
	elseif C.CustomLine then --> overall chart texture
		T:SetTexture(C.CustomLine)
	else --> no texture assigned, use default
		T:SetTexture(TextureDirectory.."line")
	end
	
	tinsert(C.GraphLib_Lines_Used, T)

	T:SetDrawLayer(layer or "ARTWORK")

	T:SetVertexColor(color[1], color[2], color[3], color[4])
	-- Determine dimensions and center point of line
	local dx, dy = ex - sx, ey - sy
	local cx, cy = (sx + ex) / 2, (sy + ey) / 2

	-- Normalize direction if necessary
	if (dx < 0) then
		dx, dy = -dx, -dy
	end

	-- Calculate actual length of line
	local l = sqrt((dx * dx) + (dy * dy))

	-- Sin and Cosine of rotation, and combination (for later)
	local s, c = -dy / l, dx / l
	local sc = s * c

	-- Calculate bounding box size and texture coordinates
	local Bwid, Bhgt, BLx, BLy, TLx, TLy, TRx, TRy, BRx, BRy
	if (dy >= 0) then
		Bwid = ((l * c) - (w * s)) * TAXIROUTE_LINEFACTOR_2
		Bhgt = ((w * c) - (l * s)) * TAXIROUTE_LINEFACTOR_2
		BLx, BLy, BRy = (w / l) * sc, s * s, (l / w) * sc
		BRx, TLx, TLy, TRx = 1 - BLy, BLy, 1 - BRy, 1 - BLx
		TRy = BRx
	else
		Bwid = ((l * c) + (w * s)) * TAXIROUTE_LINEFACTOR_2
		Bhgt = ((w * c) + (l * s)) * TAXIROUTE_LINEFACTOR_2
		BLx, BLy, BRx = s * s, -(l / w) * sc, 1 + (w / l) * sc
		BRy, TLx, TLy, TRy = BLx, 1 - BRx, 1 - BLx, 1 - BLy
		TRx = TLy
	end

	-- Thanks Blizzard for adding (-)10000 as a hard-cap and throwing errors!
	-- The cap was added in 3.1.0 and I think it was upped in 3.1.1
	-- (way less chance to get the error)
	if TLx > 10000 then TLx = 10000 elseif TLx < -10000 then TLx = -10000 end
	if TLy > 10000 then TLy = 10000 elseif TLy < -10000 then TLy = -10000 end
	if BLx > 10000 then BLx = 10000 elseif BLx < -10000 then BLx = -10000 end
	if BLy > 10000 then BLy = 10000 elseif BLy < -10000 then BLy = -10000 end
	if TRx > 10000 then TRx = 10000 elseif TRx < -10000 then TRx = -10000 end
	if TRy > 10000 then TRy = 10000 elseif TRy < -10000 then TRy = -10000 end
	if BRx > 10000 then BRx = 10000 elseif BRx < -10000 then BRx = -10000 end
	if BRy > 10000 then BRy = 10000 elseif BRy < -10000 then BRy = -10000 end

	-- Set texture coordinates and anchors
	T:ClearAllPoints()
	T:SetTexCoord(TLx, TLy, BLx, BLy, TRx, TRy, BRx, BRy)
	T:SetPoint("BOTTOMLEFT", C, relPoint, cx - Bwid, cy - Bhgt)
	T:SetPoint("TOPRIGHT", C, relPoint, cx + Bwid, cy + Bhgt)
	T:Show()
	return T
end

--Thanks to Celandro
function lib:DrawVLine(C, x, sy, ey, w, color, layer)
	local relPoint = "BOTTOMLEFT"

	if not C.GraphLib_Lines then
		C.GraphLib_Lines = {}
		C.GraphLib_Lines_Used = {}
	end

	local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
	T:SetTexture(TextureDirectory.."sline")
	tinsert(C.GraphLib_Lines_Used, T)

	T:SetDrawLayer(layer or "ARTWORK")

	T:SetVertexColor(color[1], color[2], color[3], color[4])

	if sy > ey then
		sy, ey = ey, sy
	end

	-- Set texture coordinates and anchors
	T:ClearAllPoints()
	T:SetTexCoord(1, 0, 0, 0, 1, 1, 0, 1)
	T:SetPoint("BOTTOMLEFT", C, relPoint, x - w / 2, sy)
	T:SetPoint("TOPRIGHT", C, relPoint, x + w / 2, ey)
	T:Show()
	return T
end

function lib:DrawHLine(C, sx, ex, y, w, color, layer)
	local relPoint = "BOTTOMLEFT"

	if not C.GraphLib_Lines then
		C.GraphLib_Lines = {}
		C.GraphLib_Lines_Used = {}
	end

	local T = tremove(C.GraphLib_Lines) or C:CreateTexture(nil, "ARTWORK")
	T:SetTexture(TextureDirectory.."sline")
	tinsert(C.GraphLib_Lines_Used, T)

	T:SetDrawLayer(layer or "ARTWORK")

	T:SetVertexColor(color[1], color[2], color[3], color[4])

	if sx > ex then
		sx, ex = ex, sx
	end

	-- Set texture coordinates and anchors
	T:ClearAllPoints()
	T:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
	T:SetPoint("BOTTOMLEFT", C, relPoint, sx, y - w / 2)
	T:SetPoint("TOPRIGHT", C, relPoint, ex, y + w / 2)
	T:Show()
	return T
end

function lib:HideLines(C)
	if C.GraphLib_Lines then
		for i = #C.GraphLib_Lines_Used, 1, -1 do
			C.GraphLib_Lines_Used[i]:Hide()
			tinsert(C.GraphLib_Lines, tremove(C.GraphLib_Lines_Used))
		end
	end
end

--Two parts to each bar
function lib:DrawBar(C, sx, sy, ex, ey, color, level)
	local Bar, Tri, barNum, MinY, MaxY

	--Want sx <= ex if not then flip them
	if sx > ex then
		sx, ex = ex, sx
		sy, ey = ey, sy
	end

	if not C.GraphLib_Bars then
		C.GraphLib_Bars = {}
		C.GraphLib_Tris = {}
		C.GraphLib_Bars_Used = {}
		C.GraphLib_Tris_Used = {}
		C.GraphLib_Frames = {}
	end

	if (#C.GraphLib_Bars) > 0 then
		Bar = C.GraphLib_Bars[#C.GraphLib_Bars]
		tremove(C.GraphLib_Bars, #C.GraphLib_Bars)
		Bar:Show()

		Tri = C.GraphLib_Tris[#C.GraphLib_Tris]
		tremove(C.GraphLib_Tris, #C.GraphLib_Tris)
		Tri:Show()
	end

	if not Bar then
		Bar = C:CreateTexture(nil, "ARTWORK")
		Bar:SetColorTexture(1, 1, 1, 1)

		Tri = C:CreateTexture(nil, "ARTWORK")
		Tri:SetTexture(TextureDirectory.."triangle")
	end

	tinsert(C.GraphLib_Bars_Used, Bar)
	tinsert(C.GraphLib_Tris_Used, Tri)

	if level then
		if type(C.GraphLib_Frames[level]) == "nil" then
			local newLevel = C:GetFrameLevel() + level
			C.GraphLib_Frames[level] = CreateFrame("Frame", nil, C)
			C.GraphLib_Frames[level]:SetFrameLevel(newLevel)
			C.GraphLib_Frames[level]:SetAllPoints(C)

			if C.TextFrame and C.TextFrame:GetFrameLevel() <= newLevel then
				C.TextFrame:SetFrameLevel(newLevel + 1)
				self.NeedsUpdate = true
			end
		end

		Bar:SetParent(C.GraphLib_Frames[level])
		Tri:SetParent(C.GraphLib_Frames[level])
	end

	Bar:SetVertexColor(color[1], color[2], color[3], color[4])
	Tri:SetVertexColor(color[1], color[2], color[3], color[4])


	if sy < ey then
		Tri:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
		MinY = sy
		MaxY = ey
	else
		Tri:SetTexCoord(1, 0, 1, 1, 0, 0, 0, 1)
		MinY = ey
		MaxY = sy
	end

	--Has to be at least 1 wide
	if MinY <= 1 then
		MinY = 1
	end


	Bar:ClearAllPoints()
	Bar:SetPoint("BOTTOMLEFT", C, "BOTTOMLEFT", sx, 0)

	local Width = ex - sx
	if Width < 1 then
		Width = 1
	end
	Bar:SetWidth(Width)
	Bar:SetHeight(MinY)


	if (MaxY-MinY) >= 1 then
		Tri:ClearAllPoints()
		Tri:SetPoint("BOTTOMLEFT", C, "BOTTOMLEFT", sx, MinY)
		Tri:SetWidth(Width)
		Tri:SetHeight(MaxY - MinY)
	else
		Tri:Hide()
	end
end


function lib:HideBars(C)
	if not C.GraphLib_Bars then
		return
	end

	while (#C.GraphLib_Bars_Used) > 0 do
		C.GraphLib_Bars[#C.GraphLib_Bars + 1] = C.GraphLib_Bars_Used[#C.GraphLib_Bars_Used]
		C.GraphLib_Bars[#C.GraphLib_Bars]:Hide()
		C.GraphLib_Bars_Used[#C.GraphLib_Bars_Used] = nil

		C.GraphLib_Tris[#C.GraphLib_Tris + 1] = C.GraphLib_Tris_Used[#C.GraphLib_Tris_Used]
		C.GraphLib_Tris[#C.GraphLib_Tris]:Hide()
		C.GraphLib_Tris_Used[#C.GraphLib_Tris_Used] = nil
	end
end

-- lib upgrade stuff, overwrite the old function references in
-- existing graphs with the ones in this newer library
for _, graph in ipairs(lib.RegisteredGraphRealtime) do
	SetupGraphRealtimeFunctions(graph, true)
end
for _, graph in ipairs(lib.RegisteredGraphLine) do
	SetupGraphLineFunctions(graph)
end
for _, graph in ipairs(lib.RegisteredGraphScatterPlot) do
	SetupGraphScatterPlotFunctions(graph)
end
for _, graph in ipairs(lib.RegisteredGraphPieChart) do
	SetupGraphPieChartFunctions(graph)
end

---------------------------------------------------
--Test Functions, for reference for addon authors to test, use and copy
--To test the library do /script LibStub("LibGraph-2.0"):TestGraph2Lib()
local function TestRealtimeGraph()
	local Graph = LibStub(major)
	local g = Graph:CreateGraphRealtime("TestRealtimeGraph", UIParent, "CENTER", "CENTER", -90, 90, 150, 150)
	g:SetAutoScale(true)
	g:SetGridSpacing(1.0, 10.0)
	g:SetYMax(120)
	g:SetXAxis(-11, -1)
	g:SetFilterRadius(1)
	g:SetBarColors({0.2, 0.0, 0.0, 0.4}, {1.0, 0.0, 0.0, 1.0})

	local f = CreateFrame("Frame")
	f:SetScript("OnUpdate", function()
		g:AddTimeData(1)
	end)
	f:Show()
	DEFAULT_CHAT_FRAME:AddMessage("Testing Realtime Graph")
end

local function TestRealtimeGraphRaw()
	local Graph = LibStub(major)
	local g = Graph:CreateGraphRealtime("TestRealtimeGraph", UIParent, "TOP", "TOP", 0, 0, 150, 150)
	g:SetAutoScale(true)
	g:SetGridSpacing(1.0, 10.0)
	g:SetYMax(120)
	g:SetXAxis(-10, 0)
	g:SetMode("RAW")
	g:SetBarColors({0.2, 0.0, 0.0, 0.4}, {1.0, 0.0, 0.0, 1.0})

	local f = CreateFrame("Frame")
	f.frames = 0
	f.NextUpdate = GetTime()
	f:SetScript("OnUpdate",function()
		if f.NextUpdate > GetTime() then
			return
		end

		g:AddBar(UnitHealth("player"))
		f.NextUpdate = f.NextUpdate + g.BarWidth
	end)
	f:Show()
	DEFAULT_CHAT_FRAME:AddMessage("Testing 0")
end

local function TestLineGraph()
	local Graph = LibStub(major)
	local g = Graph:CreateGraphLine("TestLineGraph", UIParent, "CENTER", "CENTER", 90, 90, 150, 150)
	g:SetXAxis(-1, 1)
	g:SetYAxis(-1, 1)
	g:SetGridSpacing(0.25, 0.25)
	g:SetGridColor({0.5, 0.5, 0.5, 0.5})
	g:SetAxisDrawing(true, true)
	g:SetAxisColor({1.0, 1.0, 1.0, 1.0})
	g:SetAutoScale(true)

	local Data1 = {{0.05, 0.05}, {0.2, 0.3}, {0.4, 0.2}, {0.9, 0.6}}
	local Data2 = {{0.05, 0.8}, {0.3, 0.1}, {0.5, 0.4}, {0.95, 0.05}}

	g:AddDataSeries(Data1,{1.0, 0.0, 0.0, 0.8})
	g:AddDataSeries(Data2,{0.0, 1.0, 0.0, 0.8})
	DEFAULT_CHAT_FRAME:AddMessage("Testing Line Graph")
end

local function TestScatterPlot()
	local Graph = LibStub(major)
	local g = Graph:CreateGraphScatterPlot("TestScatterPlot", UIParent, "CENTER", "CENTER", 90, -90, 150, 150)
	g:SetXAxis(-1, 1)
	g:SetYAxis(-1, 1)
	g:SetGridSpacing(0.25, 0.25)
	g:SetGridColor({0.5, 0.5, 0.5, 0.5})
	g:SetAxisDrawing(true, true)
	g:SetAxisColor({1.0, 1.0, 1.0, 1.0})
	g:SetLinearFit(true)
	g:SetAutoScale(true)

	local Data1 = {{0.05, 0.05}, {0.2, 0.3}, {0.4, 0.2}, {0.9, 0.6}}
	local Data2 = {{0.05, 0.8}, {0.3, 0.1}, {0.5, 0.4}, {0.95, 0.05}}

	g:AddDataSeries(Data1,{1.0, 0.0, 0.0, 0.8})
	g:AddDataSeries(Data2,{0.0, 1.0, 0.0, 0.8})
	DEFAULT_CHAT_FRAME:AddMessage("Testing Scatter Plot")
end

local function TestPieChart()
	local Graph = LibStub(major)
	local g = Graph:CreateGraphPieChart("TestPieChart", UIParent, "CENTER", "CENTER", -90, -90, 150, 150)
	g:AddPie(35, {1.0, 0.0, 0.0})
	g:AddPie(21, {0.0, 1.0, 0.0})
	g:AddPie(10, {1.0, 1.0, 1.0})
	g:CompletePie({0.2, 0.2, 1.0})
	DEFAULT_CHAT_FRAME:AddMessage("Testing Pie Chart")
end

function lib:TestGraph2Lib()
	DEFAULT_CHAT_FRAME:AddMessage("Testing "..major..", "..gsub(minor, "%$", ""))
	TestRealtimeGraph()
	TestLineGraph()
	TestScatterPlot()
	TestPieChart()
end
