 
do

	local L = LibStub ("AceLocale-3.0"):GetLocale ("DetailsTargetCaller")

	local Details = Details
	if (not Details) then
		print (L["STRING_INSTALL_ERROR1"])
		return
	end
	
	local _
	
	--> minimal details version required to run this plugin
	local MINIMAL_DETAILS_VERSION_REQUIRED = 75
	local DETAILS_ATTRIBUTE_DAMAGE = DETAILS_ATTRIBUTE_DAMAGE
	local TCALLER_VERSION = "v1.2.3"
	
	--> keeps the information which details window the plugin is being shown
	local attachedInstance
	
	--> create a plugin object
	local targetCaller = Details:NewPluginObject ("Details_TargetCaller")
	--> just localizing here the plugin's main frame
	local frame = targetCaller.Frame
	--> set the description
	targetCaller:SetPluginDescription (L ["STRING_PLUGIN_DESC"])
	--> get the framework object
	local framework = targetCaller:GetFramework()
	
	local rosterTable = {} --> indexed table with players in the raid
	local rosterHashTable = {} --> a hash table with [playername] = true, just to know we already added the player inside the indexed table
	local barsTable = {} --> holds the bars we created
	
	local currentTarget = "" --> who we are targeting now
	local currentCombat = targetCaller:GetCurrentCombat() --> the reference of the current combat on Details!
	local textformat = targetCaller:GetCurrentToKFunction() --> the current number format function used by Details!
	
	targetCaller.Bars = barsTable --> adding this as a member, so we can call UpdatePluginBarsConfig()
	
	--> simple sort function
	local sort_by_total = function (actor1, actor2)
		return actor1.total > actor2.total
	end
	local sort_by_overall = function (actor1, actor2)
		return actor1.overall > actor2.overall
	end

	function targetCaller.UpdateWindowBars()
		--> create more bars if needed
		local totalbars = attachedInstance.rows_fit_in_window
		if (totalbars > #barsTable) then
			for i  = #barsTable+1, totalbars, 1 do
				barsTable [i] = framework:CreateBar (frame)
			end
		end

		--> update SetPoint() and import row_info from the attached instance into our addon object
		--> row_info holds all config used by the bars
		targetCaller:UpdatePluginBarsConfig()
		--> set our .Frame width and height to be the same as the instance
		targetCaller:AttachToInstance()
		
		--> with row_info copied, we update the bars here
		local row_info = targetCaller.row_info
		for _, bar in ipairs (barsTable) do
			bar.height = row_info.height
			bar.texture = row_info.texture
			bar.fontsize = row_info.font_size
			bar.fontface = row_info.font_face
			bar.fontcolor = row_info.fixed_text_color
			bar.shadow = row_info.textL_outline
		end
		
		--> and also update our ToK (text format number function)
		textformat = targetCaller:GetCurrentToKFunction()
	end
	
	--> hide all bars
	function targetCaller.ClearBars()
		for _, bar in ipairs (barsTable) do
			if (bar:IsShown()) then
				bar:Hide()
			end
		end
	end
	
	--> update how much damage players dealt to our current target
	function targetCaller.UpdateTotal (actor)
		local damage_actor = currentCombat (DETAILS_ATTRIBUTE_DAMAGE, actor.name)
		if (damage_actor) then
			local total = damage_actor.targets [currentTarget]
			actor.total = (total or 0) - actor.breakpoint
		end
	end
	
	--> when we changed the target, iterate amoung all players and save which are the current damage dealt to the target
	function targetCaller.SetBreakPoints (actor)
		if (actor) then
			--> this part runs when there is an update on the group roster
			actor.total = 0
			local damage_actor = currentCombat (DETAILS_ATTRIBUTE_DAMAGE, actor.name)
			if (damage_actor) then
				actor.breakpoint = damage_actor.targets [currentTarget] or 0
			end
		else
			--> this runs when we change our target
			for _, actor in ipairs (rosterTable) do
				actor.overall = actor.overall + actor.total
				actor.total = 0
				local damage_actor = currentCombat (DETAILS_ATTRIBUTE_DAMAGE, actor.name)
				if (damage_actor) then
					--> gets the current damage by this player done to our main target
					actor.breakpoint = damage_actor.targets [currentTarget] or 0
				end
			end
		end
	end
	
	function targetCaller.UpdateRoster()
		--> update the hash table (this is in case if we need to get a single actor, so we do rosterTable [rosterHashTable [name]])
		for index, actor in ipairs (rosterTable) do
			rosterHashTable [actor.name] = index
		end
		
		--> update raid members
		for unit in GroupUtil.EnumerateGroupMembers() do
			local name = GetUnitName (unit, true)
			if (not rosterHashTable [name]) then
				local actor = {}
				actor.name = name
				actor.displayName = targetCaller:GetOnlyName (name) or name or ""
				actor.class = select (2, UnitClass (unit)) or "HERO"
				actor.total = 0
				actor.overall = 0
				actor.breakpoint = 0
				
				tinsert (rosterTable, actor)
				
				rosterHashTable [name] = #rosterTable
				targetCaller.SetBreakPoints (actor)
			end
		end
	end
	
	--> start loop with basic stuff, reset roster, register needed events, start ticker.
	function targetCaller.StartLoop()
		--> clear the delay var
		targetCaller.StartDelay = nil
	
		--> start a new combat
		wipe (rosterTable)
		wipe (rosterHashTable)
		targetCaller.UpdateWindowBars()
		currentTarget = GetUnitName ("target", true)
		currentCombat = targetCaller:GetCurrentCombat()
		targetCaller:RegisterEvent ("GROUP_ROSTER_UPDATE")
		targetCaller:RegisterEvent ("PLAYER_TARGET_CHANGED")
		targetCaller.UpdateRoster()
		
		--targetCaller.ticker = C_Timer.NewTicker (0.2, targetCaller.LoopTicker)
		
		--use details! update speed
		targetCaller.ticker = C_Timer.NewTicker (Details.update_speed, targetCaller.LoopTicker)
	end
	
	--> target changed, time to save new breakpoints, also update the title on the instance
	function targetCaller.TargetChanged()
		currentTarget = GetUnitName ("target", true) or ""
		
		targetCaller.ClearBars()
		targetCaller.SetBreakPoints()
		
		if (currentTarget ~= "") then
			attachedInstance:SetTitleBarText (L ["STRING_PLUGIN_NAME"] .. ": " .. targetCaller:GetOnlyName (currentTarget))
		else
			attachedInstance:SetTitleBarText (L ["STRING_PLUGIN_NAME"] .. ": |cFFFFAA00" .. L["STRING_OVERALL"] .. "|r")
		end
	end
	
	--> we are using member method .icon to set the icon and the texcoord, so we need to pass a table with the texture and the coords
	--> here we have the texture, the coords table we just import from details
	local iconTable = {[[Interface\AddOns\Details\images\classes_small]]}
	
	function targetCaller.UpdateOverall()
		local top = 0
		for _, actor in ipairs (rosterTable) do
			if (actor.overall > top) then
				top = actor.overall
			end
		end
		
		table.sort (rosterTable, sort_by_overall)
		local classCoords = targetCaller.class_coords --> importing the coords table from Details!
		
		for i = 1, min (#rosterTable, #barsTable) do
			local bar, actor = barsTable [i], rosterTable [i]
			
			if (actor.overall >= 1) then
				bar.lefttext = actor.displayName
				bar.righttext = textformat (_, actor.overall)
				
				--> the framework handles color strings such as non-localized class names and HTML color names.
				--> for example bar.color = "HUNTER" or bar.color = "purple" works perfectly fine
				--bar.color = actor.class 
				bar.color = "silver"

				iconTable [2] = classCoords [actor.class] --> placing the class coords into index [2]
				bar.icon = iconTable
				
				local percent = actor.overall / top * 100
				bar.value = percent

				bar:Show()
			else
				bar:Hide()
			end
		end
	end
	
	function targetCaller.LoopTicker()
	
		--> when no target, show overall
		if (currentTarget == "") then
			return targetCaller.UpdateOverall()
		end
		
		--> update to our current target
		local top = 0
		for _, actor in ipairs (rosterTable) do
			targetCaller.UpdateTotal (actor)
			if (actor.total > top) then
				top = actor.total
			end
		end
		
		table.sort (rosterTable, sort_by_total)
		local classCoords = targetCaller.class_coords --> importing the coords table from Details!
		
		for i = 1, min (#rosterTable, #barsTable) do
			local bar, actor = barsTable [i], rosterTable [i]
			
			if (actor.total >= 1) then
				bar.lefttext = actor.displayName
				bar.righttext = textformat (_, actor.total)
				
				--> the framework handles color strings such as non-localized class names and HTML color names.
				--> for example bar.color = "HUNTER" or bar.color = "purple" works perfectly fine
				bar.color = actor.class 

				iconTable [2] = classCoords [actor.class] --> placing the class coords into index [2]
				bar.icon = iconTable
				
				--no need to create a new local
				--local percent = actor.total / top * 100
				bar.value = actor.total / top * 100

				bar:Show()
			else
				bar:Hide()
			end
		end
	end
	
	--> finish the ticker, unregister events, hide bars, wipe roster tables
	function targetCaller.EndLoop (isFromDataReset)
		if (targetCaller.ticker and not targetCaller.ticker._cancelled) then
			targetCaller.ticker:Cancel()
		end
		if (targetCaller.StartDelay and not targetCaller.StartDelay._cancelled) then
			targetCaller.StartDelay:Cancel()
		end
		
		targetCaller.ClearBars()

		--> update the overall counter
		if (currentTarget ~= "" and not isFromDataReset) then
			targetCaller.SetBreakPoints()
		end
		
		currentTarget = ""
		targetCaller:UnregisterEvent ("GROUP_ROSTER_UPDATE")
		targetCaller:UnregisterEvent ("PLAYER_TARGET_CHANGED")
		
		--> show the overall when the update is done
		if (not isFromDataReset) then
			targetCaller.UpdateOverall()
		else
			--> wipe all if details! got a wipe too
			wipe (rosterTable)
			wipe (rosterHashTable)
		end
	end
	
	--> if we start checking the damage on the target
	function targetCaller.CheckFor_CanStartRunning()
		if (targetCaller:IsInCombat() and targetCaller:InGroup()) then
			if (targetCaller.StartDelay) then
				return
			end
			--> delay startloop, sometimes we get a roster event update after the enter combat event
			local timer = C_Timer.NewTimer (0.1, targetCaller.StartLoop)
			targetCaller.StartDelay = timer
		end
	end
	
	--> when receiving an event from details, handle it here
	local handle_details_event = function (event, ...)
		if (event == "HIDE") then
			--> the user closed the window or selected other plugin / mode
		 	targetCaller.EndLoop()
			
		elseif (event == "SHOW") then
			--> the plugin beign shown on a window
			targetCaller:AttachToInstance()
			attachedInstance = targetCaller:GetPluginInstance()
			currentCombat = targetCaller:GetCurrentCombat()
			targetCaller.UpdateWindowBars()
			targetCaller.CheckFor_CanStartRunning()
			
		elseif (event == "COMBAT_PLAYER_ENTER") then
			--> details create a new segment
			currentCombat = targetCaller:GetCurrentCombat()
			targetCaller.CheckFor_CanStartRunning()
			
		elseif (event == "COMBAT_PLAYER_LEAVE") then
			--> details finished a segment
			targetCaller.EndLoop()
			
		elseif (event == "DETAILS_INSTANCE_ENDRESIZE" or event == "DETAILS_INSTANCE_SIZECHANGED") then
			--> size of the window has changed
			targetCaller.UpdateWindowBars()
			
		elseif (event == "DETAILS_INSTANCE_STARTSTRETCH" or event == "DETAILS_INSTANCE_ENDSTRETCH") then
			--> the window started to be stretched
			targetCaller.UpdateWindowBars()
			
		elseif (event == "DETAILS_OPTIONS_MODIFIED") then
			--> an option has changed at details options panel
			targetCaller.UpdateWindowBars()
			
		elseif (event == "PLUGIN_DISABLED") then
			--> plugin has been disabled at the details options panel
			targetCaller.EndLoop()
		
		elseif (event == "PLUGIN_ENABLED") then
			--> plugin has been enabled at the details options panel
			attachedInstance = targetCaller:GetPluginInstance()
			currentCombat = targetCaller:GetCurrentCombat()
			targetCaller.CheckFor_CanStartRunning()
		
		elseif (event == "GROUP_ONENTER") then
			--> the player is now in a group
			targetCaller.CheckFor_CanStartRunning()
			
		elseif (event == "GROUP_ONLEAVE") then
			--> the player left the group
			if (targetCaller.ticker and not targetCaller.ticker._cancelled) then
				targetCaller.EndLoop()
			end
		
		elseif (event == "DETAILS_DATA_SEGMENTREMOVED") then
			--> old segment got deleted by the segment limit
			if (targetCaller.ticker and not targetCaller.ticker._cancelled) then
				--> there is an update going on, we need to stop
				targetCaller.EndLoop (true)
			end
		
		elseif (event == "DETAILS_DATA_RESET") then
			--> combat data got wiped
			if (targetCaller.ticker and not targetCaller.ticker._cancelled) then
				--> there is an update going on, we need to stop
				targetCaller.EndLoop (true)
				
			end
			
		end
		
	end
	
	function targetCaller:OnEvent (_, event, ...)
		if (event == "GROUP_ROSTER_UPDATE") then
			targetCaller.UpdateRoster()
			
		elseif (event == "PLAYER_TARGET_CHANGED") then
			targetCaller.TargetChanged()
		
		elseif (event == "ADDON_LOADED") then
			local AddonName = select (1, ...)
			if (AddonName == "Details_TargetCaller") then
				
				--> every plugin must have a OnDetailsEvent function
				function targetCaller:OnDetailsEvent (event, ...)
					return handle_details_event (event, ...)
				end
				
				--> Install: install -> if successful installed; saveddata -> a table saved inside details db, used to save small amount of data like configs
				local install, saveddata = Details:InstallPlugin ("RAID", L ["STRING_PLUGIN_NAME"], "Interface\\Icons\\Ability_Warrior_BattleShout", targetCaller, "DETAILS_PLUGIN_TARGET_CALLER", MINIMAL_DETAILS_VERSION_REQUIRED, "Details! Team", TCALLER_VERSION)
				if (type (install) == "table" and install.error) then
					print (install.error)
				end
				
				--> registering details events we need
				Details:RegisterEvent (targetCaller, "COMBAT_PLAYER_ENTER") --when details creates a new segment, not necessary the player entering in combat.
				Details:RegisterEvent (targetCaller, "COMBAT_PLAYER_LEAVE") --when details finishs a segment, not necessary the player leaving the combat.
				Details:RegisterEvent (targetCaller, "DETAILS_INSTANCE_ENDRESIZE") --when the player releases the grab after resize.
				Details:RegisterEvent (targetCaller, "DETAILS_INSTANCE_SIZECHANGED") --when a details window got its size changed.
				Details:RegisterEvent (targetCaller, "DETAILS_INSTANCE_STARTSTRETCH") --when the player start to stretch a window.
				Details:RegisterEvent (targetCaller, "DETAILS_INSTANCE_ENDSTRETCH") --stretch ends
				Details:RegisterEvent (targetCaller, "DETAILS_OPTIONS_MODIFIED") --fired when any options at details options panel is changed by the user.
				Details:RegisterEvent (targetCaller, "GROUP_ONENTER") --when the player enters in a group
				Details:RegisterEvent (targetCaller, "GROUP_ONLEAVE") --player left a group endd
				Details:RegisterEvent (targetCaller, "DETAILS_DATA_RESET") --details combat data has been wiped
				
				
			end
		end

	end

end
