anchor.diff:  % Returns {ok, NewState} if we can fulfil the role, or either:
   %   * {error, already_fulfilled} --> if we've already fulfilled the role
   %   * {error, cannot_fulfil} --> if this role isn't offered by the actor
  -add_role(RoleName, State) ->
  -  AvailableRoles = State#conv_state.available_roles,
  -  ConversationRoles = State#conv_state.conversation_roles,
  -  % Check whether we've already fulfilled the role in the conversation
  -  AlreadyFulfilled = lists:member(RoleName, ConversationRoles),
  -  if not AlreadyFulfilled ->
  -      % Check we can fulfil the role
  -      CanFulfil = lists:member(RoleName, AvailableRoles),
  -      if CanFulfil ->
  -           NewConversationRoles = [RoleName|ConversationRoles],
  -           {ok, State#conv_state{conversation_roles=NewConversationRoles}};
  -         not CanFulfil ->
  -           {error, cannot_fulfil}
  -      end;
  -    AlreadyFulfilled ->
  -      {error, already_fulfilled}
  +add_role(ProtocolName, RoleName, ConversationID, State) ->
  +  % Firstly, check to see we're registered for the role
  +  ProtocolRoleMap = State#conv_state.protocol_role_map,
  +  ActiveProtocols = State#conv_state.active_protocols,
  +  RoleFindRes = orddict:find(ProtocolName, ProtocolRoleMap),
  +  AlreadyFulfilled = bidirectional_map:contains_left(ProtocolName, ActiveProtocols),
  +  case {RoleFindRes, AlreadyFulfilled} of
  +    {_, true} -> {error, already_fulfilled};
  +    {{ok, RoleRes}, false} when RoleRes == RoleName ->
  +      % We can fulfil it!
  +      NewActiveProtocols = bidirectional_map:store(ProtocolName, ConversationID),
  +      {ok, State#conv_state{active_protocols=NewActiveProtocols}};
  +    Other -> {error, cannot_fulfil}
     end.
  -
  -
   
   
   % We'll have a mixture of synchronous and asynchronous messages.
  
positive.code:   case AddRoleResult of
      {ok, NewStateData} ->
        error_logger:info_msg("INFO: Actor ~w with PID ~p registered for role
                               ~w.", [NewStateData#conv_state.actor_type_name,
                                      NewStateData#conv_state.actor_pid,
                                      RoleName]),
        {reply, ok, setup, NewStateData};
      {error, Err} ->
        error_logger:warning_msg("WARN: Actor ~w could not fulfil role ~w: ~p",
                                 [StateData#conv_state.actor_type_name,
                                  RoleName,
                                  Err]),
        {reply, {error, Err}, CurrentState, StateData}
    end.
  
  handle_terminate_conversation(ConversationID, CurrentState, StateData) ->
    ActorName = StateData#conv_state.actor_type_name,
    ActorPID = StateData#conv_state.actor_pid,
    CurrentCID = StateData#conv_state.conversation_id,
    if ConversationID == CurrentCID ->
         error_logger:info_msg("INFO: Actor ~w with PID ~p exiting conversation ~p.~n",
                               [ActorName, ActorPID, ConversationID]),
         {next_state, idle, freshen_state(StateData)};
       ConversationID =/= CurrentCID ->
         error_logger:warning_msg("WARN: Terminate conversation (ID ~p) called for monitor in " ++
                                  " different conversation (ID ~p) in actor type ~w with PID ~p.~n",
                                 [ConversationID, CurrentCID, ActorName, ActorPID]),
         {next_state, CurrentState, StateData}
    end.
  
  % Idle / setup have identical (boring) behaviours for async messages,
  % as most setup messages are synchronous.
  handle_setup_async(Inv = {invitation, _, _}, CurrentState, StateData) ->
    error_logger:error_msg("ERROR: Monitor got asynchronous invitation message ~p~n", [Inv]),
    {stop, error_invitation_async, StateData};
  handle_setup_async({terminate_conversation, ConvID}, CurrentState, StateData) ->
    handle_terminate_conversation(ConvID, CurrentState, StateData);
  handle_setup_async(Msg = {message, _MsgData}, CurrentState, StateData) ->
    error_logger:warning_msg("WARN: Got message (~p) in idle state: ignoring.~n ", [Msg, StateData]),
    {next_state, CurrentState, StateData};
  handle_setup_async(Other, CurrentState, StateData) ->
    error_logger:warning_msg("WARN: Got unhandled asynchronous message (~p) in ~p state: ignoring.",
                             [Other, CurrentState]),
    {next_state, CurrentState, StateData}.
  
  
  % Here, we deliver the message to the attached actor (which is a gen_server).
  
negative.code:   RecipientPID = StateData#conv_state.actor_pid,
    gen_server:cast(RecipientPID, Msg).
  
  handle_incoming_message(Msg = {message, MessageData}, ConversationID, StateData) ->
    Monitors = StateData#conv_state.monitors,
    % Get the protocol name for the conversation
    ActiveProtocols = StateData#conv_state.active_protocols,
    ProtocolRoleMap = StateData#conv_state.protocol_role_map,
    CurrentProtocol = StateData#conv_state.current_protocol,
    % TODO: Set CurrentProtocol if it is undefined
    ConversationProtocolResult = bidirectional_map:fetch_right(ConversationID),
    case ConversationProtocolResult of
      {ok, ProtocolName} when CurrentProtocol == ProtocolName ->
        CurrentRole = orddict:fetch(ProtocolName, ProtocolRoleMap),
        % Find the monitor instance for the current role
        % FIXME: This won't work -- we haven't set it yet!
        MonitorInstance = orddict:find(CurrentRole, Monitors),
        case MonitorInstance of
          {ok, Monitor} ->
            MonitorResult = monitor:recv(MessageData, Monitor),
            case MonitorResult of
              {ok, NewMonitorInstance} ->
                NewMonitors = orddict:store(CurrentRole, NewMonitorInstance, Monitors),
                NewStateData = StateData#conv_state{monitors=NewMonitors},
                % Do delegation stuff here
                % Send the MessageData to the session actor, let the logic in there
                % unpack and delegate to user code
                deliver_message(MessageData, NewStateData),
                {next_state, working, NewStateData};
              {error, Err} ->
                error_logger:warning_msg("WARN: Monitor for conversation ID ~p " ++
                                         "in actor ~w (instance PID ~p) with " ++
                                         "active role ~w failed when receiving " ++
                                         "message ~p. Error: ~p.~n",
                                         [ConversationID,
                                          StateData#conv_state.actor_type_name,
                                          StateData#conv_state.actor_pid,
                                          CurrentRole, Msg, Err]),
                {noreply, StateData}
            end;
          error ->
            error_logger:warning_msg("WARN: Could not find monitor for role ~w in " ++
                                     "actor of type ~w (instance PID ~p) in " ++
                                     "conversation with ID ~p.~n",
                                     [CurrentRole,
                                      StateData#conv_state.actor_type_name,
                                      StateData#conv_state.actor_pid,
                                      ConversationID]),
            {noreply, StateData}
        end;
      {ok, ProtocolName} when CurrentProtocol =/= ProtocolName ->
        % Our current protocol doesn't match the protocol associated with the message.
        % Ignore for now, but will have to do something with this later.
        error_logger:warning_msg("WARN: Actor ~w (instance PID ~p) could not " ++
                                     "handle message ~p as it is for a different protocol " ++
                                     "(~p) then the one that is currently running (~p).~n",
                                     [StateData#conv_state.actor_type_name,
                                      StateData#conv_state.actor_pid,
                                      ProtocolName, CurrentProtocol]),
        {noreply, StateData};
      error ->
        error_logger:warning_msg("WARN: Monitor for actor ~w (instance PID ~p) could not " ++
                                     "find protocol for conversation ID ~p.~n",
                                     [StateData#conv_state.actor_type_name,
                                      StateData#conv_state.actor_pid,
                                      ConversationID]),
        {noreply, StateData}
    end;
  handle_incoming_message(Other, _CID, StateData) ->
    error_logger:warning_msg("WARN: handle_incoming_message called for non_message ~p " ++
                             "in actor ~p (instance PID ~p)",
                             [Other, StateData#conv_state.actor_type_name,
                              StateData#conv_state.actor_pid]),
    {noreply, StateData}.
  
  
  % Module:handle_call(Request, From, State) -> Result
  % Result = {reply,Reply,NewState} | {reply,Reply,NewState,Timeout}
  %  | {reply,Reply,NewState,hibernate}
  %  | {noreply,NewState} | {noreply,NewState,Timeout}
  %  | {noreply,NewState,hibernate}
  %  | {stop,Reason,Reply,NewState} | {stop,Reason,NewState}
  %
  
  % Synchronous messages:
  %  * Invitation
  %  * Termination
  %  handle_invitation(ProtocolName, RoleName, ConversationID, StateData) ->
  %  handle_terminate_conversation(ConversationID, CurrentState, State)
  handle_call({invitation, ProtocolName, RoleName, ConversationID}, _Sender, State) ->
    handle_invitation(ProtocolName, RoleName, ConversationID, State);
  handle_call({terminate_conversation, ConversationID}, _Sender, State) ->
    handle_terminate_conversation(ConversationID, State);
  handle_call(Other, Sender, State) ->
    ActorTypeName = State#conv_state.actor_type_name,
    ActorPID = State#conv_state.actor_pid,
    error_logger:warning_msg("WARN: Monitor for actor ~w, instance PID ~p, received " ++
                             "unhandled synchronous messsage ~p from PID ~p.~n",
                             [ActorTypeName, ActorPID, Other, Sender]),
    {noreply, State}.
  
  % Module:handle_cast(Request, State) -> Result
  % Only async messages are actually data ones.
  % Delivering these, we'll need a conv ID, I think.
  handle_cast({msg, ConversationID, MessageData}, State) ->
    handle_incoming_message(MessageData, ConversationID, State);
  handle_cast(Other, State) ->
    ActorTypeName = State#conv_state.actor_type_name,
    ActorPID = State#conv_state.actor_pid,
    error_logger:warning_msg("WARN: Monitor for actor ~w, instance PID ~p, received " ++
                             "unhandled asynchronous messsage ~p.~n",
                             [ActorTypeName, ActorPID, Other]),
    {noreply, State}.
  
  handle_info(Info, State) ->
    ActorTypeName = State#conv_state.actor_type_name,
    ActorPID = State#conv_state.actor_pid,
    error_logger:warning_msg("WARN: Monitor for actor ~w, instance PID ~p, received " ++
                             "unhandled info messsage ~p.~n",
                             [ActorTypeName, ActorPID, Info]),
    {noreply, State}.
  
  terminate(Reason, State) ->
    ActorTypeName = State#conv_state.actor_type_name,
    ActorPID = State#conv_state.actor_pid,
    error_logger:warning_msg("ERROR: Monitor for actor ~w, instance PID ~p, terminated " ++
                             "for reason ~p.~n",
                             [ActorTypeName, ActorPID, Reason]),
    ok.
  
  
  