--[[
Name: LibGraph-2.0
Revision: $Rev: 49 $
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: 49 $"):match("(%d+)"))


-- hard coded path for TSM
local TextureDirectory = "Interface\\AddOns\\TradeSkillMaster\\Libs\\LibGraph-2.0\\"


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

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

local GraphFunctions={}

local tinsert, tremove = tinsert, tremove
local pairs, ipairs = pairs, ipairs
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 GetTime = GetTime

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



--------------------------------------------------------------------------------
--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)

	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)--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.blp")
		bar:GetStatusBarTexture():SetHorizTile(false)
		bar:GetStatusBarTexture():SetVertTile(false)

		local t=bar:GetStatusBarTexture()		
		t:SetGradientAlpha("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.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)

	
	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)

	
	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)

	
	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()
		t:SetGradientAlpha("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
		v:GetStatusBarTexture():SetGradientAlpha("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.blp")
			bar:GetStatusBarTexture():SetHorizTile(false)
			bar:GetStatusBarTexture():SetVertTile(false)

			local t=bar:GetStatusBarTexture()		
			t:SetGradientAlpha("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)
	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
	
	tinsert(self.Data,{Points=data;Color=color})
	
	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 k,v
	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 k,v
	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

		local k,v		
		--Are we on the Pie Chart?
		if Dist<self.Radius then
			--What section are we on?
			for k=1,self.TotalSections do				
				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:CreateGridlines()
	local Width=self:GetWidth()
	local Height=self:GetHeight()
	local NoSecondary=(self.GridSecondaryY==nil) or (self.GridSecondaryX==nil) or (type(self.GridColorSecondary)~="table")
	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))
		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,F
				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))

		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 i,j
	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 k,v
			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			
			end		
		end
		self:SetBars()
	end

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

--Line Graph
function GraphFunctions:RefreshLineGraph()
	local k1, k2, series
	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
			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()

	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)

				self:DrawLine(self,LastPoint.x,LastPoint.y,TPoint.x,TPoint.y,32,series.Color)

				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()
	local k1, k2, series, point
	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.blp")
			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)
	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")
	T:SetTexture(TextureDirectory.."line")
	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:SetTexture(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 Reattime 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
