--[[
	Infield.lua
		A library of functions to keep frames within the UIParent's bounds
--]]

--constants
local VERSION = 60409;
local UPDATE_INTERVAL = 1;

--[[ Local Functions ]]--

--returns the adjusted x and y coordinates for a frame when scaled, keeping the topleft of the frame in the same relative spot
local function GetAdjustedCoords(frame, nScale)
	if(not ( frame:GetLeft() and frame:GetTop() ) ) then
		return nil;
	end
	return frame:GetLeft() * frame:GetScale() / nScale, frame:GetTop() * frame:GetScale() / nScale;
end

--create the frame that deals with detecting a scale change in the UI, then performing a list of actions
local function CreateInfieldMain()
	if(Infield) then
		InfieldUpdater = CreateFrame("frame" , "InfieldUpdater");
		InfieldUpdater.elapsed = 0;
		
		--set the onupdate function
		InfieldUpdater:SetScript("OnUpdate", function()
			this.elapsed = this.elapsed + arg1;
	
			if(this.elapsed > UPDATE_INTERVAL) then
				if( this.scale ~= UIParent:GetScale() ) then
					this.scale = UIParent:GetScale();

					--perform rescaling actions
					local index;
					for index in Infield.rescaleList do
						Infield.rescaleList[index]();
					end
					
				end
				this.elapsed = 0;
			end	
		end);		
	end
end

--[[ Update or Load Infield's Functions ]]--

if(not Infield or Infield.version < VERSION) then

	--create a new infield instance
	if(not Infield) then
		Infield = { rescaleList = {} };
	--do stuff needed to be done on a version change
	else
		local resList = Infield.rescaleList;
		Infield = { rescaleList = resList };
	end

	--update Infield's version
	Infield.version = VERSION;
	
	--Add a function to execute when the UI scale changes
	--This must be added before the PLAYER_LOGIN event takes place to detect the startup rescale.
	Infield.AddRescaleAction = function(funct)
		table.insert(Infield.rescaleList, funct);
	end

	--Sets <frame>'s scale <scale>, and repositions the frame if its out of the viewable screen area
	Infield.Scale = function(frame, scale)
		local x, y = GetAdjustedCoords(frame, scale);
		
		frame:SetScale(scale);
		if(x) then
			Infield.Place(frame, "TOPLEFT", UIParent, "BOTTOMLEFT", x, y);
		end
	end

	--Places a frame at a location and repositions it so that it is not outside of the UIParent
	--Syntax is similar to Frame:SetScale()
	Infield.Place = function(frame, point, parent, relPoint, x, y)
		frame:ClearAllPoints();
		frame:SetPoint(point, parent, relPoint, x, y);
		Infield.Reposition(frame, UIParent);
	end

	--Moves a frame within the bounds of parent if its out of bounds
	Infield.Reposition = function(frame, parent)

		if( frame:GetBottom() and frame:GetTop() and frame:GetLeft() and frame:GetRight() ) then
			local xoff = 0;
			local yoff = 0;
			local ratio = frame:GetScale();
			
			
			--check and see if the frame is off the screen
			--Y bounds checking
			if (  frame:GetBottom() < 0) then
				yoff = 0 - frame:GetBottom();
			elseif ( frame:GetTop()  > (parent:GetTop() / ratio ) ) then
				yoff = (parent:GetTop() / ratio) - frame:GetTop();
			end

			--X bounds checking
			if ( frame:GetLeft() < 0 ) then
				xoff = 0  - frame:GetLeft();
			elseif ( frame:GetRight()  > ( parent:GetRight() / ratio )  ) then
				xoff = (parent:GetRight() / ratio) - frame:GetRight();
			end
			
			--Reposition if anything was out of bounds
			if( xoff ~= 0 or yoff ~= 0) then
				local x = frame:GetLeft() + xoff;
				local y = frame:GetTop() + yoff;
				
				frame:ClearAllPoints();
				frame:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", x , y);
			end
		end
	end
end

--create the frame
if(not InfieldUpdater) then
	CreateInfieldMain();
end