--"Globals"
local aceDB = LibStub("AceDB-3.0");
local aceCDialog = LibStub("AceConfigDialog-3.0");
local aceConfig = LibStub("AceConfig-3.0");
local libSharedMedia = LibStub("LibSharedMedia-3.0");
local libDRData = LibStub('DRData-1.0');

--gets called when a cd is finished, reassigns the cds to frames.
function Rect:ReassignCds(which)
	local db =  Rect.db.profile;
	--bail out early, if frames are disabled
	if not db[which]["enabled"] or not db["enabled"] then return end;
	--first hide all
	for i = 1, 23 do
		local frame = Rect.frames[which][i]["frame"];
		frame:Hide();
		local colorframe = Rect.frames[which][i]["colorframe"];
		colorframe:Hide();
	end
	--check if frames are unlocked
	if not db["locked"] then return end;
	--check if we need to display them for the player
	if not db["selfCDRegister"] and self.targets[which] == UnitGUID("player") then return end;
	--check if we have cooldown for that unit
	if not self.cds[self.targets[which]] then return end;
	--update cds
	Rect:UpdateCds(which);
	--sort them
	local tmp = Rect:SortCDs(which);
	--let's fill them up
	local i = 1;
	for k, v in ipairs(tmp) do
		local frame = Rect.frames[which][i]["frame"];
		local text = Rect.frames[which][i]["texture"];
		text:SetTexture(v["spellIcon"]);
		local CoolDown = Rect.frames[which][i]["cooldown"];
		CoolDown:SetCooldown(v["currentTime"], v["cd"]);
		frame:Show();
		if (db[which]["colorframeenabled"]) then
			local colorframe = Rect.frames[which][i]["colorframe"];
			--self:Print(v["spellID"] .. " cat: " .. v["spellCategory"]);
			
			colorframe:SetBackdropColor(db["color"][v["spellCategory"]]["r"], 
					db["color"][v["spellCategory"]]["g"], 
					db["color"][v["spellCategory"]]["b"], 
					db["color"][v["spellCategory"]]["a"]);
			colorframe:Show();
		end
		i = i + 1;
	end
end

function Rect:AddCd(srcGUID, spellID)
	local db =  Rect.db.profile;
	if not db["enabled"] then return end;
	
	if not Rect.cds[srcGUID] then 
		Rect.cds[srcGUID] = {}
		Rect.cds[srcGUID]["spec"] = {};
		Rect.cds[srcGUID]["spec"][1] = 1;
		Rect.cds[srcGUID]["spec"][2] = "";
	end
	
	local specchange = false;
	if db["specdetection"] then
		if Rect:DetectSpec(srcGUID, spellID) then
			specchange = true;
		end
	end
	
	local spec = Rect.cds[srcGUID]["spec"][1];
	local class, isPet = Rect.spells[spellID][7], Rect.spells[spellID][9];
	local cd, reset, spellCategory = Rect.spells[spellID][spec], Rect.spells[spellID][2], Rect.spells[spellID][8];
	
	if db["petcdguessing"] then
		if (Rect.cds[srcGUID]["spec"][2] == "") and class then
			Rect.cds[srcGUID]["spec"][2] = class;
		end
	end
	
	if specchange and (not cd) then
		if self.targets["target"] == srcGUID then
			self:ReassignCds("target");
		end
		
		if self.targets["focus"] == srcGUID then
			self:ReassignCds("focus");
		end
	end
	
	--means spec detection spell
	if not cd then return end;
	
	local spellName, spellRank, spellIcon = GetSpellInfo(spellID);
	local currentTime = GetTime();
	local endTime = currentTime + cd;
	Rect.cds[srcGUID][spellID] = {
		currentTime,
		endTime,
		cd,
		spellIcon,
		spellID,
		spellCategory
	}
	
	--add it to every class of the same type
	if db["petcdguessing"] and isPet then
		for k, v in pairs(Rect.cds) do
			if (v["spec"][2] == class) then
				Rect.cds[k][spellID] = {
					currentTime,
					endTime,
					cd,
					spellIcon,
					spellID,
					spellCategory
				}
			end
		end
	end
	
	--self:Print(Rect.cds[srcGUID][spellID][1] .. " " .. Rect.cds[srcGUID][spellID][2] .. " " .. Rect.cds[srcGUID][spellID][3]);
	
	if reset then
		Rect:CdRemoval(srcGUID, reset);
	end
	
	--self:Print(self.targets["target"]);
	--self:Print(s
	
	if self.targets["target"] == srcGUID or isPet then
		self:ReassignCds("target");
	end
	
	if self.targets["focus"] == srcGUID or isPet then
		self:ReassignCds("focus");
	end
end

function Rect:DetectSpec(srcGUID, spellID)
	local spec = Rect.spells[spellID][6];
	
	if spec == 0 then return false end;
	if Rect.cds[srcGUID]["spec"][1] == spec then return false end;
	--self:Print("spec found: " .. spec);
	Rect:RemapSpecCDs(srcGUID, spec);
	return true;
end

function Rect:RemapSpecCDs(srcGUID, spec)
	if not self.cds[srcGUID] then return end
	for k, v in pairs(self.cds[srcGUID]) do
		if not (k == "spec") then
			local cd = Rect.spells[v[5]][spec];
			v[3] = cd;
			v[2] = v[1] + cd;
		end
	end
end

function Rect:CdRemoval(srcGUID, resetArray)
	if not self.cds[srcGUID] then return end
	for k, v in pairs(self.cds[srcGUID]) do
		if not (k == "spec") then
			for j, x in pairs(resetArray) do
				if v[5] == x then
					--self:Print("Removed cd: " .. v[5]);
					self.cds[srcGUID][v[5]] = nil;
				end
			end
		end
	end
end

function Rect:SortCDs(which)
	local db = Rect.db.profile;
	local tmp = {};

	--make the tmp table
	local i = 1;
	for k, v in pairs(self.cds[self.targets[which]]) do
		if not (k == "spec") then
			tmp[i] = {
				currentTime = v[1],
				endTime = v[2],
				cd = v[3],
				spellIcon = v[4],
				spellID = v[5],
				spellCategory = v[6]
				};
				
			--self:Print(v[1] .. v[2] .. v[3] .. v[4] .. v[5])
			--self:Print(tmp[i]["currentTime"] .. " " .. tmp[i]["endTime"] .. " " .. tmp[i]["cd"]);-- .. " " .. tmp[i][4] .. " " .. tmp[i][5])
			i = i + 1;
		end
	end

	if not tmp then return end;
	
	if not db["cdtypesortorder"]["enabled"] then
		if db[which]["sortOrder"] == "1" then --["1"] = "Ascending (CD left)",
			table.sort(tmp, function(a, b) return Rect:ComparerAscendingCDLeft(a, b) end);
		elseif db[which]["sortOrder"] == "2" then --["2"] = "Descending (CD left)",
			table.sort(tmp, function(a, b) return Rect:ComparerDescendingCDLeft(a, b) end);
		elseif db[which]["sortOrder"] == "3" then --["3"] = "Ascending (CD total)",
			table.sort(tmp, function(a, b) return Rect:ComparerAscendingCDTotal(a, b) end);
		elseif db[which]["sortOrder"] == "4" then --["4"] = "Descending (CD total)",
			table.sort(tmp, function(a, b) return Rect:ComparerDescendingCDTotal(a, b) end);
		elseif db[which]["sortOrder"] == "5" then --["5"] = "Recent first",
			table.sort(tmp, function(a, b) return Rect:ComparerRecentFirst(a, b) end);
		elseif db[which]["sortOrder"] == "6" then --["6"] = "Recent Last",
			table.sort(tmp, function(a, b) return Rect:ComparerRecentLast(a, b) end);
		end --["7"] = "No order"
	else
		if db[which]["sortOrder"] == "1" then --["1"] = "Ascending (CD left)",
			table.sort(tmp, function(a, b) return Rect:ComparerAscendingCDLeftT(a, b) end);
		elseif db[which]["sortOrder"] == "2" then --["2"] = "Descending (CD left)",
			table.sort(tmp, function(a, b) return Rect:ComparerDescendingCDLeftT(a, b) end);
		elseif db[which]["sortOrder"] == "3" then --["3"] = "Ascending (CD total)",
			table.sort(tmp, function(a, b) return Rect:ComparerAscendingCDTotalT(a, b) end);
		elseif db[which]["sortOrder"] == "4" then --["4"] = "Descending (CD total)",
			table.sort(tmp, function(a, b) return Rect:ComparerDescendingCDTotalT(a, b) end);
		elseif db[which]["sortOrder"] == "5" then --["5"] = "Recent first",
			table.sort(tmp, function(a, b) return Rect:ComparerRecentFirstT(a, b) end);
		elseif db[which]["sortOrder"] == "6" then --["6"] = "Recent Last",
			table.sort(tmp, function(a, b) return Rect:ComparerRecentLastT(a, b) end);
		end --["7"] = "No order"
	end
	
	return tmp;
end

function Rect:CreateFrames(which)
	for i = 1, 23 do
		local frame = CreateFrame("Frame", nil, UIParent, nil);
		frame:SetFrameStrata("MEDIUM");
		frame:SetWidth(150);
		frame:SetHeight(150);
		if i == 1 then
			frame:SetScript("OnUpdate", function() self:VOnTimerUpdate(which) end)
		end
		local text = frame:CreateTexture();
		text:SetTexture("Interface\\Icons\\Spell_Arcane_Blink")
		text:SetAllPoints(frame);
		frame.texture = text;
		local CoolDown = CreateFrame("Cooldown", "RectCoolDown" .. i, frame);
		CoolDown:SetAllPoints()
		CoolDown:SetCooldown(GetTime(), 50);
		frame:Hide();
		
		local colorframe = CreateFrame("Frame", nil, UIParent, nil);
		colorframe:SetFrameStrata("BACKGROUND");
		colorframe:SetWidth(150);
		colorframe:SetHeight(150);
		colorframe:SetBackdrop({bgFile = "Interface/Tooltips/UI-Tooltip-Background", 
					--edgeFile = "Interface/Tooltips/UI-Tooltip-Border", 
					edgeFile = nil,
					tile = true, tileSize = 16, edgeSize = 0, 
					--insets = { left = 4, right = 4, top = 4, bottom = 4 }});
					insets = nil});
		colorframe:Hide();
		
		Rect.frames[which][i] = {}
		Rect.frames[which][i]["frame"] = frame;
		Rect.frames[which][i]["texture"] = text;
		Rect.frames[which][i]["cooldown"] = CoolDown;
		Rect.frames[which][i]["colorframe"] = colorframe;
	end
end

function Rect:MoveTimersStop(which)
	local db = Rect.db.profile;
	local x = db[which]["xPos"];
	local y = db[which]["yPos"];
	local size = db[which]["size"];
	local growOrder = db[which]["growOrder"];
	local cdbacksize = db[which]["colorframesize"];
	
	for i = 1, 23 do
		local frame = Rect.frames[which][i]["frame"];
		frame:ClearAllPoints();
		frame:SetFrameStrata("MEDIUM");
		frame:SetWidth(size);
		frame:SetHeight(size);
		local text = Rect.frames[which][i]["texture"];
		text:SetAllPoints(frame);
		frame.texture = text;
		
		local colorframe = Rect.frames[which][i]["colorframe"];
		colorframe:ClearAllPoints();
		colorframe:SetFrameStrata("BACKGROUND");
		colorframe:SetBackdropColor(1, 1, 1, 1);
		
		--set them based on the grow type
		if growOrder == "1" then --Up
			frame:SetPoint("BOTTOMLEFT", x, y + ((i - 1) * size));
			
			colorframe:SetWidth(size + (2 * cdbacksize));
			colorframe:SetHeight(size);
			colorframe:SetPoint("BOTTOMLEFT", x - cdbacksize, y + ((i - 1) * size));
		elseif growOrder == "2" then --Right
			frame:SetPoint("BOTTOMLEFT", x + ((i - 1) * size), y);
			
			colorframe:SetWidth(size);
			colorframe:SetHeight(size + (2 * cdbacksize));
			colorframe:SetPoint("BOTTOMLEFT", x + ((i - 1) * size), y - cdbacksize);
		elseif growOrder == "3" then --Down
			frame:SetPoint("BOTTOMLEFT", x, y - ((i - 1) * size));
			
			colorframe:SetWidth(size + (2 * cdbacksize));
			colorframe:SetHeight(size);
			colorframe:SetPoint("BOTTOMLEFT", x - cdbacksize, (y - ((i - 1) * size)));
		else --Left
			frame:SetPoint("BOTTOMLEFT", x - ((i - 1) * size), y);
			
			colorframe:SetWidth(size);
			colorframe:SetHeight(size + (2 * cdbacksize));
			colorframe:SetPoint("BOTTOMLEFT", x - ((i - 1) * size), y - cdbacksize);
		end
		local CoolDown = Rect.frames[which][i]["cooldown"];
		CoolDown:SetAllPoints();
		--frame:Show();
	end
end

function Rect:VOnTimerUpdate(which)
	if (Rect:UpdateCds(which)) then
		--we have to update both, because if somebody is targeted and focused since sorting is 
		--implemented it triggers only one update, probably it had bugs before too, but got unnoticed
		self:ReassignCds("target");
		self:ReassignCds("focus");
	end
end

function Rect:UpdateCds(which)
	--check if we have cooldown for that unit
	if not self.cds[self.targets[which]] then return end
	local t = GetTime();
	local found = false;
	--let's check if one of the cooldowns finished
	for k, v in pairs(self.cds[self.targets[which]]) do
		if not (k == "spec") then
			if v[2] <= t then
				self.cds[self.targets[which]][v[5]] = nil;
				found = true;
			end
		end
	end
	return found;
end