-- YAY FOR SPARKLES~!

SparkleFrame = CreateFrame("FRAME");
local Sparkle = SparkleFrame:CreateTexture();

function Sparkle:New (sparkleFrame, sparkleTemplate)
	if sparkleFrame.freeSparkles[1] then
		local sparkle = sparkleFrame.freeSparkles[1];
		sparkle:Reuse(sparkleTemplate);
		tremove(sparkleFrame.freeSparkles, 1);
		tinsert(sparkleFrame.sparkles, sparkle);
		return sparkle;
	end
	
	sparkleTemplate = sparkleTemplate or "SparkleTextureNormal"
	local name = sparkleFrame:GetName();
	local sparkle;
	if name then
		sparkle = sparkleFrame:CreateTexture(name .. "Sparkle" .. sparkleFrame.numSparkles, "ARTWORK", sparkleTemplate)
	else
		sparkle = sparkleFrame:CreateTexture(nil, "ARTWORK", sparkleTemplate);
	end
	
	setmetatable(sparkle, self);
	self.__index = self;

	tinsert(sparkleFrame.sparkles, sparkle)
	sparkleFrame.numSparkles = sparkleFrame.numSparkles + 1;
	
	return sparkle;
end

function Sparkle:Free ()
	local sparkleFrame = self:GetParent();
	if self.OnFinished then
		self:OnFinished();
	end
	
	self:Hide(); 
	for i = 1, self.numArgs do
		self["param" .. i] = nil;
	end
	
	self.name = nil;
	self.elapsed = nil;
	self.loop = nil;
	self.Animate = nil;
	
	tinsert(sparkleFrame.freeSparkles, self);
end

local SparkleDimensions = 
{
	["SparkleTextureNormal"] = { height = 13, width = 13 },
	["SparkleTextureKindaSmall"] = { height = 10, width = 10 },
	["SparkleTextureSmall"] = { height = 7, width = 7 },
}



function Sparkle:Reuse (sparkleTemplate)
	local dimensions = SparkleDimensions[sparkleTemplate]
	if not dimensions then
		error()
	end
	
	self:SetHeight(dimensions.height);
	self:SetWidth(dimensions.width);
end

function Sparkle:SetOnFinished (func)
	self.OnFinished = func;
end

function Sparkle:LinearTranslate (elapsed)
	-- Parameters for LinearTranslate:
	-- 1 - relativePoint, 2 - xStart, 3 - xStop, 4 - yStart, 5 - yStop, 6 - duration
	local relativePoint, xStart, xStop, yStart, yStop, duration = self.param1, self.param2, self.param3, self.param4, self.param5, self.param6;
	
	self:Show();
	self.elapsed = self.elapsed + elapsed;
	local xRange = xStart - xStop;
	local yRange = yStart - yStop;
	local xDir = ( xStart < xStop and 1 ) or -1;
	local yDir = ( yStart < yStop and 1 ) or -1;
	
	local position = self.elapsed / duration;
	self:SetPoint("CENTER", "$parent", relativePoint, xStart + (math.abs(xRange * position)*xDir), yStart + (math.abs(yRange * position)*yDir));
	if position >= 1 and self.loop then
		self.elapsed = 0;
	elseif position >= 1 then
		return true;
	end
	
	return false;
end

function Sparkle:Pulse (elapsed)
	-- Parameters for Sparkle:
	-- relativePoint, xPos, yPos, startAlpha, maxAlpha, endAlpha, fadeInDuration, holdDuration, fadeOutDuration
	
	local relativePoint, xPos, yPos = self.param1, self.param2, self.param3;
	local startAlpha, maxAlpha, endAlpha = self.param4, self.param5, self.param6;
	local fadeInDuration, holdDuration, fadeOutDuration = self.param7, self.param8, self.param9;
	
	self:Show();
	self:SetPoint("CENTER", "$parent", relativePoint, xPos, yPos);
	self.elapsed = self.elapsed + elapsed;
	if self.elapsed <= fadeInDuration then
		local range = maxAlpha - startAlpha;
		local percentage = self.elapsed/fadeInDuration;
		self:SetAlpha(startAlpha + (range*percentage));
	elseif self.elapsed <= holdDuration then
		self:SetAlpha(maxAlpha);
	elseif self.elapsed <= fadeOutDuration then
		local range = maxAlpha - endAlpha;
		local percentage = (self.elapsed-holdDuration)/(fadeOutDuration-holdDuration);
		self:SetAlpha(maxAlpha - (range*percentage));
	elseif self.loop then
		self.elapsed = 0;
	else
		return true;
	end
	
	return false;
end

local cos = cos;
local sin = sin;

local cosTable = {};
local sinTable = {};

function Sparkle:RadialTranslate (elapsed)
	-- Parameters for RadialTranslate:
	-- relativePoint, radius, startDegree, stopDegree, duration
	local relativePoint, offsetX, offsetY, radius, startDegree, stopDegree, duration = self.param1, self.param2, self.param3, self.param4, self.param5, self.param6, self.param7;
	
	self:Show();
	self.elapsed = self.elapsed + elapsed;
	local range = startDegree - stopDegree;
	local position = self.elapsed/duration
	local degree = math.floor(startDegree + (range * position));
	
	local xPos, yPos
	local cosVal = cosTable[degree];
	if cosVal then
		xPos = offsetX + (radius * cosVal);
	else
		cosTable[degree] = cos(degree);
		xPos = offsetX + (radius * cosTable[degree]);
	end
	
	local sinVal = sinTable[degree];
	if sinVal then
		yPos = offsetY + (radius * sinVal);
	else
		sinTable[degree] = sin(degree);
		yPos = offsetY + (radius + sinTable[degree]);
	end
	
	local xPos = offsetX + (radius * cos(degree));
	local yPos = offsetY + (radius * sin(degree));
	self:SetPoint("CENTER", "$parent", relativePoint, xPos, yPos);
	if position >= 1 and self.loop then
		self.elapsed = 0;
	elseif position >= 1 then
		return true;
	end
	
	return false;
end

function SparkleFrame:New (parent)
	local sparkleFrame;
	if parent then
		sparkleFrame = CreateFrame("FRAME", parent:GetName() .. "SparkleFrame", parent);
		sparkleFrame:SetPoint("TOPLEFT");
		sparkleFrame:SetPoint("BOTTOMRIGHT");
	else
		sparkleFrame = CreateFrame("FRAME");
	end
	
	setmetatable(sparkleFrame, self);
	self.__index = self;

	sparkleFrame.timeSinceLast = 0;
	sparkleFrame.updateTime = 0.0334 -- Roughly 30 frames per second.
	sparkleFrame.numSparkles = 0;
	sparkleFrame.freeSparkles = {};
	sparkleFrame.sparkles = {};	
	
	sparkleFrame:SetScript("OnUpdate", sparkleFrame.OnUpdate);
	
	return sparkleFrame;
end

function SparkleFrame:SetFrameRate(framesPerSec)
	self.updateTime = 1/(framesPerSec or 30);
end

function SparkleFrame:OnUpdate(elapsed)
	local timeSinceLast = self.timeSinceLast + elapsed;
	if timeSinceLast >= self.updateTime then
		local sparkles = self.sparkles;
		for i = #sparkles, 1, -1 do
			if sparkles[i] and sparkles[i]:Animate(timeSinceLast) then
				sparkles[i]:Free();
				tremove(sparkles, i);
			end
		end
		self.timeSinceLast = 0;
		return;
	end
	self.timeSinceLast = timeSinceLast;
end

function SparkleFrame:StartAnimation (name, animationType, sparkleTemplate, loop, ...)
	if not Sparkle[animationType] then
		return;
	end
	
	local sparkle = Sparkle:New(self, sparkleTemplate);
	sparkle.numArgs = select("#", ...);
	for i = 1, sparkle.numArgs do 
		sparkle["param"..i] = select(i, ...);
	end
	
	sparkle.name = name;
	sparkle.elapsed = 0;
	sparkle.loop = loop;
	sparkle.Animate = Sparkle[animationType];
	
	return sparkle
end

function SparkleFrame:EndAnimation (name)
	local sparkles, sparkle = self.sparkles;
	for i = #sparkles, 1, -1 do
		if sparkles[i].name == name then
			sparkles[i]:Free();
			tremove(sparkles, i);
		end
	end
end

function SparkleFrame:SetAnimationVertexColor (name, r, g, b, a)
	for i, sparkle in next, self.sparkles do
		if sparkle.name == name then
			sparkle:SetVertexColor(r, g, b, a);
		end
	end
end
