--[[---------------------------------------------------------------------------

Copyright (c) 2008 by K. Scott Piel 
All Rights Reserved

E-mail: < kscottpiel@gmail.com >
Web:    < http://www.scottpiel.com >

This file is part of nUI.

    nUI is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    nUI is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with nUI.  If not, see <http://www.gnu.org/licenses/>.
	
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

--]]---------------------------------------------------------------------------

if not nUI_DefaultConfig then nUI_DefaultConfig = {}; end

local CreateFrame         = CreateFrame;
local GetPetActionInfo    = GetPetActionInfo;
local GetPetActionsUsable = GetPetActionsUsable;
local RegisterStateDriver = RegisterStateDriver;
local SetDesaturation     = SetDesaturation;
local UnitIsUnit          = UnitIsUnit;

-------------------------------------------------------------------------------

nUI_DefaultConfig.PetBar =
{
	enabled = true;
	
	anchor =
	{
		anchor_pt   = "BOTTOM",
		relative_to = "nUI_Dashboard",
		relative_pt = "CENTER",
		xOfs        = 0,
		yOfs        = 187,
	},
	options =
	{
		enabled  = true,
		strata   = nil,
		level    = 2,
		btn_size = 39,
		btn_gap  = 1,
		inset    = 3,
		
		border =
		{
			backdrop =
			{
				bgFile   = "Interface\\AddOns\\nUI\\Art\\nUI_BevelboxBg.blp", 
				edgeFile = "Interface\\AddOns\\nUI\\Art\\nUI_BevelboxBorder.blp", 
				tile     = true, 
				tileSize = 1, 
				edgeSize = 12, 
				insets   = {left = 0, right = 0, top = 0, bottom = 0},
			},
			border_color   = { r = 1, g = 1, b = 1, a = 1 },
			backdrop_color = { r = 0, g = 0, b = 0, a = 0.35 },
		}
	},
};

-------------------------------------------------------------------------------
-- the shapeshift bar frame

local anchor     = CreateFrame( "Frame", "nUI_PetBarEvents", WorldFrame );
local background = CreateFrame( "Frame", "nUI_PetBarBackground", nUI_Dashboard.Anchor );
local frame      = CreateFrame( "Frame", "nUI_PetBar", nUI_Dashboard.Anchor, "SecureStateHeaderTemplate" );

background:SetAllPoints( frame );
RegisterStateDriver( frame, "visibility", "[bonusbar:5] hide; [pet] show; hide" );

-------------------------------------------------------------------------------
-- button initialization

frame.Buttons = {};

for i=1, NUM_PET_ACTION_SLOTS do

	local button            = CreateFrame( "CheckButton", "$parent_Button"..i, frame, "PetActionButtonTemplate" );	
	local btn_name          = button:GetName();
	frame.Buttons[i]        = button;	
	button.enabled          = true;
	button.layers           = {};
	button.layers.icon      = _G[btn_name.."Icon"];
	button.layers.cooldown  = _G[btn_name.."Cooldown"];
	button.layers.castable  = _G[btn_name.."AutoCastable"];
	button.layers.autocast  = _G[btn_name.."AutoCast"];	
	button.actionType       = "BONUSACTIONBUTTON";
	button.actionID         = i;
	
	button:SetID( i );
	button:SetNormalTexture( "" );
	frame:SetAttribute( "addchild", button );

	button.SetNormalTexture = function() end;	
	
	button.layers.castable:Show();
	button.layers.autocast:Show();
	
end

-------------------------------------------------------------------------------

frame.applyScale = function( scale )
	
	local anchor  = scale and frame.anchor or nil;
	local scale   = scale or frame.scale or 1;
	local options = frame.options;
	
	if options then
		
		local btn_size = options.btn_size * scale * nUI.scale;
		local btn_gap  = (options.btn_gap or 0) * scale * nUI.scale;
		local inset    = (options.inset or 0) * scale * nUI.scale;
		
		if frame.btn_size ~= btn_size 
		or frame.btn_gap  ~= btn_gap
		or frame.inset    ~= inset
		then
			
			frame.btn_size = btn_size;
			frame.btn_gap  = btn_gap;
			frame.inset    = inset;
			
			for i=1,#frame.Buttons do
				
				local button = frame.Buttons[i];
				
				button:ClearAllPoints();
				button:SetScale( btn_size / button:GetHeight() );
				
				if i == 1 then button:SetPoint( "TOPLEFT", frame, "TOPLEFT", inset, -inset );
				else button:SetPoint( "LEFT", frame.Buttons[i-1], "RIGHT", btn_gap, 0 );
				end
				
			end
						
			local width = btn_size * NUM_PET_ACTION_SLOTS + btn_gap * (NUM_PET_ACTION_SLOTS-1) + inset * 2;
			
			if frame.width ~= width then
				frame.width = width;
				frame:SetHeight( btn_size + inset * 2 );
				frame:SetWidth( width );
			end			
		end
	end
	
	if anchor then frame.applyAnchor(); end
	
end

-------------------------------------------------------------------------------

frame.applyAnchor = function( anchor )
	
	local anchor      = anchor or frame.anchor or {};
	local anchor_pt   = anchor.anchor_pt or "CENTER";
	local relative_to = anchor.relative_to or frame:GetParent():GetName();
	local relative_pt = anchor.relative_pt or anchor_pt;
	local xOfs        = (anchor.xOfs or 0) * nUI.scale;
	local yOfs        = (anchor.yOfs or 0) * nUI.scale;
	
	if frame.anchor_pt   ~= anchor_pt
	or frame.relative_to ~= relative_to
	or frame.relative_pt ~= relative_pt
	or frame.xOfs        ~= xOfs
	or frame.yOfs        ~= yOfs
	then
		
		frame.anchor_pt   = anchor_pt;
		frame.relative_to = relative_to;
		frame.relative_pt = relative_pt;
		frame.xOfs        = xOfs;
		frame.yOfs        = yOfs;
		
		frame:ClearAllPoints();
		frame:SetPoint( anchor_pt, relative_to, relative_pt, xOfs, yOfs );
		
	end
end

-------------------------------------------------------------------------------

frame.applyOptions = function( options )

	frame.options = options;
	
	if not options or not options.enabled then

		frame.enabled = false;
		RegisterStateDriver( frame, "visibility", "hide" );
		frame:Hide();
		
	else

		background:SetFrameStrata( options.strata or frame:GetParent():GetFrameStrata() );
		background:SetFrameLevel( options.level or frame:GetParent():GetFrameLevel()+1 );
		frame:SetFrameStrata( background:GetFrameStrata() );
		frame:SetFrameLevel( background:GetFrameLevel()+1 );
		
		for i=1, #frame.Buttons do
			
			local button = frame.Buttons[i];
			button:SetFrameStrata( frame:GetFrameStrata() );
			button:SetFrameLevel( frame:GetFrameLevel()+1 );
		end
		
		frame.enabled = true;
		RegisterStateDriver( frame, "visibility", "[bonusbar:5] hide; [pet] show; hide" );
	
		frame.applyScale( options.scale or frame.scale or 1 );
		
		if options.border then
			
			local border_color   = options.border.border_color;
			local backdrop_color = options.border.backdrop_color;
			
			frame:SetBackdrop( options.border.backdrop );
			frame:SetBackdropColor( backdrop_color.r, backdrop_color.g, backdrop_color.b, backdrop_color.a );
			frame:SetBackdropBorderColor( border_color.r, border_color.g, border_color.b, border_color.a );
			
		end
		
		if options.background then
			
			local border_color   = options.background.border_color;
			local backdrop_color = options.background.backdrop_color;
			
			background:SetBackdrop( options.background.backdrop );
			background:SetBackdropColor( backdrop_color.r, backdrop_color.g, backdrop_color.b, backdrop_color.a );
			background:SetBackdropBorderColor( border_color.r, border_color.g, border_color.b, border_color.a );
			
		end
		
	end
end

-------------------------------------------------------------------------------

frame.applySkin = function( skin )
	
	frame.skin = skin.PetBar or nUI_DefaultConfig.PetBar;
	
	if not frame.skin or not frame.skin.enabled then

		frame.enabled = false;
		
		frame:Hide();
		RegisterStateDriver( frame, "visibility", "hide" );
	
	else
		
		frame.enabled = true;
		RegisterStateDriver( frame, "visibility", "[bonusbar:5] hide; [pet] show; hide" );
		
		frame.applyOptions( frame.skin.options );
		frame.applyAnchor( frame.skin.anchor );
		
	end
end

-------------------------------------------------------------------------------

frame.updateState = function( index )
	
	local button = frame.Buttons[index];
	local petActionsUsable = GetPetActionsUsable();
	local name, subtext, texture, isToken, isActive, 
	      autoCastAllowed, autoCastEnabled = GetPetActionInfo( index );
	
	if not isToken then
		button.layers.icon:SetTexture( texture );
		button.tooltipName = name;
	else
		button.layers.icon:SetTexture( getglobal( texture ) );
		button.tooltipName = getglobal( name );
	end
	
	button.isToken        = isToken;
	button.tooltipSubtext = subtext;
	
	if isActive then button:SetChecked( 1 );
	else button:SetChecked( 0 );
	end
	
	if name then 

		if not button.enabled then			
			button.enabled = true;
			button:SetAlpha( 1 );
		end
		
		if autoCastAllowed then button.layers.castable:SetAlpha( 1 ); 
		else button.layers.castable:SetAlpha( 0 ); 
		end
		
		if autoCastEnabled then button.layers.autocast:SetAlpha( 0.05 ); 
		else button.layers.autocast:SetAlpha( 0 ); 
		end
		
		if texture then
			
			if petActionsUsable then
				SetDesaturation( button.layers.icon, nil );
			else
				SetDesaturation( button.layers.icon, 1 );
			end
			
			button.layers.icon:SetAlpha( 1 );
			
		else
			
			button.layers.icon:SetAlpha( 0 );
			
		end
		
		if frame.btn_size then
				
			local width = frame.btn_size * NUM_PET_ACTION_SLOTS + frame.btn_gap * (NUM_PET_ACTION_SLOTS-1) + frame.inset * 2;
			
			if frame.width ~= width then
				frame.width = width;
				frame:SetWidth( width );
			end			
		end				
	elseif button.enabled then

		button.enabled = false;
		button:SetAlpha( 0 );
		button.layers.castable:SetAlpha( 0 );
		button.layers.autocast:SetAlpha( 0 );
		button.layers.icon:SetAlpha( 0 );
		
	end	
end

-------------------------------------------------------------------------------
-- manage the layout of the bar

anchor:SetScript( "OnEvent", 

	function() 

		local update_state = false;
		
		if event == "VARIABLES_LOADED" 
		then
		
			nUI:registerScalableFrame( frame );
			nUI:registerSkinnedFrame( frame );
			
		elseif event == "UNIT_PET" 
		then
			
			if UnitIsUnit( arg1, "player" ) 
			then update_state = true;
			end
			
		elseif event == "UNIT_FLAGS"
		or     event == "UNIT_AURA"
		then
			
			if UnitIsUnit( arg1, "pet" )
			then update_state = true;
			end
			
		else
			
			update_state = true;

		end
		
		if update_state then
			for i=1, #frame.Buttons do
				frame.updateState( i );
			end
		end		
	end 
);

anchor:RegisterEvent( "VARIABLES_LOADED" );
anchor:RegisterEvent( "PLAYER_ENTERING_WORLD" );
anchor:RegisterEvent( "PLAYER_CONTROL_LOST" );
anchor:RegisterEvent( "PLAYER_CONTROL_GAINED" );
anchor:RegisterEvent( "PLAYER_FARSIGHT_FOCUS_CHANGED" );
anchor:RegisterEvent( "PET_BAR_UPDATE" );
anchor:RegisterEvent( "UNIT_PET" );
anchor:RegisterEvent( "UNIT_FLAGS" );
anchor:RegisterEvent( "UNIT_AURA" );
