local Azurah		= _G['Azurah'] -- grab addon table from global
local Mover			= {}

local rootWidth, rootHeight
local OnUpdate

-- UPVALUES --
local WM			= GetWindowManager()
local round			= zo_round
local GuiRoot		= GuiRoot
local unpack		= unpack
local abs			= math.abs

-- Anchor frame to target based on offset from GuiRoot center
local function AnchorFrameToTarget(frame, target)
	local x, y = target:GetLeft() + (target:GetWidth() / 2), target:GetTop() + (target:GetHeight() / 2)
	x, y = x - (GuiRoot:GetWidth() / 2), y - (GuiRoot:GetHeight() / 2)
	frame:ClearAnchors()
	frame:SetAnchor(CENTER, GuiRoot, CENTER, x, y)
end

-- Get the anchor of the frame relative to nearest GuiRoot edge
local function GetAnchorRelativeToScreen(frame)
	local left, top		= frame:GetLeft(), frame:GetTop()
	local right, bottom	= frame:GetRight(), frame:GetBottom()
	local rootW, rootH	= GuiRoot:GetWidth(), GuiRoot:GetHeight()
	local point			= 0
	local x, y

	-- special case for Magicka/Stamina frames to keep 'expansion' alignment (provided by Garkin)
	if (frame == ZO_PlayerAttributeMagicka) then
		x		= right - rootW
		y		= (bottom + top) / 2 - rootH / 2
		point	= RIGHT
	elseif (frame == ZO_PlayerAttributeStamina) then
		x		= left
		y		= (bottom + top) / 2 - rootH / 2
		point	= LEFT
	else
		if (left < (rootW - right) and left < math.abs((left + right) / 2 - rootW / 2)) then
			x, point = left, 2 -- 'LEFT'
		elseif ((rootW - right) < math.abs((left + right) / 2 - rootW / 2)) then
			x, point = right - rootW, 8 -- 'RIGHT'
		else
			x, point = (left + right) / 2 - rootW / 2, 0
		end

		if (bottom < (rootH - top) and bottom < math.abs((bottom + top) / 2 - rootH / 2)) then
			y, point = top, point + 1 -- 'TOP|TOPLEFT|TOPRIGHT'
		elseif ((rootH - top) < math.abs((bottom + top) / 2 - rootH / 2)) then
			y, point = bottom - rootH, point + 4 -- 'BOTTOM|BOTTOMLEFT|BOTTOMRIGHT'
		else
			y = (bottom + top) / 2 - rootH / 2
		end

		point = (point == 0) and 128 or point -- 'CENTER'
	end

	return point, x, y
end

local function Sync(self)
	AnchorFrameToTarget(self, self.targetFrame)
end

local function Show(self)
	self:SetHidden(false)
end

local function Hide(self)
	self:SetHidden(true)
end

local function SetLabelText(self, text)
	self.labelText = (text) and text or ''
	self.label:SetText(self.labelText)
end

local function OnMouseEnter(self)
	self.label:SetText(self.labelText)
end

local function OnMouseExit(self)
	self.label:SetText(self.labelText)
end

local function OnMouseWheel(self, delta)
	self.targetFrame:SetScale(self.targetFrame:GetScale() + (delta > 0 and 0.01 or -0.01))
	self:SetDimensions(self.targetFrame:GetWidth(), self.targetFrame:GetHeight())
	self.label:SetText('(' .. round(self.targetFrame:GetScale() * 100) .. '%)')

	local point, x, y = GetAnchorRelativeToScreen(self.targetFrame) -- get screen relative anchor for callback
	Azurah:RecordUserData(self.targetFrame:GetName(), point, x, y, self.targetFrame:GetScale())
end

local function OnMouseDown(self)
	self.label:SetText(self.labelText) -- ensure the overlay label shows the name and not scaling percent
	self:SetDimensions(self.targetFrame:GetWidth(), self.targetFrame:GetHeight()) -- ensure overlay size is consistant with target (scaling outside of lib)
	-- Register GuiRoot current dimensions (saves having to constantly monitor for scaling changes)
	rootWidth, rootHeight = GuiRoot:GetWidth(), GuiRoot:GetHeight()
	self:SetHandler('OnUpdate', OnUpdate)
end

local function OnMouseUp(self)
	self.label:SetText(self.labelText) -- ensure the overlay label shows the name and not scaling percent
	self:SetHandler('OnUpdate', nil)
	self:StopMovingOrResizing()
	self.targetFrame:StopMovingOrResizing()

	local point, x, y = GetAnchorRelativeToScreen(self.targetFrame)

	self.targetFrame:ClearAnchors()
	self.targetFrame:SetAnchor(point, GuiRoot, point, x, y)

	Azurah:RecordUserData(self.targetFrame:GetName(), point, x, y, self.targetFrame:GetScale())

	AnchorFrameToTarget(self, self.targetFrame) -- ensure Handler is placed relative to target
end

do
	local gridSize = 10
	local width, height, offsetHorz, offsetVert, nearLR, nearTB

	OnUpdate = function(self)
		self.targetFrame:ClearAnchors()
		-- get relative offset from the CENTER of GuiRoot to the CENTER of self
		width, height = self:GetWidth(), self:GetHeight()
		offsetHorz = (self:GetLeft() + (width / 2)) - (rootWidth / 2)
		offsetVert = (self:GetTop() + (height / 2)) - (rootHeight / 2)

		if (Azurah.snapToGrid) then -- update offset to snapped values if enabled
			offsetHorz	= round(offsetHorz / gridSize) * gridSize
			offsetVert	= round(offsetVert / gridSize) * gridSize

			nearLR, nearTB = 0, 0

			if (self:GetLeft() - gridSize < 0) then
				nearLR = -1 -- near left edge
			elseif (self:GetRight() + gridSize > rootWidth) then
				nearLR = 1 -- near right edge
			end

			if (self:GetTop() - gridSize < 0) then
				nearTB = -1 -- near top edge
			elseif (self:GetBottom() + gridSize > rootHeight) then
				nearTB = 1 -- near bottom edge
			end

			self.targetFrame:SetAnchor(CENTER, GuiRoot, CENTER,
				(nearLR == 0) and offsetHorz or nearLR * ((rootWidth / 2) - (width / 2)),
				(nearTB == 0) and offsetVert or nearTB * ((rootHeight / 2) - (height / 2))
			)
		else
			self.targetFrame:SetAnchor(CENTER, GuiRoot, CENTER, offsetHorz, offsetVert)
		end
	end
end

function Mover:New(target, text)
	local new = WM:CreateControlFromVirtual(nil, GuiRoot, 'Azurah_Mover')

	new.targetFrame					= target
	new.labelText					= (text) and text or ''

	new['Sync']						= Sync
	new['Show']						= Show
	new['Hide']						= Hide
	new['SetLabelText']				= SetLabelText

	new:SetHandler('OnMouseEnter',	OnMouseEnter)
	new:SetHandler('OnMouseExit',	OnMouseExit)
	new:SetHandler('OnMouseWheel',	OnMouseWheel)
	new:SetHandler('OnMouseDown',	OnMouseDown)
	new:SetHandler('OnMouseUp',		OnMouseUp)

	new:SetDimensions(target:GetWidth(), target:GetHeight())
	new:SetDrawLayer(4)
	new:SetParent(GuiRoot)
	new:SetMouseEnabled(true)
	new:SetMovable(true)
	new:SetClampedToScreen(true)

	-- overlay
	new.overlay = WM:CreateControl(nil, new, CT_BACKDROP)
	new.overlay:SetDrawLevel(2)
	new.overlay:SetAnchorFill(target)
	new.overlay:SetCenterColor(0, 0.5, 0.7, 0.32)
	new.overlay:SetEdgeColor(0, 0.5, 0.7, 1)
	new.overlay:SetEdgeTexture('', 8, 1, 0)
	-- label
	new.label = WM:CreateControl(nil, new, CT_LABEL)
	new.label:SetDrawLevel(3)
	new.label:SetAnchorFill(target)
	new.label:SetFont('ZoFontWinH5')
	new.label:SetColor(1, 0.82, 0, 0.9)
	new.label:SetHorizontalAlignment(1)
	new.label:SetVerticalAlignment(1)
	new.label:SetText(new.labelText)

	AnchorFrameToTarget(new, target)

	Azurah.movers[target] = new

	return new
end

Azurah.Mover = Mover