First version of providing event based updates to the frames.

Mere [07-13-13 - 13:02]
First version of providing event based updates to the frames.

While the events should be fired, only health and healthMax are actually hooked up.

As this works I wanted to get it checked in while I pull apart the rest.
Filename
CommandLine.lua
HealingBar.lua
HealingPanel.lua
PlayerEvents/EventDB.lua
PlayerEvents/EventManager.lua
RaidManagement.lua
RiftAddon.toc
diff --git a/CommandLine.lua b/CommandLine.lua
index e2bcc0f..ddc744a 100644
--- a/CommandLine.lua
+++ b/CommandLine.lua
@@ -281,6 +281,7 @@ local DebugOptions = {
 		{"tenman", "t", nil, "add some fake players to layouts" },
 		{"twentyman", "w", nil, "add some fake players to layouts" },
 		{"nofakes", "n", nil, "remove fake players from layouts" },
+        {"eventDBCheck", "x", nil, "Validate EventDB changes"},
 		}
 function MereHealingFrames.Commands.Debug(params)
 	table.remove(params,1)
@@ -317,6 +318,8 @@ function MereHealingFrames.Commands.Debug(params)
 		MereHealingFrames.RaidManagement.AddFakes(20, false, 3, 6, 2, 9)
 	elseif (getOptParams.nofakes) then
 		MereHealingFrames.RaidManagement.RemoveFakes()
+    elseif (getOptParams.eventDBCheck) then
+        MereHealingFrames.PlayerEventDB.ValidateDB()
     else
         print("/mhf debug should be followed by the options:")
         GetOpt.print(DebugOptions)
diff --git a/HealingBar.lua b/HealingBar.lua
index 029aad0..014d51f 100644
--- a/HealingBar.lua
+++ b/HealingBar.lua
@@ -24,15 +24,27 @@ MereHealingFrames.HealingBar = {
     topLeftX = nil,
     topLeftY = nil,
     borderedText = true,
-    fontSize = 10
+    fontSize = 10,
+    valueChangeEvent = nil,
+    maxValueChangeEvent = nil,
+    updateValueFunction = nil,
+    updateMaxValueFunction = nil
 	}
-
+
+local healingBarId = 1
 function MereHealingFrames.HealingBar:new (settings)
 	local this = settings or {}

 	setmetatable(this, self)
-	self.__index = self
-	return this
+	self.__index = self
+
+    this.healingBarName = "HealingBar" .. healingBarId
+    healingBarId = healingBarId + 1
+
+    this.updateValueFunction = function(handle, value) MereHealingFrames.HealingBar.UpdateValue(this, value) end
+    this.updateMaxValueFunction = function(handle, value) MereHealingFrames.HealingBar.UpdateMax(this, value) end
+
+    return this
 end

 function MereHealingFrames.HealingBar.Create(settings, ParentFrame)
@@ -166,6 +178,25 @@ function MereHealingFrames.HealingBar:UpdateBar(value, maxValue)
     return self:UpdateBarDisplay()
 end

+function MereHealingFrames.HealingBar:UpdateEvents(currentEvent, maxEvent)
+    self:ReplaceEventHandler("valueChangeEvent", currentEvent, self.updateValueFunction)
+    self:ReplaceEventHandler("maxValueChangeEvent", maxEvent, self.updateMaxValueFunction)
+end
+
+function MereHealingFrames.HealingBar:ReplaceEventHandler(eventHandlerName, newEvent, updateFunction)
+    local eventHandle = self[eventHandlerName]
+
+    if (eventHandle) then
+        Command.Event.Detach(eventHandle, updateFunction, self.healingBarName)
+    end
+
+    self[eventHandlerName] = newEvent
+
+    if newEvent then
+        Command.Event.Attach(newEvent, updateFunction, self.healingBarName)
+    end
+end
+
 function MereHealingFrames.HealingBar:UpdateValue(value)
     self.CurrentValue = value
     return self:UpdateBarDisplay()
diff --git a/HealingPanel.lua b/HealingPanel.lua
index e45e27c..8eb3153 100644
--- a/HealingPanel.lua
+++ b/HealingPanel.lua
@@ -165,7 +165,9 @@ function MereHealingFrames.HealingPanel:SetPlayerInfo(playerInfo)
 	MereHealingFrames.UnitIdPanels[self.unitId] = panels

 	self:SetupPlayerValues()
-
+
+    self.Health:UpdateEvents(playerInfo.PlayerEvents.Events.health, playerInfo.PlayerEvents.Events.healthMax)
+
 	-- TODO Secure call
 	if not playerInfo.isFake then
 		self.Mask:SetMouseoverUnit(self.unitId)
@@ -547,11 +549,11 @@ function MereHealingFrames.HealingPanel:ResizeBars()
 end

 function MereHealingFrames.HealingPanel:UpdateHealth(value)
-	return self.Health:UpdateValue(value)
+	--return self.Health:UpdateValue(value)
 end

 function MereHealingFrames.HealingPanel:UpdateHealthMax(value)
-	return self.Health:UpdateMax(value)
+	--return self.Health:UpdateMax(value)
 end

 function MereHealingFrames.HealingPanel:UpdateMana(value)
diff --git a/PlayerEvents/EventDB.lua b/PlayerEvents/EventDB.lua
new file mode 100644
index 0000000..8f5bd36
--- /dev/null
+++ b/PlayerEvents/EventDB.lua
@@ -0,0 +1,130 @@
+-- This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
+-- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/
+-- or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+
+local MereHealingFrames, privateVars = ...
+
+MereHealingFrames.PlayerEventDB = MereHealingFrames.PlayerEventDB or {}
+
+-- this is a db of player event info, it's used to build the internal event structures and also for Matching things
+-- up in the UI
+
+-- Type is what kind of event:
+--   number is a number
+--   toggle is a boolean
+--   calling is an indication of change in calling
+--   ready is readycheck state (nil, true or false)
+
+-- numbers may also have a MaxEvent field to indicate the linked Max Event, eg ManaChange and ManaMaxChange.
+-- not all numbers will have a max, eg range
+
+MereHealingFrames.PlayerEventDB.EventDB = {
+    mana = {
+        Type = 'number',
+        MaxEvent = 'manaMax'
+    },
+    manaMax = {
+        Type = 'number'
+    },
+
+    health = {
+        Type = 'number',
+        MaxEvent = 'healthMax'
+    },
+    healthMax = {
+        Type = 'number'
+    },
+
+    absorb = {
+        Type = 'number',
+        MaxEvent = 'healthMax'
+    },
+
+    energy = {
+        Type = 'number',
+        MaxEvent = 'energyMax'
+    },
+    energyMax = {
+        Type = 'number'
+    },
+
+    power = {
+        Type = 'number',
+        MaxValue = 100
+    },
+
+    role = {
+        Type = 'number'
+    },
+
+    ready = {
+        Type = 'ready'
+    },
+
+    aggro = {
+        Type = 'toggle'
+    },
+
+    blocked = {
+        Type = 'toggle'
+    },
+
+    offline = {
+        Type = 'toggle'
+    },
+
+    dead = {
+        Type = 'toggle'
+    },
+
+    afk = {
+        Type = 'toggle'
+    },
+
+    name = {
+        Type = 'text'
+    },
+
+    calling = {
+        Type = 'calling'
+    },
+
+    range = {
+        Type = 'number'
+    },
+
+    castRange = {
+        Type = 'number'
+    },
+
+    inRange = {
+        Type = 'toggle'
+    }
+}
+
+
+function MereHealingFrames.PlayerEventDB.ValidateDB()
+    for EventName, EventDetails in pairs(MereHealingFrames.PlayerEventDB.EventDB) do
+        if (EventDetails.Type == nil) then
+            print(string.format("Event %s has no defined Type", EventName))
+        end
+
+        if (EventDetails.Type == 'number') then
+            if (EventDetails.MaxEvent ~= nil) then
+                local maxEvent = MereHealingFrames.PlayerEventDB.EventDB[EventDetails.MaxEvent]
+                if (maxEvent == nil) then
+                    print(string.format("Event %s has a non-existent MaxEvent %s", EventName, EventDetails.MaxEvent))
+                elseif (maxEvent.Type ~= 'number') then
+                    print(string.format("Event %s uses a non-numeric MaxEvent %s", EventName, EventDetails.MaxEvent))
+                end
+            end
+        elseif (EventDetails.Type == 'text') then
+        elseif (EventDetails.Type == 'calling') then
+        elseif (EventDetails.Type == 'toggle') then
+        elseif (EventDetails.Type == 'ready') then
+        else
+            print(string.format("Event %s has unknown Type %s", EventName, EventDetails.Type))
+        end
+    end
+end
+
diff --git a/PlayerEvents/EventManager.lua b/PlayerEvents/EventManager.lua
new file mode 100644
index 0000000..dd79e70
--- /dev/null
+++ b/PlayerEvents/EventManager.lua
@@ -0,0 +1,67 @@
+-- This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
+-- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/
+-- or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
+
+local MereHealingFrames, privateVars = ...
+
+MereHealingFrames.PlayerEventManager = MereHealingFrames.PlayerEventManager or {}
+
+local NextPlayerEventSetId = 0
+
+local PlayerEventFactoryBucket = {}
+
+function MereHealingFrames.PlayerEventManager.NewEvents()
+    if #PlayerEventFactoryBucket ~= 0 then
+        return table.remove(PlayerEventFactoryBucket)
+    else
+        return MereHealingFrames.PlayerEventManager.PlayerEvents:new({})
+    end
+end
+
+function MereHealingFrames.PlayerEventManager.OldEvents(playerEvents)
+    playerEvents:ResetEvents()
+    return table.insert(PlayerEventFactoryBucket, playerEvents)
+end
+
+MereHealingFrames.PlayerEventManager.PlayerEvents = {}
+
+function MereHealingFrames.PlayerEventManager.PlayerEvents:new()
+    local this = {}
+
+    setmetatable(this, self)
+    self.__index = self
+
+    this.Functions = {}
+    this.Events = {}
+    this:HookEvents()
+
+    return this
+end
+
+function MereHealingFrames.PlayerEventManager.PlayerEvents:HookEvents()
+    local PlayerEventSetId = NextPlayerEventSetId
+    NextPlayerEventSetId = NextPlayerEventSetId + 1
+
+    local EventSetName = 'PlayerEvents.EventSet' .. PlayerEventSetId
+    self.EventSetName = EventSetName
+
+    for EventName, EventDetails in pairs(MereHealingFrames.PlayerEventDB.EventDB) do
+        local EventPath = EventSetName .. '.' .. EventName
+        local caller, handle = Utility.Event.Create("MereHealingFrames", EventPath)
+        self.Functions[EventName] = caller
+        self.Events[EventName] = handle
+    end
+
+    self.EventTable = Event.MereHealingFrames[EventSetName]
+end
+
+function MereHealingFrames.PlayerEventManager.PlayerEvents:ResetEvents()
+    -- I so hope this isn't expensive...
+    for eventName, eventHandle in pairs(self.Events) do
+        local hookedEvents = Inspect.Event.List(eventHandle)
+        for _, eventDetail in ipairs(hookedEvents) do
+            print(string.format("Detaching from event %s, label %s", eventName, eventDetail.label))
+            Command.Event.Detach(eventHandle, eventDetail.handler, eventDetail.label, eventDetail.priority, eventDetail.owner)
+        end
+    end
+end
\ No newline at end of file
diff --git a/RaidManagement.lua b/RaidManagement.lua
index d0d3799..de8a657 100644
--- a/RaidManagement.lua
+++ b/RaidManagement.lua
@@ -107,7 +107,9 @@ function playerInfo:new(settings, unitId, spec)
 	this.grouping = calculateGrouping(spec)

 	this.buffMgmt = MereHealingFrames.BuffManager.PlayerBuffs:new(this)
-
+
+    this.PlayerEvents = MereHealingFrames.PlayerEventManager.NewEvents()
+
 	this:UpdateDetails()
 	return this
 end
@@ -134,7 +136,9 @@ end

 function playerInfo:delete()
 	MereHealingFrames.RaidManagement.Units[self.unitId] = nil
-	MereHealingFrames.RaidManagement.Specifiers[self.specifier] = nil
+	MereHealingFrames.RaidManagement.Specifiers[self.specifier] = nil
+    MereHealingFrames.PlayerEventManager.OldEvents(self.PlayerEvents)
+    self.PlayerEvents = nil
 end

 local function PanelUpdate(unitId, targetFunction, value)
@@ -172,6 +176,7 @@ local eventLookups =

 function playerInfo:UpdateValue(field, value)
 	self[field] = value
+    self.PlayerEvents.Functions[field](value)
 	return PanelUpdate(self.unitId, eventLookups[field], value)
 end

@@ -184,6 +189,7 @@ function playerInfo.UpdateUnitValues(UnitIds, field)
         local playerInfo = localRMUnits[unitId]
         if playerInfo then
             playerInfo[field] = value
+            playerInfo.PlayerEvents.Functions[field](value)
             PanelUpdate(unitId, PanelFunction, value)
         end
     end
@@ -250,15 +256,29 @@ function playerInfo:RecalcRange()
 	local xDiff, yDiff, zDiff = playerPos.x - self.coordX, playerPos.y - self.coordY, playerPos.z - self.coordZ

 	local distanceFromPlayer = localsqrt ((xDiff * xDiff) + (yDiff * yDiff) + (zDiff * zDiff))
+
+    local castDistanceFromPlayer = distanceFromPlayer - self.radius - playerPos.radius
+
+    local currentCastDistance = self.castDistanceFromPlayer
+
+    if (currentCastDistance == castDistanceFromPlayer) then
+        return
+    end
+
     self.distanceFromPlayer = distanceFromPlayer
+    self.castDistanceFromPlayer = castDistanceFromPlayer
+
+    self.PlayerEvents.Functions.range(distanceFromPlayer)
+    self.PlayerEvents.Functions.castRange(castDistanceFromPlayer)

 	local currentRange = self.inRange
-	local newRange = (distanceFromPlayer < (MereHealingFrames.SpellRange + self.radius + playerPos.radius))
+	local newRange = (castDistanceFromPlayer < MereHealingFrames.SpellRange)

 	if newRange == currentRange then
 		return
     else
         self.inRange = newRange
+        self.PlayerEvents.Functions.inRange(newRange)
 		return PanelUpdate(self.unitId, MereHealingFrames.HealingPanel.StatusChange,
 			newRange)
 	end
diff --git a/RiftAddon.toc b/RiftAddon.toc
index 08a07d4..43ba4c6 100644
--- a/RiftAddon.toc
+++ b/RiftAddon.toc
@@ -14,6 +14,8 @@ Environment = "2.3"
 RunOnStartup = {
 	"Debug.lua",
 	"StaticGlobals.lua",
+	"PlayerEvents/EventDB.lua",
+	"PlayerEvents/EventManager.lua",
 	"UI.ShadowedText.lua",
 	"HealingPanel.lua",
 	"HealingPanel.RoleIcon.lua",