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

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_Unit then nUI_Unit = {}; end
if not nUI_UnitOptions then nUI_UnitOptions = {}; end
if not nUI_DefaultConfig then nUI_DefaultConfig = {}; end

local UnitClass          = UnitClass;
local UnitClassification = UnitClassification;
local UnitCreatureFamily = UnitCreatureFamily;
local UnitCreatureType   = UnitCreatureType;
local UnitExists         = UnitExists;
local UnitFactionGroup   = UnitFactionGroup;
local UnitGUID           = UnitGUID;
local UnitIsPlayer       = UnitIsPlayer;
local UnitIsUnit         = UnitIsUnit;
local UnitName           = UnitName;
local UnitRace           = UnitRace;
local UnitSex            = UnitSex;

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

if not nUI_Unit.Drivers then 
	nUI_Unit.Drivers = CreateFrame( "Frame", "nUI_UnitDrivers", WorldFrame ); 
end

local frame                 = CreateFrame( "Frame", "$parent_UnitChange", nUI_Unit.Drivers );
local UnitChangeCallbacks   = {};
local UnitChangeUnits       = {};

nUI_Unit.Drivers.UnitChange = frame;

-------------------------------------------------------------------------------
-- unit event handler

local function onUnitChangeEvent()

	local unit_id = arg1;
	
	if event == "PLAYER_TARGET_CHANGED" then
		
		if UnitChangeCallbacks["target"] and #UnitChangeCallbacks["target"] > 0 then
			frame.newUnitInfo( "target", nUI_Unit:getUnitInfo( "target" ) );
		end
		
	elseif event == "PLAYER_FOCUS_CHANGED" then
		
		if UnitChangeCallbacks["focus"] and #UnitChangeCallbacks["focus"] > 0 then
			frame.newUnitInfo( "focus", nUI_Unit:getUnitInfo( "focus" ) );
		end
		
	-- for the rest of the events, we don't know which units are affected, so
	-- we span the list of all known interested units to see who is watching
		
	else

		-- the notify callbacks method already scans the entire watch list to
		-- see if the GUID has changed for any of the interested listeners,
		-- so we'll just pass it the player unit info structure to kick it
		-- off and let it rip from there
		
		frame.newUnitInfo( "player", nUI_Unit.PlayerInfo );
		
	end	
end

frame:SetScript( "OnEvent", onUnitChangeEvent );
frame:RegisterEvent( "UNIT_TARGET" );
frame:RegisterEvent( "UNIT_NAME_UPDATE" );
frame:RegisterEvent( "PARTY_MEMBERS_CHANGED" );	
frame:RegisterEvent( "PLAYER_ENTERING_WORLD" );
frame:RegisterEvent( "PLAYER_TARGET_CHANGED" );	
frame:RegisterEvent( "PLAYER_FOCUS_CHANGED" );	
frame:RegisterEvent( "RAID_ROSTER_UPDATE" );	

-------------------------------------------------------------------------------
-- unit update loop
--[[
local function onUnitChangeUpdate( who, elapsed )
end

frame:SetScript( "OnUpdate", onUnitChangeUpdate );
]]--
-------------------------------------------------------------------------------
-- this method is triggered anytime a request for a unit id's unit_info structre
-- via nUI_Unit:getUnitInfo() finds a new GUID on the unit

frame.newUnitInfo = function( unit_id, unit_info )
	
	local callbacks = UnitChangeCallbacks;
	local unitlist  = UnitChangeUnits;
	
	nUI_Unit:notifyCallbacks( nUI_L["unit change"], callbacks, unitlist, unit_info, unit_id, false );
	
end

-------------------------------------------------------------------------------
-- add and remove callbacks from the list of listeners who want to know
-- when the underlying GUID / unit information of a unit changes
--
-- calling this method will return the current unit_info structure
-- for this unit id if it exists or nil if the unit does not exist
--
-- Note: this is NOT recommended for unit existence! Use RegisterUnitWatch()
--       or RegisterUnitWatchState() for that purpose (allows for a taint safe
--		 way to know when a unit does and does not exist including the ability
--		 to show and hide frames, move frames, etc.)

function nUI_Unit:registerUnitChangeCallback( unit_id, callback )
	
	local unit_info = nil;
	
	if unit_id and callback then
		
		local list = UnitChangeCallbacks[unit_id] or {};
		
		nUI:TableInsertByValue( list, callback );
		
		if not UnitChangeCallbacks[unit_id] then
			UnitChangeCallbacks[unit_id] = list;
		end	
		
		unit_info = nUI_Unit:getUnitInfo( unit_id );
		
	end
	
	return unit_info;
	
end

function nUI_Unit:unregisterUnitChangeCallback( unit_id, callback )
	
	if unit_id and callback then
		
		local list = UnitChangeCallbacks[unit_id] or {};
		
		nUI:TableRemoveByValue( list, callback );
		
	end
end
