The Cast Process

This is how I believe the spellcasting process goes in terms of events received
(not including combat log events, which are another kettle of fish entirely).

First of all, obviously this applies to character abilities that aren't
technically spells as well, such as Warrior or Rogue combat techniques, racial
abilities, or even crafting.

Second of all, I could be wrong about this, as it is based in many places on
assumptions that I have yet to test.

1/2. Possibly UNIT_SPELLCAST_FAILED(caster, spellname, spellrank)

Even before UNIT_SPELLCAST_SENT goes out, you can get a UNIT_SPELLCAST_FAILED.
This is because the WoW client can handle some FAILED cases even before sending
a request to the server -- the target is out of range or friendly, or you're
out of mana.  These are things there's no need to ask the server about.

1. UNIT_SPELLCAST_SENT(caster, spellname, spellrank, spelltarget)

The player presses an action button to activate some spell or ability.  The WoW
client sents a message to the server that the player wants to use the ability
in question on the target in question (which might be the targeted unit, the
focused unit, the moused-over unit, one of the units in the party or raid, or
even some unit just mentioned by name).  When the server gets this message, it
sends out the UNIT_SPELLCAST_SENT event to the player and everybody else in the
player's party or raid.  Note that this occurs before the server determines
whether it can actually happen.  Note also that none of the events that happen
after this one give information about the target.

2. UNIT_SPELLCAST_START(caster, spellname, spellrank)
or UNIT_SPELLCAST_FAILED(caster, spellname, spellrank)

Cases where the client can't resolve whether the cast is possible are now
resolved, such as whether the target is in line of sight, which requires a
server round trip to determine.  Such cases cause a UNIT_SPELLCAST_FAILED
event, just like the client-resolvable cases, which goes to the player and
everyone else in the player's party/raid, and the sequence ends.  But if no
failure happens, they get UNIT_SPELLCAST_START instead.  If the spell/ability
is instant (this includes channeled spells that don't have a casting time
before channeling begins), the action skips straight to UNIT_SPELLCAST_STOP.
If it has a cast time, though, the casting phase begins.

3. possible UNIT_SPELLCAST_DELAYED(caster, spellname, spellrank)
or UNIT_SPELLCAST_INTERRUPTED(caster, spellname, spellrank)

If the caster takes damage during casting, causing the cast timer to lose time,
the server sends out a UNIT_SPELLCAST_DELAYED event.  Damage to the caster that
does not cause loss of casting time does not trigger this event.  Anything that
interrupts the spellcast completely causes a UNIT_SPELLCAST_INTERRUPTED,
including interruption abilities and stuns from enemies as well as the player
canceling the spell him/herself.

4. UNIT_SPELLCAST_STOP(caster, spellname, spellrank)

The server sends out this event whenever casting reaches its conclusion,
whether the spell is successful or not.  If spellcasting is interrupted, you
get a UNIT_SPELLCAST_INTERRUPTED (see above) followed by a UNIT_SPELLCAST_STOP
(and possibly a few more INTERRUPTED events too).

5. UNIT_SPELLCAST_SUCCEEDED(caster, spellname, spellrank)
or UNIT_SPELLCAST_FAILED(caster, spellname, spellrank)

Another failure can happen here -- for example, if the caster was facing the
target when they started casting, but the caster has turned and/or the target
has moved since then, and the facing is no longer correct.  Or the target may
have moved out of range or line of sight.  Or the caster may have been drained
of mana and can no longer cast the spell.  At any rate, this triggers a
UNIT_SPELLCAST_FAILED and casting ends.  However, if none of these things
happen, a UNIT_SPELLCAST_SUCCEEDED event goes out.  Note that this is
regardless of whether the spell or ability is resisted, blocked, etc.

6. Combat log event stating what happened

The results of the spell or ability will then come out in the combat log.  The
process ends unless the spell is channeled.

7. If channeled, UNIT_SPELLCAST_CHANNEL_START(caster, spellname, spellrank)

Channeling begins, causing the server to send out this event.

8. UNIT_SPELLCAST_UPDATE(caster, spellname, spellrank)
and/or possibly UNIT_SPELLCAST_INTERRUPTED(caster, spellname, spellrank)
or UNIT_SPELLCAST_DELAYED(caster, spellname, spellrank)

Periodically during channeling, the server sends out UNIT_SPELLCAST_UPDATE
messages to indicate that channeling is still occurring.  If the caster takes
damage during channeling causing them to lose channeling time, there will be a
UNIT_SPELLCAST_DELAYED event, and if something completely interrupts casting
before the channeling time ends naturally, a UNIT_SPELLCAST_INTERRUPTED event
will go out.

9. UNIT_SPELLCAST_CHANNEL_STOP(caster, spellname, spellrank)

When the channeling time naturally comes to an end, the
UNIT_SPELLCAST_CHANNEL_STOP event goes out.

Targets

Note that only UNIT_SPELLCAST_SENT specifies the target of the spell.  The lack
of target arguments makes things interesting for an event handler writer such
as myself.  I have to trust that the WoW server will always send out events
telling me about the spellcasting process and that the WoW client will always
send them to the scripting API.  I also have to assume that the player can't
cast more than one spell at once; i.e. that the server won't send a
UNIT_SPELLCAST_SENT event before the previous spellcast is terminated.

This is because the only way to know the target, for purposes of filling out
the target-based % parameters when handling events that don't include any
target information as arguments, is to remember the spelltarget argument from
UNIT_SPELLCAST_SENT.  We can make sure that the caster is the player on those
events (ignoring events from other casters) and that the spellname and
spellrank arguments match those of the last UNIT_SPELLCAST_SENT (ignoring the
event if either name or rank differs from the remembered value).  This seems to
work fairly well.

So the way we do this is: we have global lasttarget, lastspell and lastrank
variables, which obviously store the last target, spell, and spell rank that
appeared in UNIT_SPELLCAST_SENT.  In the UNIT_SPELLCAST_SENT event handler, we
make sure to store these values.  Then, when another UNIT_SPELLCAST_* event
arrives, we first compare the caster with the player, discarding any spell
events not cast by the player, and compare the spellname and spellrank with
lastspell and lastrank, discarding any spell events that don't match those.  If
we make it this far without the event being discarded, the spell to which the
event refers must be directed at lasttarget.

Flowchart (with UNIT_SPELLCAST_ removed from event names for brevity)

                            yes
client-resolvable failure? -----> FAILED
           |
           | no
           v
          SENT
           |
           v                yes
server-resolvable failure? -----> FAILED
           |
           | no
           v
         START
           |<-------------+
           |              |
           v     yes      |
         delay? -----> DELAYED
           |
           | no
           v        yes
      interrupted? -----> INTERRUPTED --> STOP
           |
           | no
           v
       SUCCEEDED
           |
           v      no
        channel? ----> STOP
           |
           | yes
           v
     CHANNEL_START
           |
           |<-------------+<---+
           |              |    |
           v              |    |
     CHANNEL_UPDATE ----->|    |
           |              |    |
           v     yes      |    |
         delay? -----> DELAYED |
           |                   |
           | no                |
           v                   |
     CHANNEL_UPDATE -----------+
           |
           v        yes
      interrupted? -----> INTERRUPTED --> CHANNEL_STOP
           |
           | no
           v
      CHANNEL_STOP
