Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
############################################################################## ]]--
--[[ GLOBALS ]]--
local _G = _G;
local unpack        = _G.unpack;
local select        = _G.select;
local assert        = _G.assert;
local type          = _G.type;
local error         = _G.error;
local pcall         = _G.pcall;
local print         = _G.print;
local ipairs        = _G.ipairs;
local pairs         = _G.pairs;
local tostring      = _G.tostring;
local tonumber      = _G.tonumber;

--STRING
local string        = _G.string;
local upper         = string.upper;
local format        = string.format;
local find          = string.find;
local match         = string.match;
local gsub          = string.gsub;
--TABLE
local table 		= _G.table;
local tinsert       = _G.tinsert;
local tremove       = _G.tremove;
local twipe 		= _G.wipe;
--MATH
local math      	= _G.math;
local random 		= math.random;
local min 			= math.min;
local floor         = math.floor
local ceil          = math.ceil
--BLIZZARD API
local GameTooltip          	= _G.GameTooltip;
local InCombatLockdown     	= _G.InCombatLockdown;
local CreateFrame          	= _G.CreateFrame;
local GetTime         		= _G.GetTime;
local GetItemCooldown       = _G.GetItemCooldown;
local GetItemCount         	= _G.GetItemCount;
local GetItemInfo          	= _G.GetItemInfo;
local GetSpellInfo         	= _G.GetSpellInfo;
local IsSpellKnown         	= _G.IsSpellKnown;
local GetProfessions       	= _G.GetProfessions;
local GetProfessionInfo    	= _G.GetProfessionInfo;
local hooksecurefunc     	= _G.hooksecurefunc;
--[[
##########################################################
ADDON
##########################################################
]]--
local SV = select(2, ...)
local L = SV.L;
--[[
##########################################################
DOCKING
##########################################################
]]--
local ORDER_TEMP = {};
local ORDER_TEST = {};

local SPARK_ANIM = [[Interface\AddOns\SVUI\assets\artwork\Doodads\DOCK-SPARKS-]];

local DOCK_LOCATIONS = {
	["BottomLeft"] = {1, "LEFT", true, "ANCHOR_TOPLEFT"},
	["BottomRight"] = {-1, "RIGHT", true, "ANCHOR_TOPLEFT"},
	["TopLeft"] = {1, "LEFT", false, "ANCHOR_BOTTOMLEFT"},
	["TopRight"] = {-1, "RIGHT", false, "ANCHOR_BOTTOMLEFT"},
};

local Dock = SV:NewClass("Dock", L["Docks"]);

Dock.Border = {};
Dock.Registration = {};
Dock.Locations = {};

local DOCK_DROPDOWN_OPTIONS = {};

DOCK_DROPDOWN_OPTIONS["BottomLeft"] = { text = "To BottomLeft", func = function(button) Dock.BottomLeft.Bar:Add(button) end };
DOCK_DROPDOWN_OPTIONS["BottomRight"] = { text = "To BottomRight", func = function(button) Dock.BottomRight.Bar:Add(button) end };
DOCK_DROPDOWN_OPTIONS["TopLeft"] = { text = "To TopLeft", func = function(button) Dock.TopLeft.Bar:Add(button) end };
--DOCK_DROPDOWN_OPTIONS["TopRight"] = { text = "To TopRight", func = function(button) Dock.TopRight.Bar:Add(button) end };
--[[
##########################################################
SOUND EFFECTS
##########################################################
]]--
local ButtonSound = SV.Sounds:Blend("DockButton", "Buttons", "Levers");
local ErrorSound = SV.Sounds:Blend("Malfunction", "Sparks", "Wired");
--[[
##########################################################
CORE FUNCTIONS
##########################################################
]]--
_G.ToggleSuperDockLeft = function(self, button)
	GameTooltip:Hide()
	if(button and IsAltKeyDown()) then
		SV:StaticPopup_Show('RESETDOCKS_CHECK')
	elseif(button and button == 'RightButton') then
		if(InCombatLockdown()) then
			ErrorSound()
			SV:AddonMessage(ERR_NOT_IN_COMBAT)
			return
		end
		ButtonSound()
		local userSize = SV.db.Dock.dockLeftHeight
		if(not SV.cache.Docks.LeftExpanded) then
			SV.cache.Docks.LeftExpanded = true
			Dock.BottomLeft.Window:SetHeight(userSize + 300)
		else
			SV.cache.Docks.LeftExpanded = nil
			Dock.BottomLeft.Window:SetHeight(userSize)
		end
		Dock.BottomLeft.Bar:Update()
		Dock:UpdateDockBackdrops()
		SV.Events:Trigger("DOCK_LEFT_EXPANDED");
	else
		if SV.cache.Docks.LeftFaded then
			SV.cache.Docks.LeftFaded = nil;
			Dock.BottomLeft:FadeIn(0.2, Dock.BottomLeft:GetAlpha(), 1)
			Dock.BottomLeft.Bar:FadeIn(0.2, Dock.BottomLeft.Bar:GetAlpha(), 1)
			SV.Events:Trigger("DOCK_LEFT_FADE_IN");
			PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
		else
			SV.cache.Docks.LeftFaded = true;
			Dock.BottomLeft:FadeOut(0.2, Dock.BottomLeft:GetAlpha(), 0)
			Dock.BottomLeft.Bar:FadeOut(0.2, Dock.BottomLeft.Bar:GetAlpha(), 0)
			SV.Events:Trigger("DOCK_LEFT_FADE_OUT");
			PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
		end
	end
end

_G.ToggleSuperDockRight = function(self, button)
	GameTooltip:Hide()
	if(button and IsAltKeyDown()) then
		SV:StaticPopup_Show('RESETDOCKS_CHECK')
	elseif(button and button == 'RightButton') then
		if(InCombatLockdown()) then
			ErrorSound()
			SV:AddonMessage(ERR_NOT_IN_COMBAT)
			return
		end
		ButtonSound()
		local userSize = SV.db.Dock.dockRightHeight
		if(not SV.cache.Docks.RightExpanded) then
			SV.cache.Docks.RightExpanded = true
			Dock.BottomRight.Window:SetHeight(userSize + 300)
		else
			SV.cache.Docks.RightExpanded = nil
			Dock.BottomRight.Window:SetHeight(userSize)
		end
		Dock.BottomRight.Bar:Update()
		Dock:UpdateDockBackdrops()
		SV.Events:Trigger("DOCK_RIGHT_EXPANDED");
	else
		if SV.cache.Docks.RightFaded then
			SV.cache.Docks.RightFaded = nil;
			Dock.BottomRight:FadeIn(0.2, Dock.BottomRight:GetAlpha(), 1)
			Dock.BottomRight.Bar:FadeIn(0.2, Dock.BottomRight.Bar:GetAlpha(), 1)
			SV.Events:Trigger("DOCK_RIGHT_FADE_IN");
			PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
		else
			SV.cache.Docks.RightFaded = true;
			Dock.BottomRight:FadeOut(0.2, Dock.BottomRight:GetAlpha(), 0)
			Dock.BottomRight.Bar:FadeOut(0.2, Dock.BottomRight.Bar:GetAlpha(), 0)
			SV.Events:Trigger("DOCK_RIGHT_FADE_OUT");
			PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
		end
	end
end

_G.ToggleSuperDocks = function()
	if(SV.cache.Docks.AllFaded) then
		SV.cache.Docks.AllFaded = nil;
		SV.cache.Docks.LeftFaded = nil;
		SV.cache.Docks.RightFaded = nil;
		Dock.BottomLeft:FadeIn(0.2, Dock.BottomLeft:GetAlpha(), 1)
		Dock.BottomLeft.Bar:FadeIn(0.2, Dock.BottomLeft.Bar:GetAlpha(), 1)
		SV.Events:Trigger("DOCK_LEFT_FADE_IN");
		Dock.BottomRight:FadeIn(0.2, Dock.BottomRight:GetAlpha(), 1)
		Dock.BottomRight.Bar:FadeIn(0.2, Dock.BottomRight.Bar:GetAlpha(), 1)
		SV.Events:Trigger("DOCK_RIGHT_FADE_IN");
		PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
	else
		SV.cache.Docks.AllFaded = true;
		SV.cache.Docks.LeftFaded = true;
		SV.cache.Docks.RightFaded = true;
		Dock.BottomLeft:FadeOut(0.2, Dock.BottomLeft:GetAlpha(), 0)
		Dock.BottomLeft.Bar:FadeOut(0.2, Dock.BottomLeft.Bar:GetAlpha(), 0)
		SV.Events:Trigger("DOCK_LEFT_FADE_OUT");
		Dock.BottomRight:FadeOut(0.2, Dock.BottomRight:GetAlpha(), 0)
		Dock.BottomRight.Bar:FadeOut(0.2, Dock.BottomRight.Bar:GetAlpha(), 0)
		SV.Events:Trigger("DOCK_RIGHT_FADE_OUT");
		PlaySoundFile([[sound\doodad\be_scryingorb_explode.ogg]])
	end
end

function Dock:EnterFade()
	if SV.cache.Docks.LeftFaded then
		self.BottomLeft:FadeIn(0.2, self.BottomLeft:GetAlpha(), 1)
		self.BottomLeft.Bar:FadeIn(0.2, self.BottomLeft.Bar:GetAlpha(), 1)
		SV.Events:Trigger("DOCK_LEFT_FADE_IN");
	end
	if SV.cache.Docks.RightFaded then
		self.BottomRight:FadeIn(0.2, self.BottomRight:GetAlpha(), 1)
		self.BottomRight.Bar:FadeIn(0.2, self.BottomRight.Bar:GetAlpha(), 1)
		SV.Events:Trigger("DOCK_RIGHT_FADE_IN");
	end
end

function Dock:ExitFade()
	if SV.cache.Docks.LeftFaded then
		self.BottomLeft:FadeOut(2, self.BottomLeft:GetAlpha(), 0)
		self.BottomLeft.Bar:FadeOut(2, self.BottomLeft.Bar:GetAlpha(), 0)
		SV.Events:Trigger("DOCK_LEFT_FADE_OUT");
	end
	if SV.cache.Docks.RightFaded then
		self.BottomRight:FadeOut(2, self.BottomRight:GetAlpha(), 0)
		self.BottomRight.Bar:FadeOut(2, self.BottomRight.Bar:GetAlpha(), 0)
		SV.Events:Trigger("DOCK_RIGHT_FADE_OUT");
	end
end
--[[
##########################################################
SET DOCKBAR FUNCTIONS
##########################################################
]]--
local RefreshDockWindows = function(self)
	--print('RefreshDockWindows')
	local dd = self.Data.Default
	local button = _G[dd]
	local default
	if(button) then
		default = button:GetAttribute("ownerFrame")
	end
	for name,window in pairs(self.Data.Windows) do
		if(window ~= default) then
			if(window.DockButton) then
				window.DockButton:Deactivate()
			end
		end
	end
end

local RefreshDockButtons = function(self)
	--print('RefreshDockButtons')
	for name,docklet in pairs(Dock.Registration) do
		if(docklet) then
			if(docklet.DockButton) then
				docklet.DockButton:Deactivate()
			end
			--docklet:FadeOut(0.1, 1, 0, true)
		end
	end
end

local GetDefault = function(self)
	--print('GetDefault')
	local default = self.Data.Default
	local button = _G[default]
	if(button) then
		local window = button:GetAttribute("ownerFrame")
		if window and _G[window] then
			self:Refresh()
			self.Parent.Window.FrameLink = _G[window]
			self.Parent.Window:FadeIn()
			_G[window]:Show()
			button:Activate()
		end
	end
end

local OldDefault = function(self)
	--print('OldDefault')
	local default = self.Data.OriginalDefault
	local button = _G[default]
	if(button) then
		local window = button:GetAttribute("ownerFrame")
		if window and _G[window] then
			self:Refresh()
			self.Parent.Window.FrameLink = _G[window]
			self.Parent.Window:FadeIn()
			_G[window]:Show()
			button:Activate()
		end
	end
end

local ToggleDockletWindow = function(self, button)
	--print('ToggleDockletWindow')
	local frame  = button.FrameLink
	if(frame) then
		self.Parent.Window.FrameLink = frame
		self.Parent.Window:FadeIn()
		self:Cycle()
		--frame:FadeIn()
		button:Activate()
	else
		button:Deactivate()
		self:GetDefault()
	end
end

local AlertActivate = function(self, child)
	local size = SV.db.Dock.buttonSize or 22;
	self:SetHeightToScale(size)
	self.backdrop:Show()
	child:ClearAllPoints()
	child:SetAllPoints(self)
end

local AlertDeactivate = function(self)
	self.backdrop:Hide()
	self:SetHeightToScale(1)
end

local Docklet_OnShow = function(self)
	--print('Docklet_OnShow')
	if(self.FrameLink) then
		if(not InCombatLockdown()) then
			self.FrameLink:SetFrameLevel(10)
		end
		self.FrameLink:FadeIn()
	end
end

local Docklet_OnHide = function(self)
	--print('Docklet_OnHide')
	if(self.FrameLink) then
		if(not InCombatLockdown()) then
			self.FrameLink:SetFrameLevel(0)
			self.FrameLink:Hide()
		else
			self.FrameLink:FadeOut(0.2, 1, 0, true)
		end
	end
end

local DockButtonMakeDefault = function(self)
	self.Parent.Data.Default = self:GetName()
	self.Parent:GetDefault()
	if(not self.Parent.Data.OriginalDefault) then
		self.Parent.Data.OriginalDefault = self:GetName()
	end
end

local DockButtonActivate = function(self)
	--print('DockButtonActivate')
	self:SetAttribute("isActive", true)
	self:SetPanelColor("green")
	self.Icon:SetGradient(unpack(SV.Media.gradient.green))
	if(self.FrameLink) then
		if(not InCombatLockdown()) then
			self.FrameLink:SetFrameLevel(10)
		end
		self.FrameLink:FadeIn()
	end
end

local DockButtonDeactivate = function(self)
	--print('DockButtonDeactivate')
	if(self.FrameLink) then
		if(not InCombatLockdown()) then
			self.FrameLink:SetFrameLevel(0)
			self.FrameLink:Hide()
		else
			self.FrameLink:FadeOut(0.2, 1, 0, true)
		end
	end
	self:SetAttribute("isActive", false)
	self:SetPanelColor("default")
	self.Icon:SetGradient(unpack(SV.Media.gradient.icon))
end

local DockButton_OnEnter = function(self, ...)
	Dock:EnterFade()

	self:SetPanelColor("highlight")
	self.Icon:SetGradient(unpack(SV.Media.gradient.highlight))

	GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT", 0, 4)
	GameTooltip:ClearLines()
	local tipText = self:GetAttribute("tipText")
	GameTooltip:AddDoubleLine("[Left-Click]", tipText, 0, 1, 0, 1, 1, 1)
	local tipExtraText = self:GetAttribute("tipExtraText")
	GameTooltip:AddDoubleLine("[Right-Click]", tipExtraText, 0, 1, 0, 1, 1, 1)
	GameTooltip:AddLine(" ")
	GameTooltip:AddDoubleLine("[Alt + Click]", "Reset Dock Buttons", 0, 0.5, 1, 0.5, 1, 0.5)
	GameTooltip:Show()
end

local DockletButton_OnEnter = function(self, ...)
	Dock:EnterFade()

	self:SetPanelColor("highlight")
	self.Icon:SetGradient(unpack(SV.Media.gradient.highlight))

	local tipAnchor = self:GetAttribute("tipAnchor")
	GameTooltip:SetOwner(self, tipAnchor, 0, 4)
	GameTooltip:ClearLines()
	if(self.CustomTooltip) then
		self:CustomTooltip()
	else
		local tipText = self:GetAttribute("tipText")
		GameTooltip:AddDoubleLine("[Left-Click]", tipText, 0, 1, 0, 1, 1, 1)
	end
	if(self:GetAttribute("hasDropDown") and self.GetMenuList) then
		GameTooltip:AddLine(" ")
		GameTooltip:AddDoubleLine("[Alt + Click]", "Docking Options", 0, 0.5, 1, 0.5, 1, 0.5)
	end
	GameTooltip:Show()
end

local DockletButton_OnLeave = function(self, ...)
	Dock:ExitFade()

	if(self:GetAttribute("isActive")) then
		self:SetPanelColor("green")
		self.Icon:SetGradient(unpack(SV.Media.gradient.green))
	else
		self:SetPanelColor("default")
		self.Icon:SetGradient(unpack(SV.Media.gradient.icon))
	end

	GameTooltip:Hide()
end

local DockletButton_OnClick = function(self, button)
	--if InCombatLockdown() then return end
	self.Sparks:SetTexture(SPARK_ANIM .. random(1,3))
	self.Sparks.anim:Play()
	ButtonSound()
	if(IsAltKeyDown() and (not InCombatLockdown()) and self:GetAttribute("hasDropDown") and self.GetMenuList) then
		local list = self:GetMenuList();
		SV.Dropdown:Open(self, list);
	else
		if self.PostClickFunction then
			self:PostClickFunction()
		else
			self.Parent:Toggle(self)
		end
	end
end

local DockletButton_OnPostClick = function(self, button)
	if InCombatLockdown() then
		ErrorSound()
		return
	end
	ButtonSound()
	if(IsAltKeyDown() and self:GetAttribute("hasDropDown") and self.GetMenuList) then
		local list = self:GetMenuList();
		SV.Dropdown:Open(self, list);
	end
end

local DockletEnable = function(self)
	local dock = self.Parent;
	if(self.DockButton) then dock.Bar:Add(self.DockButton) end
end

local DockletDisable = function(self)
	local dock = self.Parent;
	if(self.DockButton) then dock.Bar:Remove(self.DockButton) end
end

local DockletButtonSize = function(self)
	local size = self.Bar.ToolBar:GetHeight() or 30;
	return size;
end

local DockletRelocate = function(self, location)
	local newParent = Dock[location];

	if(not newParent) then return end

	if(self.DockButton) then
		newParent.Bar:Add(self.DockButton)
	end

	if(self.Bar) then
		local height = newParent.Bar.ToolBar:GetHeight();
		local mod = newParent.Bar.Data[1];
		local barAnchor = newParent.Bar.Data[2];
		local barReverse = SV:GetReversePoint(barAnchor);
		local spacing = SV.db.Dock.buttonSpacing;

		self.Bar:ClearAllPoints();
		self.Bar:SetPointToScale(barAnchor, newParent.Bar.ToolBar, barReverse, (spacing * mod), 0)
	end
end

local GetDockablePositions = function(self)
	local button = self;
	local name = button:GetName();
	local currentLocation = Dock.Locations[name];
	local t;

	if(self.GetPreMenuList) then
		t = self:GetPreMenuList();
		tinsert(t, { title = "Move This", divider = true })
	else
		t = {{ title = "Move This", divider = true }};
	end

	for location,option in pairs(DOCK_DROPDOWN_OPTIONS) do
		if(currentLocation ~= location) then
		    tinsert(t, option);
		end
	end

	tinsert(t, { title = "Re-Order", divider = true });

	for i=1, #button.Parent.Data.Order do
		if(i ~= button.OrderIndex) then
			local positionText = ("Position #%d"):format(i);
		    tinsert(t, { text = positionText, func = function() button.Parent:ChangeOrder(button, i) end });
		end
	end

	return t;
end

local ChangeBarOrder = function(self, button, targetIndex)
	local targetName = button:GetName();
	local currentIndex = button.OrderIndex;
	wipe(ORDER_TEST);
	wipe(ORDER_TEMP);
	for i = 1, #self.Data.Order do
		local nextName = self.Data.Order[i];
		if(i == targetIndex) then
			if(currentIndex > targetIndex) then
				tinsert(ORDER_TEMP, targetName)
				tinsert(ORDER_TEMP, nextName)
			else
				tinsert(ORDER_TEMP, nextName)
				tinsert(ORDER_TEMP, targetName)
			end
		elseif(targetName ~= nextName) then
			tinsert(ORDER_TEMP, nextName)
		end
	end

	wipe(self.Data.Order);
	local safeIndex = 1;
	for i = 1, #ORDER_TEMP do
		local nextName = ORDER_TEMP[i];
		local nextButton = self.Data.Buttons[nextName];
		if(nextButton and (not ORDER_TEST[nextName])) then
			ORDER_TEST[nextName] = true
			tinsert(self.Data.Order, nextName);
			nextButton.OrderIndex = safeIndex;
			safeIndex = safeIndex + 1;
		end
	end

	self:Update()
end

local RefreshBarOrder = function(self)
	wipe(ORDER_TEST);
	wipe(ORDER_TEMP);
	for i = 1, #self.Data.Order do
		local nextName = self.Data.Order[i];
		tinsert(ORDER_TEMP, nextName)
	end
	wipe(self.Data.Order);
	local safeIndex = 1;
	for i = 1, #ORDER_TEMP do
		local nextName = ORDER_TEMP[i];
		local nextButton = self.Data.Buttons[nextName];
		if(nextButton and (not ORDER_TEST[nextName])) then
			ORDER_TEST[nextName] = true
			tinsert(self.Data.Order, nextName);
			nextButton.OrderIndex = safeIndex;
			safeIndex = safeIndex + 1;
		end
	end
end

local CheckBarOrder = function(self, targetName)
	local found = false;
	for i = 1, #self.Data.Order do
		if(self.Data.Order[i] == targetName) then
			found = true;
		end
	end
	if(not found) then
		tinsert(self.Data.Order, targetName);
		self:UpdateOrder();
	end
end

local RefreshBarLayout = function(self)
	local anchor = upper(self.Data.Location)
	local mod = self.Data.Modifier
	local size = self.ToolBar:GetHeight();
	local count = #self.Data.Order;
	local offset = 1;
	local safeIndex = 1;
	for i = 1, count do
		local nextName = self.Data.Order[i];
		local nextButton = self.Data.Buttons[nextName];
		if(nextButton) then
			offset = (safeIndex - 1) * (size + 6) + 6
			nextButton:ClearAllPoints();
			nextButton:SetSize(size, size);
			nextButton:SetPoint(anchor, self.ToolBar, anchor, (offset * mod), 0);
			if(not nextButton:IsShown()) then
				nextButton:Show();
			end
			nextButton.OrderIndex = safeIndex;
			safeIndex = safeIndex + 1;
		end
	end

	self.ToolBar:SetWidth(offset + size);

	if(SV.Dropdown:IsShown()) then
		ToggleFrame(SV.Dropdown)
	end
end

local AddToDock = function(self, button)
	if not button then return end
	local name = button:GetName();
	if(self.Data.Buttons[name]) then return end

	local registeredLocation = Dock.Locations[name]
	local currentLocation = self.Data.Location

	if(registeredLocation) then
		if(registeredLocation ~= currentLocation) then
			if(Dock[registeredLocation].Bar.Data.Buttons[name]) then
				Dock[registeredLocation].Bar:Remove(button);
			else
				Dock[registeredLocation].Bar:Add(button);
				return
			end
		end
	end

	self.Data.Buttons[name] = button;
	self:CheckOrder(name);

	Dock.Locations[name] = currentLocation;
	button.Parent = self;
	button:SetParent(self.ToolBar);

	if(button.FrameLink) then
		local frame = button.FrameLink
		local frameName = frame:GetName()
		self.Data.Windows[frameName] = frame;
		Dock.Locations[frameName] = currentLocation;
		frame:Show()
		frame:ClearAllPoints()
		frame:SetParent(self.Parent.Window)
		frame:SetAllPointsIn(self.Parent.Window)
		frame.Parent = self.Parent
	end

	-- self:UpdateOrder()
	self:Update()
end

local RemoveFromDock = function(self, button)
	if not button then return end
	local name = button:GetName();
	local registeredLocation = Dock.Locations[name];
	local currentLocation = self.Data.Location

	if(registeredLocation and (registeredLocation == currentLocation)) then
		Dock.Locations[name] = nil;
	end

	for i = 1, #self.Data.Order do
		local nextName = self.Data.Order[i];
		if(nextName == name) then
			tremove(self.Data.Order, i);
			break;
		end
	end

	if(not self.Data.Buttons[name]) then return end

	button:Hide()
	if(button.FrameLink) then
		local frameName = button.FrameLink:GetName()
		Dock.Locations[frameName] = nil;
		button.FrameLink:FadeOut(0.2, 1, 0, true)
		self.Data.Windows[frameName] = nil;
	end

	button.OrderIndex = 0;
	self.Data.Buttons[name] = nil;
	self:UpdateOrder()
	self:Update()
end

local ActivateDockletButton = function(self, button, clickFunction, tipFunction, isAction)
	button.Activate = DockButtonActivate
	button.Deactivate = DockButtonDeactivate
	button.MakeDefault = DockButtonMakeDefault
	button.GetMenuList = GetDockablePositions

	if(tipFunction and type(tipFunction) == "function") then
		button.CustomTooltip = tipFunction
	end

	button.Parent = self
	button:SetPanelColor("default")
	button.Icon:SetGradient(unpack(SV.Media.gradient.icon))
	button:SetScript("OnEnter", DockletButton_OnEnter)
	button:SetScript("OnLeave", DockletButton_OnLeave)
	if(not isAction) then
		button:SetScript("OnClick", DockletButton_OnClick)
	else
		button:SetScript("PostClick", DockletButton_OnPostClick)
	end

	if(clickFunction and type(clickFunction) == "function") then
		button.PostClickFunction = clickFunction
	end
end

local CreateBasicToolButton = function(self, displayName, texture, onclick, globalName, tipFunction, primaryTemplate)
	local dockIcon = texture or [[Interface\AddOns\SVUI\assets\artwork\Icons\SVUI-ICON]];
	local size = self.ToolBar:GetHeight();
	local template = "SVUI_DockletButtonTemplate"

	if(primaryTemplate) then
		template = primaryTemplate .. ", SVUI_DockletButtonTemplate"
	end

	local button = _G[globalName .. "DockletButton"] or CreateFrame("Button", globalName, self.ToolBar, template)

	button:ClearAllPoints()
	button:SetSize(size, size)
	button:SetStylePanel("HeavyButton")
	button.Icon:SetTexture(dockIcon)
	button:SetAttribute("tipText", displayName)
	button:SetAttribute("tipAnchor", self.Data.TipAnchor)
    button:SetAttribute("ownerFrame", globalName)

    button.OrderIndex = 0;

    local sparkSize = size * 5;
    local sparkOffset = size * 0.5;

    local sparks = button.__border:CreateTexture(nil, "OVERLAY", nil, 2)
	sparks:SetSizeToScale(sparkSize, sparkSize)
	sparks:SetPoint("CENTER", button, "BOTTOMRIGHT", -sparkOffset, 4)
	sparks:SetTexture(SPARK_ANIM .. 1)
	sparks:SetVertexColor(0.7, 0.6, 0.5)
	sparks:SetBlendMode("ADD")
	sparks:SetAlpha(0)

	SV.Animate:Sprite8(sparks, 0.08, 2, false, true)

	button.Sparks = sparks;

    self:Add(button)
	self:Initialize(button, onclick, tipFunction, primaryTemplate)

	return button
end
--[[
##########################################################
DOCKS
##########################################################
]]--
for location, settings in pairs(DOCK_LOCATIONS) do
	Dock[location] = _G["SVUI_Dock" .. location];
	Dock[location].Bar = _G["SVUI_DockBar" .. location];

	Dock[location].Alert.Activate = AlertActivate;
	Dock[location].Alert.Deactivate = AlertDeactivate;

	Dock[location].Bar.Parent = Dock[location];
	Dock[location].Bar.Refresh = RefreshDockButtons;
	Dock[location].Bar.Cycle = RefreshDockWindows;
	Dock[location].Bar.GetDefault = GetDefault;
	Dock[location].Bar.UnsetDefault = OldDefault;
	Dock[location].Bar.Toggle = ToggleDockletWindow;
	Dock[location].Bar.Update = RefreshBarLayout;
	Dock[location].Bar.UpdateOrder = RefreshBarOrder;
	Dock[location].Bar.ChangeOrder = ChangeBarOrder;
	Dock[location].Bar.CheckOrder = CheckBarOrder;
	Dock[location].Bar.Add = AddToDock;
	Dock[location].Bar.Remove = RemoveFromDock;
	Dock[location].Bar.Initialize = ActivateDockletButton;
	Dock[location].Bar.Create = CreateBasicToolButton;
	Dock[location].Bar.Data = {
		Location = location,
		Anchor = settings[2],
		Modifier = settings[1],
		TipAnchor = settings[4],
		Default = "",
		Buttons = {},
		Windows = {},
		Order = {},
	};
end

function Dock:SetSuperDockStyle(dock, isBottom)
	if dock.backdrop then return end

	local backdrop = CreateFrame("Frame", nil, dock)
	backdrop:SetAllPoints(dock)
	backdrop:SetFrameStrata("BACKGROUND")

	backdrop.bg = backdrop:CreateTexture(nil, "BORDER")
	backdrop.bg:SetAllPointsIn(backdrop)
	backdrop.bg:SetTexture(1, 1, 1, 1)

	if(isBottom) then
		backdrop.bg:SetGradientAlpha("VERTICAL", 0, 0, 0, 0.8, 0, 0, 0, 0)
	else
		backdrop.bg:SetGradientAlpha("VERTICAL", 0, 0, 0, 0, 0, 0, 0, 0.8)
	end

	backdrop.left = backdrop:CreateTexture(nil, "OVERLAY")
	backdrop.left:SetTexture(1, 1, 1, 1)
	backdrop.left:SetPointToScale("TOPLEFT", 1, -1)
	backdrop.left:SetPointToScale("BOTTOMLEFT", -1, -1)
	backdrop.left:SetWidthToScale(4)
	if(isBottom) then
		backdrop.left:SetGradientAlpha("VERTICAL", 0, 0, 0, 1, 0, 0, 0, 0)
	else
		backdrop.left:SetGradientAlpha("VERTICAL", 0, 0, 0, 0, 0, 0, 0, 1)
	end

	backdrop.right = backdrop:CreateTexture(nil, "OVERLAY")
	backdrop.right:SetTexture(1, 1, 1, 1)
	backdrop.right:SetPointToScale("TOPRIGHT", -1, -1)
	backdrop.right:SetPointToScale("BOTTOMRIGHT", -1, -1)
	backdrop.right:SetWidthToScale(4)
	if(isBottom) then
		backdrop.right:SetGradientAlpha("VERTICAL", 0, 0, 0, 1, 0, 0, 0, 0)
	else
		backdrop.right:SetGradientAlpha("VERTICAL", 0, 0, 0, 0, 0, 0, 0, 1)
	end

	backdrop.bottom = backdrop:CreateTexture(nil, "OVERLAY")
	backdrop.bottom:SetPointToScale("BOTTOMLEFT", 1, -1)
	backdrop.bottom:SetPointToScale("BOTTOMRIGHT", -1, -1)
	if(isBottom) then
		backdrop.bottom:SetTexture(0, 0, 0, 1)
		backdrop.bottom:SetHeightToScale(4)
	else
		backdrop.bottom:SetTexture(0, 0, 0, 0)
		backdrop.bottom:SetAlpha(0)
		backdrop.bottom:SetHeightToScale(1)
	end

	backdrop.top = backdrop:CreateTexture(nil, "OVERLAY")
	backdrop.top:SetPointToScale("TOPLEFT", 1, -1)
	backdrop.top:SetPointToScale("TOPRIGHT", -1, 1)
	if(isBottom) then
		backdrop.top:SetTexture(0, 0, 0, 0)
		backdrop.top:SetAlpha(0)
		backdrop.top:SetHeightToScale(1)
	else
		backdrop.top:SetTexture(0, 0, 0, 1)
		backdrop.top:SetHeightToScale(4)
	end

	return backdrop
end

local function InitDockButton(button, location)
	button:SetPanelColor("default")
	button.Icon:SetGradient(unpack(SV.Media.gradient.icon))
	button:SetScript("OnEnter", DockButton_OnEnter)
	button:SetScript("OnLeave", DockletButton_OnLeave)
	if(location == "BottomLeft") then
		button:SetScript("OnClick", ToggleSuperDockLeft)
	else
		button:SetScript("OnClick", ToggleSuperDockRight)
	end
end

local function BorderColorUpdates()
	Dock.Border.Top:SetBackdropColor(unpack(SV.Media.color.specialdark))
	Dock.Border.Top:SetBackdropBorderColor(0,0,0,1)
	Dock.Border.Bottom:SetBackdropColor(unpack(SV.Media.color.default))
	Dock.Border.Bottom:SetBackdropBorderColor(0,0,0,1)
end

SV.Events:On("SVUI_COLORS_UPDATED", "BorderColorUpdates", BorderColorUpdates)
--[[
##########################################################
EXTERNALLY ACCESSIBLE METHODS
##########################################################
]]--
function Dock:SetDockButton(location, displayName, texture, onclick, globalName, tipFunction, primaryTemplate)
	if(self.Locations[globalName]) then
		location = self.Locations[globalName];
	else
		self.Locations[globalName] = location;
	end
	local parent = self[location]
	return parent.Bar:Create(displayName, texture, onclick, globalName, tipFunction, primaryTemplate)
end

function Dock:GetDimensions(location)
	local width, height;

	if(location:find("Left")) then
		width = SV.db.Dock.dockLeftWidth;
		height = SV.db.Dock.dockLeftHeight;
		if(SV.cache.Docks.LeftExpanded) then
			height = height + 300
		end
	else
		width = SV.db.Dock.dockRightWidth;
		height = SV.db.Dock.dockRightHeight;
		if(SV.cache.Docks.RightExpanded) then
			height = height + 300
		end
	end

	return width, height;
end

function Dock:NewDocklet(location, globalName, readableName, texture, onclick)
	if(self.Registration[globalName]) then return end;

	if(self.Locations[globalName]) then
		location = self.Locations[globalName];
	else
		self.Locations[globalName] = location;
	end

	local newParent = self[location];
	if(not newParent) then return end
	local frame = _G[globalName] or CreateFrame("Frame", globalName, UIParent, "SVUI_DockletWindowTemplate");
	frame:SetParent(newParent.Window);
	frame:SetSize(newParent.Window:GetSize());
	frame:SetAllPoints(newParent.Window);
	frame:SetFrameStrata("BACKGROUND");
	frame.Parent = newParent
	frame.Disable = DockletDisable;
	frame.Enable = DockletEnable;
	frame.Relocate = DockletRelocate;
	frame.GetButtonSize = DockletButtonSize;
	frame:FadeCallback(function() newParent.Bar:Cycle() newParent.Bar:GetDefault() end, false, true)

	newParent.Bar.Data.Windows[globalName] = frame;

	local buttonName = ("%sButton"):format(globalName)
	frame.DockButton = newParent.Bar:Create(readableName, texture, onclick, buttonName);
	frame.DockButton.FrameLink = frame
	self.Registration[globalName] = frame;
	frame:SetAlpha(0)
	return frame
end

function Dock:NewAdvancedDocklet(location, globalName)
	if(self.Registration[globalName]) then return end;

	if(self.Locations[globalName]) then
		location = self.Locations[globalName];
	else
		self.Locations[globalName] = location;
	end

	local newParent = self[location];
	if(not newParent) then return end

	local frame = CreateFrame("Frame", globalName, UIParent, "SVUI_DockletWindowTemplate");
	frame:SetParent(newParent.Window);
	frame:SetSize(newParent.Window:GetSize());
	frame:SetAllPoints(newParent.Window);
	frame:SetFrameStrata("BACKGROUND");
	frame.Parent = newParent
	frame.Disable = DockletDisable;
	frame.Enable = DockletEnable;
	frame.Relocate = DockletRelocate;
	frame.GetButtonSize = DockletButtonSize;

	newParent.Bar.Data.Windows[globalName] = frame;

	local height = newParent.Bar.ToolBar:GetHeight();
	local mod = newParent.Bar.Data.Modifier;
	local barAnchor = newParent.Bar.Data.Anchor;
	local barReverse = SV:GetReversePoint(barAnchor);
	local spacing = SV.db.Dock.buttonSpacing;

	frame.Bar = CreateFrame("Frame", nil, newParent);
	frame.Bar:SetSize(1, height);
	frame.Bar:SetPointToScale(barAnchor, newParent.Bar.ToolBar, barReverse, (spacing * mod), 0)
	SV.Mentalo:Add(frame.Bar, globalName .. " Dock Bar");

	self.Registration[globalName] = frame;
	return frame
end
--[[
##########################################################
BUILD/UPDATE
##########################################################
]]--
function Dock:UpdateDockBackdrops()
	if SV.db.Dock.rightDockBackdrop then
		Dock.BottomRight.backdrop:Show()
		Dock.BottomRight.backdrop:ClearAllPoints()
		Dock.BottomRight.backdrop:SetAllPointsOut(Dock.BottomRight.Window, 4, 4)

		Dock.BottomRight.Alert.backdrop:ClearAllPoints()
		Dock.BottomRight.Alert.backdrop:SetAllPointsOut(Dock.BottomRight.Alert, 4, 4)
	else
		Dock.BottomRight.backdrop:Hide()
	end
	if SV.db.Dock.leftDockBackdrop then
		Dock.BottomLeft.backdrop:Show()
		Dock.BottomLeft.backdrop:ClearAllPoints()
		Dock.BottomLeft.backdrop:SetAllPointsOut(Dock.BottomLeft.Window, 4, 4)

		Dock.BottomLeft.Alert.backdrop:ClearAllPoints()
		Dock.BottomLeft.Alert.backdrop:SetAllPointsOut(Dock.BottomLeft.Alert, 4, 4)
	else
		Dock.BottomLeft.backdrop:Hide()
	end
end

function Dock:BottomBorderVisibility()
	if SV.db.Dock.bottomPanel then
		self.Border.Bottom:Show()
	else
		self.Border.Bottom:Hide()
	end
end

function Dock:TopBorderVisibility()
	if SV.db.Dock.topPanel then
		self.Border.Top:Show()
	else
		self.Border.Top:Hide()
	end
end

function Dock:ResetAllButtons()
	wipe(SV.cache.Docks.Order)
	wipe(SV.cache.Docks.Locations)
	ReloadUI()
end

function Dock:UpdateAllDocks()
	for location, settings in pairs(DOCK_LOCATIONS) do
		local dock = self[location];
		dock.Bar:Cycle()
		dock.Bar:GetDefault()
	end
end

function Dock:Refresh()
	local buttonsize = SV.db.Dock.buttonSize;
	local spacing = SV.db.Dock.buttonSpacing;

	for location, settings in pairs(DOCK_LOCATIONS) do
		if(location ~= "TopRight") then
			local width, height = self:GetDimensions(location);
			local dock = self[location];

			dock.Bar:SetSize(width, buttonsize)
		    dock.Bar.ToolBar:SetHeight(buttonsize)
		    dock:SetSize(width, height)
		    dock.Alert:SetSize(width, 1)
		    dock.Window:SetSize(width, height)

		    if(dock.Bar.Button) then
		    	dock.Bar.Button:SetSize(buttonsize, buttonsize)
		    end

		    dock.Bar:Update()
		end
	end

	self:BottomBorderVisibility();
	self:TopBorderVisibility();
	self:UpdateDockBackdrops();

	self:RefreshStats();

	SV.Events:Trigger("DOCKS_UPDATED");
end

function Dock:Initialize()
	SV.cache.Docks = SV.cache.Docks	or {}

	if(not SV.cache.Docks.AllFaded) then
		SV.cache.Docks.AllFaded = false
	end

	if(not SV.cache.Docks.LeftFaded) then
		SV.cache.Docks.LeftFaded = false
	end

	if(not SV.cache.Docks.RightFaded) then
		SV.cache.Docks.RightFaded = false
	end

	if(not SV.cache.Docks.LeftExpanded) then
		SV.cache.Docks.LeftExpanded = false
	end

	if(not SV.cache.Docks.RightExpanded) then
		SV.cache.Docks.RightExpanded = false
	end

	if(not SV.cache.Docks.Order) then
		SV.cache.Docks.Order = {}
	end

	if(not SV.cache.Docks.Locations) then
		SV.cache.Docks.Locations = {}
	end

	self.Locations = SV.cache.Docks.Locations;

	local buttonsize = SV.db.Dock.buttonSize;
	local spacing = SV.db.Dock.buttonSpacing;
	local texture = [[Interface\AddOns\SVUI\assets\artwork\Template\BUTTON]];

	-- [[ TOP AND BOTTOM BORDERS ]] --

	self.Border.Top = CreateFrame("Frame", "SVUITopBorder", UIParent)
	self.Border.Top:SetPointToScale("TOPLEFT", SV.Screen, "TOPLEFT", -1, 1)
	self.Border.Top:SetPointToScale("TOPRIGHT", SV.Screen, "TOPRIGHT", 1, 1)
	self.Border.Top:SetHeightToScale(14)
	self.Border.Top:SetBackdrop({
		bgFile = texture,
		edgeFile = [[Interface\BUTTONS\WHITE8X8]],
		tile = false,
		tileSize = 0,
		edgeSize = 1,
		insets = {left = 0, right = 0, top = 0, bottom = 0}
	})
	self.Border.Top:SetBackdropColor(unpack(SV.Media.color.specialdark))
	self.Border.Top:SetBackdropBorderColor(0,0,0,1)
	self.Border.Top:SetFrameLevel(0)
	self.Border.Top:SetFrameStrata('BACKGROUND')
	self.Border.Top:SetScript("OnShow", function(this)
		this:SetFrameLevel(0)
		this:SetFrameStrata('BACKGROUND')
	end)
	self:TopBorderVisibility()

	self.Border.Bottom = CreateFrame("Frame", "SVUIBottomBorder", UIParent)
	self.Border.Bottom:SetPointToScale("BOTTOMLEFT", SV.Screen, "BOTTOMLEFT", -1, -1)
	self.Border.Bottom:SetPointToScale("BOTTOMRIGHT", SV.Screen, "BOTTOMRIGHT", 1, -1)
	self.Border.Bottom:SetHeightToScale(14)
	self.Border.Bottom:SetBackdrop({
		bgFile = texture,
		edgeFile = [[Interface\BUTTONS\WHITE8X8]],
		tile = false,
		tileSize = 0,
		edgeSize = 1,
		insets = {left = 0, right = 0, top = 0, bottom = 0}
	})
	self.Border.Bottom:SetBackdropColor(unpack(SV.Media.color.default))
	self.Border.Bottom:SetBackdropBorderColor(0,0,0,1)
	self.Border.Bottom:SetFrameLevel(0)
	self.Border.Bottom:SetFrameStrata('BACKGROUND')
	self.Border.Bottom:SetScript("OnShow", function(this)
		this:SetFrameLevel(0)
		this:SetFrameStrata('BACKGROUND')
	end)
	self:BottomBorderVisibility()

	for location, settings in pairs(DOCK_LOCATIONS) do
		local width, height = self:GetDimensions(location);
		local dock = self[location];
		local mod = settings[1];
		local anchor = upper(location);
		local reverse = SV:GetReversePoint(anchor);
		local barAnchor = settings[2];
		local barReverse = SV:GetReversePoint(barAnchor);
		local isBottom = settings[3];
		local vertMod = isBottom and 1 or -1

		dock.Bar:SetParent(SV.Screen)
		dock.Bar:ClearAllPoints()
		dock.Bar:SetSize(width, buttonsize)
		dock.Bar:SetPoint(anchor, SV.Screen, anchor, (2 * mod), (2 * vertMod))

		if(not SV.cache.Docks.Order[location]) then
			SV.cache.Docks.Order[location] = {}
		end

		dock.Bar.Data.Order = SV.cache.Docks.Order[location];

		dock.Bar.ToolBar:ClearAllPoints()

		if(dock.Bar.Button) then
	    	dock.Bar.Button:SetSize(buttonsize, buttonsize)
	    	dock.Bar.Button:SetStylePanel("HeavyButton")
	    	dock.Bar.ToolBar:SetSize(1, buttonsize)
	    	dock.Bar.ToolBar:SetPointToScale(barAnchor, dock.Bar.Button, barReverse, (spacing * mod), 0)
	    	InitDockButton(dock.Bar.Button, location)
	    else
	    	dock.Bar.ToolBar:SetSize(1, buttonsize)
	    	dock.Bar.ToolBar:SetPointToScale(barAnchor, dock.Bar, barAnchor, 0, 0)
	    end

	    dock:SetParent(SV.Screen)
	    dock:ClearAllPoints()
	    dock:SetPoint(anchor, dock.Bar, reverse, 0, (12 * vertMod))
	    dock:SetSize(width, height)
	    dock:SetAttribute("buttonSize", buttonsize)
	    dock:SetAttribute("spacingSize", spacing)

	    dock.Alert:ClearAllPoints()
	    dock.Alert:SetSize(width, 1)
	    dock.Alert:SetPoint(anchor, dock, anchor, 0, 0)

	    dock.Window:ClearAllPoints()
	    dock.Window:SetSize(width, height)
	    dock.Window:SetPoint(anchor, dock.Alert, reverse, 0, (4 * vertMod))

		if(isBottom) then
			dock.backdrop = self:SetSuperDockStyle(dock.Window, isBottom)
			dock.Alert.backdrop = self:SetSuperDockStyle(dock.Alert, isBottom)
			dock.Alert.backdrop:Hide()
			SV.Mentalo:Add(dock.Bar, location .. " Dock ToolBar");
			SV.Mentalo:Add(dock, location .. " Dock Window")
		end
	end

	if SV.cache.Docks.LeftFaded then Dock.BottomLeft:Hide() end
	if SV.cache.Docks.RightFaded then Dock.BottomRight:Hide() end

	SV:ManageVisibility(self.BottomRight.Window)
	SV:ManageVisibility(self.TopLeft)
	SV:ManageVisibility(self.TopRight)

	if not InCombatLockdown() then
		self.BottomLeft.Bar:Refresh()
		self.BottomRight.Bar:Refresh()
		self.TopLeft.Bar:Refresh()
		self.TopRight.Bar:Refresh()
	end

	self:UpdateDockBackdrops()

	self:InitializeStats()
end