/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program 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.

This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/////////////////////////////////////////////////////////////////////////////
//
//  MESSAGE    : plNotifyMsg
//  PARAMETERS : none
//
//  PURPOSE    : This is the message that notifies someone (either a responder or activator)
//             : that some event or transition of state has happened
//
//

#include "plNotifyMsg.h"


plNotifyMsg::plNotifyMsg(const plKey &s, const plKey &r)
{
    fSender = s;
    AddReceiver(r);
    IInit();
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IInit and ~plNotifyMsg
//  PARAMETERS : none
//
//  PURPOSE    : Initialization (called from constructors) and destructor
//
void plNotifyMsg::IInit()
{ 
    SetBCastFlag(plMessage::kNetPropagate);
    fType = kActivator;
    fState = 0.0f;          // start state at (completely) false
    fID = 0;
}

plNotifyMsg::~plNotifyMsg()
{
    ClearEvents();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddEvent
//  PARAMETERS : ed     - pointer to event that needs to be added (be recreating)
//
//  PURPOSE    : Add an event record to this notify message
//
//
void plNotifyMsg::AddEvent( proEventData* ed )
{
    switch ( ed->fEventType )
    {
        case proEventData::kCollision:
            {
                proCollisionEventData *evt = (proCollisionEventData *)ed;
                AddCollisionEvent(evt->fEnter, evt->fHitter, evt->fHittee );
            }
            break;

        case proEventData::kSpawned:
            {
                proSpawnedEventData *evt = (proSpawnedEventData *)ed;
                AddSpawnedEvent( evt->fSpawner, evt->fSpawnee );
            }
            break;

        case proEventData::kPicked:
            {
                proPickedEventData *evt = (proPickedEventData *)ed;
                AddPickEvent( evt->fPicker, evt->fPicked, evt->fEnabled, evt->fHitPoint );
            }
            break;

        case proEventData::kContained:
            {
                proContainedEventData *evt = (proContainedEventData *)ed;
                AddContainerEvent( evt->fContained, evt->fContainer, evt->fEntering );
            }
            break;
        
        case proEventData::kCallback:
            {
                proCallbackEventData *evt = (proCallbackEventData *)ed;
                AddCallbackEvent( evt->fEventType );
            }
            break;

        case proEventData::kResponderState:
            {
                proResponderStateEventData *evt = (proResponderStateEventData *)ed;
                AddResponderStateEvent( evt->fState );
            }
            break;

        case proEventData::kMultiStage:
            {
                proMultiStageEventData *evt = (proMultiStageEventData *)ed;
                AddMultiStageEvent( evt->fStage, evt->fEvent, evt->fAvatar );
            }
            break;
            
        case proEventData::kCoop:
            {
                proCoopEventData *evt = (proCoopEventData *)ed;
                AddCoopEvent( evt->fID, evt->fSerial);
            }

        case proEventData::kControlKey:
            {
                proControlKeyEventData *evt = (proControlKeyEventData *)ed;
                AddControlKeyEvent( evt->fControlKey, evt->fDown );
            }
            break;

        case proEventData::kFacing:
            {
                proFacingEventData *evt = (proFacingEventData *)ed;
                AddFacingEvent( evt->fFacer, evt->fFacee, evt->dot, evt->enabled );
            }
            break;
        
        case proEventData::kActivate:
            {
                proActivateEventData *evt = (proActivateEventData *)ed;
                AddActivateEvent( evt->fActivate );
            }
            break;

        case proEventData::kVariable:
            {
                proVariableEventData *evt = (proVariableEventData *)ed;
                switch (evt->fDataType)
                {
                    case proEventData::kNumber:
                        AddVariableEvent(evt->fName, evt->fNumber);
                        break;
                    case proEventData::kKey:
                        AddVariableEvent(evt->fName, evt->fKey);
                        break;
                }
            }
            break;
        case proEventData::kClickDrag:
            {
                proClickDragEventData* evt = (proClickDragEventData*)ed;
                AddClickDragEvent(evt->picker, evt->picked, evt->animPos);
            }
            break;
        case proEventData::kOfferLinkingBook:
            {
                proOfferLinkingBookEventData* evt = (proOfferLinkingBookEventData*)ed;
                AddOfferBookEvent(evt->offerer, evt->targetAge, evt->offeree);
            }
        case proEventData::kBook:
            {
                proBookEventData* evt = (proBookEventData*)ed;
                AddBookEvent( evt->fEvent, evt->fLinkID );
            }
        case proEventData::kClimbingBlockerHit:
            {
                proClimbingBlockerHitEventData* evt = (proClimbingBlockerHitEventData*)ed;
                AddHitClimbingBlockerEvent(evt->fBlockerKey);
            }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddCollisionEvent
//  PARAMETERS : enter  - true for when entering collision and false when exiting collision
//             : other  - the plKey of the other object involved (Hitter)
//             : self   - the plKey to probably us (Hittee)
//
//  PURPOSE    : Add a collision event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddCollisionEvent( hsBool enter, const plKey &other, const plKey &self, hsBool onlyOneCollision )
{
    // if this is the normal case of there can only be one collision, then get rid of any others
    if ( onlyOneCollision )
    {
        // remove records that are like the one being added
        int num_recs = fEvents.GetCount();
        if ( num_recs > 0 )
        {
            int i;
            for ( i=0; i<num_recs; i++ )
            {
                // see if its the same type 
                proEventData* pEDTest = fEvents.Get(i);
                if ( pEDTest->fEventType == proEventData::kCollision )
                {
                    // remove it
                    delete fEvents[i];
                    fEvents.Remove(i);
                    // then jump out.. the count is no longer good and anyway there should only be one of the same type
                    break;
                }
            }
        }
    }

    // create the collision event record
    proCollisionEventData* pED = TRACKED_NEW proCollisionEventData;
    pED->fEnter = enter;
    pED->fHitter = other;
    pED->fHittee = self;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddCallbackEvent
//  PARAMETERS : event  - the event type, as enumerated in plEventCallbackMsg.h
//
//
void plNotifyMsg::AddCallbackEvent( Int32 event )
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kCallback )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the collision event record
    proCallbackEventData* pED = TRACKED_NEW proCallbackEventData;
    pED->fEventType = event;
    fEvents.Append(pED);    // then add it to the list of event records
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddResponderStateEvent
//  PARAMETERS : state  - the state for the responder to switch to before triggering
//
//
void plNotifyMsg::AddResponderStateEvent( Int32 state )
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kResponderState )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the collision event record
    proResponderStateEventData* pED = TRACKED_NEW proResponderStateEventData;
    pED->fState = state;
    fEvents.Append(pED);    // then add it to the list of event records
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddMultiStageEvent
//  PARAMETERS : stage   - the stage the multistage behavior is on
//             : event   - what was the event that happened
//
//
void plNotifyMsg::AddMultiStageEvent( Int32 stage, Int32 event, const plKey& avatar )
{
    // we can have multi events of this type
    // create the mutlistage event record
    proMultiStageEventData* pED = TRACKED_NEW proMultiStageEventData;
    pED->fStage = stage;
    pED->fEvent = event;
    pED->fAvatar = avatar;
    fEvents.Append(pED);    // then add it to the list of event records
}

void plNotifyMsg::AddCoopEvent(UInt32 id, UInt16 serial)
{
    proCoopEventData *pED = TRACKED_NEW proCoopEventData;
    pED->fID = id;
    pED->fSerial = serial;
    fEvents.Append(pED);
}

void plNotifyMsg::AddSpawnedEvent (const plKey &spawner, const plKey &spawnee)
{
    proSpawnedEventData* pED = TRACKED_NEW proSpawnedEventData();
    pED->fSpawner = spawner;
    pED->fSpawnee = spawnee;
    fEvents.Append(pED);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddActivateEvent
//  PARAMETERS : activate  - true or false
//
//  PURPOSE    : Sometimes you just want a yes or no
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddActivateEvent( hsBool activate )
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kActivate )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the collision event record
    proActivateEventData* pED = TRACKED_NEW proActivateEventData;
    pED->fActive = true;
    pED->fActivate = activate;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPickEvent
//  PARAMETERS : other  - the plKey of the other object involved (Picker)
//             : self   - the plKey to probably us (Picked)
//
//  PURPOSE    : Add a pick event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddPickEvent( const plKey &other, const plKey& self, hsBool enabled, hsPoint3 hitPoint )
{

    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kPicked )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the pick event record
    proPickedEventData* pED = TRACKED_NEW proPickedEventData;
    pED->fPicker = other;
    pED->fPicked = self;
    pED->fEnabled = enabled;
    pED->fHitPoint = hitPoint;

    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddContainerEvent
//  PARAMETERS : container  - the plKey of the object contained 
//             : contained   - the plKey of the containing volume
//
//  PURPOSE    : Add a container event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddContainerEvent( const plKey &container, const plKey &contained, hsBool entering )
{

    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kContained )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the pick event record
    proContainedEventData* pED = TRACKED_NEW proContainedEventData;
    pED->fContained = contained;
    pED->fContainer = container;
    pED->fEntering = entering;
    fEvents.Append(pED);    // then add it to the list of event records
}



/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddFacingEvent
//  PARAMETERS : other  - the plKey of the other object involved (Facer)
//             : self   - the plKey to probably us (Facee)
//             : dot    - the dot prod. of the facing angle
//
//  PURPOSE    : Add a facing event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddFacingEvent( const plKey &other, const plKey &self, hsScalar dot, hsBool enabled )
{

    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kFacing )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the pick event record
    proFacingEventData* pED = TRACKED_NEW proFacingEventData;
    pED->fFacer = other;
    pED->fFacee = self;
    pED->dot = dot;
    pED->enabled = enabled;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddControlKeyEvent
//  PARAMETERS : id     - identification, could be the controlkey that was hit
//             : down   - whether the control is going down or up in action
//
//  PURPOSE    : Add a control key event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddControlKeyEvent( Int32 key, hsBool down )
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kControlKey )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the control key event record
    proControlKeyEventData* pED = TRACKED_NEW proControlKeyEventData;
    pED->fControlKey = key;
    pED->fDown = down;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddVariableEvent
//  PARAMETERS : name    - name of the variable
//             : number  - the value of the variable as a number
//
//  PURPOSE    : Add a variable event record to this notify message
//
void plNotifyMsg::AddVariableEvent( const char* name, hsScalar number )
{
    // create the control key event record
    proVariableEventData* pED = TRACKED_NEW proVariableEventData;
    pED->fName = hsStrcpy(nil,name);
//  pED->fName = (char*)name;
    pED->fDataType = proEventData::kNumber;
    pED->fNumber = number;
    fEvents.Append(pED);    // then add it to the list of event records
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddVariableEvent
//  PARAMETERS : name    - name of the variable
//             : number  - the value of the variable as a number
//
//  PURPOSE    : Add a variable event record to this notify message
//
void plNotifyMsg::AddVariableEvent( const char* name, const plKey &key )
{
    // create the control key event record
    proVariableEventData* pED = TRACKED_NEW proVariableEventData;
    pED->fName = hsStrcpy(nil,name);
//  pED->fName = (char*)name;
    pED->fDataType = proEventData::kKey;
    pED->fKey = key;
    fEvents.Append(pED);    // then add it to the list of event records
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddClickDragEvent
//  PARAMETERS : picker     - always the local player
//             : pickee    - the click/draggable
//             : animPos - 0.0 to 1.0 % of the way we are through the animation
//
//  PURPOSE    : Add a click/drag event record to this notify message
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddClickDragEvent( const plKey& dragger, const plKey& dragee, hsScalar animPos )
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kClickDrag )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the control key event record
    proClickDragEventData* pED = TRACKED_NEW proClickDragEventData;
    pED->picked = dragee;
    pED->picker = dragger;
    pED->animPos = animPos;
    fEvents.Append(pED);    // then add it to the list of event records
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddOfferBookEvent
//  PARAMETERS : offerer   - the book offerer - the local player on the sender's machine, a remote avatar at the receiver's end
//             : targetAge - the age we are offering.  this # is taken from the konstant list of age link panels in xLinkingBookPopupGUI.py
//
//  PURPOSE    : Add an OfferBookEvent - note this message should NOT EVER be locally delivered - Networked ONLY and only to a specific net transport member.
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddOfferBookEvent(const plKey& offerer, int targetAge, int offeree)
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kOfferLinkingBook )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the control key event record
    proOfferLinkingBookEventData* pED = TRACKED_NEW proOfferLinkingBookEventData;
    pED->offerer = offerer;
    pED->targetAge = targetAge;
    pED->offeree = offeree;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddBookEvent
//  PARAMETERS : event - The type of event we are, as defined by the enum in pfJournalBook.h
//             : linkID - For image link event types, the link ID as defined in the esHTML source. Otherwise, unused.
//
//  PURPOSE    : Add an OfferBookEvent - note this message should NOT EVER be locally delivered - Networked ONLY and only to a specific net transport member.
//             : Remove like event records, only the last one counts
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddBookEvent( UInt32 event, UInt32 linkID /*=0*/)
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kBook )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the control key event record
    proBookEventData* pED = TRACKED_NEW proBookEventData;
    pED->fEvent = event;
    pED->fLinkID = linkID;
    fEvents.Append(pED);    // then add it to the list of event records
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddClimbingBlockerHit
//  PARAMETERS : fBlockerKey - the key of the blocker we hit
//
//  PURPOSE    : this is to notify python we hit a specific climbing blocker 
//             : 
//
// NOTE: To test for duplicate record, it only checks for records of the same type
//     : Eventually, it might be wise to check if the same 'self' key also?
//
void plNotifyMsg::AddHitClimbingBlockerEvent(const plKey &blocker)
{
    // remove records that are like the one being added
    int num_recs = fEvents.GetCount();
    if ( num_recs > 0 )
    {
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == proEventData::kClimbingBlockerHit )
            {
                // remove it
                delete fEvents[i];
                fEvents.Remove(i);
                // then jump out.. the count is no longer good and anyway there should only be one of the same type
                break;
            }
        }
    }

    // create the control key event record
    proClimbingBlockerHitEventData* pED = TRACKED_NEW proClimbingBlockerHitEventData;
    pED->fBlockerKey = blocker;
    fEvents.Append(pED);    // then add it to the list of event records
}



/////////////////////////////////////////////////////////////////////////////
//
//  Function   : FindEventRecord
//  PARAMETERS : eventtype  -  the event type record that we are looking for
//
//  PURPOSE    : Find the first record in the event records that is of type eventtype
//
proEventData* plNotifyMsg::FindEventRecord( Int32 eventtype )
{
    // make sure that its a legal event type
    if ( eventtype >= 0 && eventtype < proEventData::kNone )
    {
        // loop thru the event records looking for what they want
        int num_recs = fEvents.GetCount();
        int i;
        for ( i=0; i<num_recs; i++ )
        {
            // see if its the same type 
            proEventData* pEDTest = fEvents.Get(i);
            if ( pEDTest->fEventType == eventtype )
                return pEDTest;
        }
    }
    return nil;
}



/////////////////////////////////////////////////////////////////////////////
//
//  Function   : ClearEvents
//  PARAMETERS : none
//
//  PURPOSE    : clear all the event records
//
void plNotifyMsg::ClearEvents()
{
    // clean up fEvent records
    int i;
    for( i = 0; i < fEvents.GetCount(); i++ )
        delete fEvents[i];
    fEvents.Reset();
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Read
//  PARAMETERS : stream  - where to read the data from
//             : mgr     - resource manager (for special help)
//
//  PURPOSE    : Read object from stream
//
void plNotifyMsg::Read(hsStream* stream, hsResMgr* mgr)
{
    plMessage::IMsgRead(stream, mgr);
    // read in the static data
    fType = stream->ReadSwap32();
    stream->ReadSwap(&fState);
    fID = stream->ReadSwap32();
    // read in the variable part of the message
    Int32 numberEDs = stream->ReadSwap32();
    fEvents.SetCountAndZero(numberEDs);
    if ( numberEDs > 0 )
    {
        int i;
        for ( i=0 ; i < numberEDs ; i++ )
        {
            proEventData* pED = proEventData::Read( stream, mgr );
            fEvents[i] = pED;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : Write
//  PARAMETERS : stream  - where to write the data to
//             : mgr     - resource manager (for special help)
//
//  PURPOSE    : Write object from stream
//
void plNotifyMsg::Write(hsStream* stream, hsResMgr* mgr)
{
    plMessage::IMsgWrite(stream, mgr);
    // write static data
    stream->WriteSwap32(fType);
    stream->WriteSwap(fState);
    stream->WriteSwap32(fID);
    // then write the variable data
    Int32 numberEDs = fEvents.Count();
    stream->WriteSwap32(numberEDs);
    if ( numberEDs > 0 )
    {
        // write out each record
        int i;
        for ( i=0 ; i < numberEDs; i++ )
        {
            fEvents[i]->Write(stream,mgr);
        }
    }

}

enum NotifyMsgFlags
{
    kNotifyMsgType,
    kNotifyMsgState,
    kNotifyMsgID,
    kNotifyMsgEDs,
};

#include "../pnNetCommon/plNetApp.h"

void plNotifyMsg::ReadVersion(hsStream* s, hsResMgr* mgr)
{
    plMessage::IMsgReadVersion(s, mgr);

    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kNotifyMsgType))
        fType = s->ReadSwap32();

    if (contentFlags.IsBitSet(kNotifyMsgState))
        s->ReadSwap(&fState);

    if (contentFlags.IsBitSet(kNotifyMsgID))
        fID = s->ReadSwap32();

    if (contentFlags.IsBitSet(kNotifyMsgEDs))
    {
        // read in the variable part of the message
        Int32 numberEDs = s->ReadSwap32();
        fEvents.SetCountAndZero(numberEDs);
        if (numberEDs > 0)
        {
            for (int i = 0; i < numberEDs ; i++)
            {
                proEventData* pED = proEventData::ReadVersion(s, mgr);
                fEvents[i] = pED;
            }
        }
    }

    plKey avKey = GetAvatarKey();
    if (plNetClientApp::GetInstance() && avKey == plNetClientApp::GetInstance()->GetLocalPlayerKey())
    {
        SetBCastFlag(plMessage::kNetStartCascade, true);
        SetBCastFlag(plMessage::kNetNonLocal | plMessage::kNetPropagate, false);
    }
}

void plNotifyMsg::WriteVersion(hsStream* s, hsResMgr* mgr)
{
    plMessage::IMsgWriteVersion(s, mgr);

    hsBitVector contentFlags;
    contentFlags.SetBit(kNotifyMsgType);
    contentFlags.SetBit(kNotifyMsgState);
    contentFlags.SetBit(kNotifyMsgID);
    contentFlags.SetBit(kNotifyMsgEDs);
    contentFlags.Write(s);

    // kNotifyMsgType
    s->WriteSwap32(fType);

    // kNotifyMsgState
    s->WriteSwap(fState);

    // kNotifyMsgID
    s->WriteSwap32(fID);

    // kNotifyMsgEDs
    Int32 numberEDs = fEvents.Count();
    s->WriteSwap32(numberEDs);
    if (numberEDs > 0)
    {
        // write out each record
        for (int i = 0; i < numberEDs; i++)
        {
            fEvents[i]->WriteVersion(s, mgr);
        }
    }
}


plKey plNotifyMsg::GetAvatarKey()
{
    for (int i = 0; i < fEvents.GetCount(); i++)
    {
        proEventData *event = fEvents[i];

        switch (event->fEventType)
        {
        case proEventData::kCollision:
            return ( (proCollisionEventData *)event )->fHitter;

        case proEventData::kPicked:
            return ( (proPickedEventData *)event )->fPicker;
        
        case proEventData::kSpawned:
            return ( (proSpawnedEventData *)event )->fSpawnee;

        case proEventData::kMultiStage:
            return ( (proMultiStageEventData *)event )->fAvatar;
        }
    }

    return nil;
}


///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////


proEventData* proEventData::ICreateEventDataType(Int32 type)
{
    switch (type)
    {
    case kCollision:        return TRACKED_NEW proCollisionEventData;
    case kPicked:           return TRACKED_NEW proPickedEventData;
    case kControlKey:       return TRACKED_NEW proControlKeyEventData;
    case kVariable:         return TRACKED_NEW proVariableEventData;
    case kFacing:           return TRACKED_NEW proFacingEventData;
    case kContained:        return TRACKED_NEW proContainedEventData;
    case kActivate:         return TRACKED_NEW proActivateEventData;
    case kCallback:         return TRACKED_NEW proCallbackEventData;
    case kResponderState:   return TRACKED_NEW proResponderStateEventData;
    case kMultiStage:       return TRACKED_NEW proMultiStageEventData;
    case kCoop:             return TRACKED_NEW proCoopEventData;
    case kSpawned:          return TRACKED_NEW proSpawnedEventData;
    case kOfferLinkingBook: return TRACKED_NEW proOfferLinkingBookEventData;
    case kBook:             return TRACKED_NEW proBookEventData;
    case kClimbingBlockerHit: return TRACKED_NEW proClimbingBlockerHitEventData;
    }

    return nil;
}

//// proEventData::Read /////////////////////////////////////////////////////
//  Static function on proEventData that reads in a derived event data type
//  from the given stream and returns it.

proEventData* proEventData::Read( hsStream *stream, hsResMgr *mgr )
{
    Int32 evtType = stream->ReadSwap32();

    proEventData* data = ICreateEventDataType(evtType);

    if (data != nil)
        data->IRead(stream, mgr);

    return data;
}

void proEventData::Write(hsStream *stream, hsResMgr *mgr)
{
    stream->WriteSwap32(fEventType);
    IWrite(stream, mgr);
}

enum proEventDataFlags
{
    kProEventDataType,
};

proEventData* proEventData::ReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProEventDataType))
    {
        Int32 evtType = s->ReadSwap32();

        proEventData* data = ICreateEventDataType(evtType);

        if (data != nil)
            data->IReadVersion(s, mgr);

        return data;
    }

    return nil;
}

void proEventData::WriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProEventDataType);
    contentFlags.Write(s);

    // kProEventDataType
    s->WriteSwap32(fEventType);

    IWriteVersion(s, mgr);
}

void proCollisionEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fEnter = stream->ReadBool();
    fHitter = mgr->ReadKey(stream);
    fHittee = mgr->ReadKey(stream);
}

void proCollisionEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteBool(fEnter);
    mgr->WriteKey(stream, fHitter);
    mgr->WriteKey(stream, fHittee);
}

enum proCollisionFlags
{
    kProCollisionEnter,
    kProCollisionHitter,
    kProCollisionHittee,
};

void proCollisionEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProCollisionEnter))
        fEnter = s->ReadBool();
    if (contentFlags.IsBitSet(kProCollisionHitter))
        fHitter = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProCollisionHittee))
        fHittee = mgr->ReadKey(s);
}

void proCollisionEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProCollisionEnter);
    contentFlags.SetBit(kProCollisionHitter);
    contentFlags.SetBit(kProCollisionHittee);
    contentFlags.Write(s);

    // kProCollisionEnter
    s->WriteBool(fEnter);
    // kProCollisionHitter
    mgr->WriteKey(s, fHitter);
    // kProCollisionHittee
    mgr->WriteKey(s, fHittee);
}

void proPickedEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fPicker = mgr->ReadKey(stream);
    fPicked = mgr->ReadKey(stream);
    fEnabled = stream->ReadBool();
    fHitPoint.Read(stream);
}

void proPickedEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, fPicker);
    mgr->WriteKey(stream, fPicked);
    stream->WriteBool(fEnabled);
    fHitPoint.Write(stream);
}

enum ProPickedFlags
{
    kProPickedPicker,
    kProPickedPicked,
    kProPickedEnabled,
    kProPickedHitPoint,
};

void proPickedEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProPickedPicker))
        fPicker = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProPickedPicked))
        fPicked = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProPickedEnabled))
        fEnabled = s->ReadBool();
    if (contentFlags.IsBitSet(kProPickedHitPoint))
        fHitPoint.Read(s);
}

void proPickedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProPickedPicker);
    contentFlags.SetBit(kProPickedPicked);
    contentFlags.SetBit(kProPickedEnabled);
    contentFlags.SetBit(kProPickedHitPoint);
    contentFlags.Write(s);

    // kProPickedPicker
    mgr->WriteKey(s, fPicker);
    // kProPickedPicked
    mgr->WriteKey(s, fPicked);
    // kProPickedEnabled
    s->WriteBool(fEnabled);
    // kProPickedHitPoint
    fHitPoint.Write(s);
}

void proSpawnedEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fSpawner = mgr->ReadKey(stream);
    fSpawnee = mgr->ReadKey(stream);
}

void proSpawnedEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, fSpawner);
    mgr->WriteKey(stream, fSpawnee);
}

enum ProSpawnedFlags
{
    kProSpawnedSpawner,
    kProSpawnedSpawnee,
};

void proSpawnedEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProSpawnedSpawner))
        fSpawner = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProSpawnedSpawnee))
        fSpawnee = mgr->ReadKey(s);
}

void proSpawnedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProSpawnedSpawner);
    contentFlags.SetBit(kProSpawnedSpawnee);
    contentFlags.Write(s);

    // kProSpawnedSpawner
    mgr->WriteKey(s, fSpawner);
    // kProSpawnedSpawnee
    mgr->WriteKey(s, fSpawnee);
}

void proControlKeyEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fControlKey = stream->ReadSwap32();
    fDown = stream->ReadBool();
}
void proControlKeyEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fControlKey);
    stream->WriteBool(fDown);
}

enum ProControlFlags
{
    kProControlKey,
    kProControlDown,
};

void proControlKeyEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProControlKey))
        fControlKey = s->ReadSwap32();
    if (contentFlags.IsBitSet(kProControlDown))
        fDown = s->ReadBool();
}
void proControlKeyEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProControlKey);
    contentFlags.SetBit(kProControlDown);
    contentFlags.Write(s);

    // kProControlKey
    s->WriteSwap32(fControlKey);
    // kProControlDown
    s->WriteBool(fDown);
}


void proVariableEventData::IInit()
{
    fName = nil;
}
void proVariableEventData::IDestruct()
{
    if ( fName != nil )
        delete [] fName;
    fName = nil;
}

void proVariableEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fName = stream->ReadSafeString();
    fDataType = stream->ReadSwap32();
    fNumber = stream->ReadSwapScalar();
    fKey = mgr->ReadKey(stream);
}

void proVariableEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSafeString(fName);
    stream->WriteSwap32(fDataType);
    stream->WriteSwapScalar(fNumber);
    mgr->WriteKey(stream, fKey);
}

enum ProVariableFlags
{
    kProVariableName,
    kProVariableDataType,
    kProVariableNumber,
    kProVariableKey,
};

void proVariableEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProVariableName))
        fName = s->ReadSafeString();
    if (contentFlags.IsBitSet(kProVariableDataType))
        fDataType = s->ReadSwap32();
    if (contentFlags.IsBitSet(kProVariableNumber))
        fNumber = s->ReadSwapScalar();
    if (contentFlags.IsBitSet(kProVariableKey))
        fKey = mgr->ReadKey(s);
}

void proVariableEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProVariableName);
    contentFlags.SetBit(kProVariableDataType);
    contentFlags.SetBit(kProVariableNumber);
    contentFlags.SetBit(kProVariableKey);
    contentFlags.Write(s);

    // kProVariableName
    s->WriteSafeString(fName);
    // kProVariableDataType
    s->WriteSwap32(fDataType);
    // kProVariableNumber
    s->WriteSwapScalar(fNumber);
    // kProVariableKey
    mgr->WriteKey(s, fKey);
}

void proFacingEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fFacer = mgr->ReadKey(stream);
    fFacee = mgr->ReadKey(stream);
    dot = stream->ReadSwapScalar();
    enabled = stream->ReadBool();
}

void proFacingEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, fFacer);
    mgr->WriteKey(stream, fFacee);
    stream->WriteSwapScalar(dot);
    stream->WriteBool(enabled);
}

enum ProFacingFlags
{
    kProFacingFacer,
    kProFacingFacee,
    kProFacingDot,
    kProFacingEnabled,
};

void proFacingEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProFacingFacer))
        fFacer = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProFacingFacee))
        fFacee = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProFacingDot))
        dot = s->ReadSwapScalar();
    if (contentFlags.IsBitSet(kProFacingEnabled))
        enabled = s->ReadBool();
}

void proFacingEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProFacingFacer);
    contentFlags.SetBit(kProFacingFacee);
    contentFlags.SetBit(kProFacingDot);
    contentFlags.SetBit(kProFacingEnabled);
    contentFlags.Write(s);

    // kProFacingFacer
    mgr->WriteKey(s, fFacer);
    // kProFacingFacee  
    mgr->WriteKey(s, fFacee);
    // kProFacingDot    
    s->WriteSwapScalar(dot);
    // kProFacingEnabled
    s->WriteBool(enabled);
}

void proContainedEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fContained = mgr->ReadKey(stream);
    fContainer = mgr->ReadKey(stream);
    fEntering = stream->ReadBool();
}

void proContainedEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, fContained);
    mgr->WriteKey(stream, fContainer);
    stream->WriteBool(fEntering);
}

enum ProContainedFlags
{
    kProContainedContained,
    kProContainedContainer,
    kProContainedEntering,
};

void proContainedEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProContainedContained))
        fContained = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProContainedContainer))
        fContainer = mgr->ReadKey(s);
    if (contentFlags.IsBitSet(kProContainedEntering))
        fEntering = s->ReadBool();
}

void proContainedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProContainedContained);
    contentFlags.SetBit(kProContainedContainer);
    contentFlags.SetBit(kProContainedEntering);
    contentFlags.Write(s);

    // kProContainedContained
    mgr->WriteKey(s, fContained);
    // kProContainedContainer
    mgr->WriteKey(s, fContainer);
    // kProContainedEntering
    s->WriteBool(fEntering);
}

void proActivateEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fActive = stream->ReadBool();
    fActivate = stream->ReadBool();
}

void proActivateEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteBool(fActive);
    stream->WriteBool(fActivate);
}

enum ProActivateFlags
{
    kProActivateActive,
    kProActivateActivate,
};

void proActivateEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProActivateActive))
        fActive = s->ReadBool();
    if (contentFlags.IsBitSet(kProActivateActivate))
        fActivate = s->ReadBool();
}

void proActivateEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProActivateActive);
    contentFlags.SetBit(kProActivateActivate);
    contentFlags.Write(s);
    
    // kProActivateActive
    s->WriteBool(fActive);
    // kProActivateActivate
    s->WriteBool(fActivate);
}

void proCallbackEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fEventType = stream->ReadSwap32();
}

void proCallbackEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fEventType);
}

enum ProCallbackFlags
{
    kProCallbackEventType,
};

void proCallbackEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProCallbackEventType))
        fEventType = s->ReadSwap32();
}

void proCallbackEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProCallbackEventType);
    contentFlags.Write(s);

    // kProCallbackEventType
    s->WriteSwap32(fEventType);
}

void proResponderStateEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fState = stream->ReadSwap32();
}

void proResponderStateEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fState);
}

enum ProResponderFlags
{
    kProResponderState,
};

void proResponderStateEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProResponderState))
        fState = s->ReadSwap32();
}

void proResponderStateEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProResponderState);
    contentFlags.Write(s);

    // kProResponderState
    s->WriteSwap32(fState);
}

void proMultiStageEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fStage = stream->ReadSwap32();
    fEvent = stream->ReadSwap32();
    fAvatar = mgr->ReadKey(stream);
}

void proMultiStageEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fStage);
    stream->WriteSwap32(fEvent);
    mgr->WriteKey(stream, fAvatar);
}

enum ProMultiStageFlags
{
    kProMultiStageStage,
    kProMultiStageEvent,
    kProMultiStageAvatar,
};

void proMultiStageEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProMultiStageStage))
        fStage = s->ReadSwap32();
    if (contentFlags.IsBitSet(kProMultiStageEvent))
        fEvent = s->ReadSwap32();
    if (contentFlags.IsBitSet(kProMultiStageAvatar))
        fAvatar = mgr->ReadKey(s);
}

void proMultiStageEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProMultiStageStage);
    contentFlags.SetBit(kProMultiStageEvent);
    contentFlags.SetBit(kProMultiStageAvatar);
    contentFlags.Write(s);

    // kProMultiStageStage
    s->WriteSwap32(fStage);
    // kProMultiStageEvent
    s->WriteSwap32(fEvent);
    // kProMultiStageAvatar
    mgr->WriteKey(s, fAvatar);
}

void proCoopEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fID = stream->ReadSwap32();
    fSerial = stream->ReadSwap16();
}

void proCoopEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fID);
    stream->WriteSwap16(fSerial);
}

enum ProCoopFlags
{
    kProCoopID,
    kProCoopSerial
};

void proCoopEventData::IReadVersion(hsStream* stream, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(stream);

    if(contentFlags.IsBitSet(kProCoopID))
        fID = stream->ReadSwap32();
    if(contentFlags.IsBitSet(kProCoopSerial))
        fSerial = stream->ReadSwap16();
}

void proCoopEventData::IWriteVersion(hsStream* stream, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProCoopID);
    contentFlags.SetBit(kProCoopSerial);
    contentFlags.Write(stream);

    stream->WriteSwap32(fID);
    stream->WriteSwap16(fSerial);
    
}

void proOfferLinkingBookEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, offerer);
    stream->WriteSwap32(targetAge);
    stream->WriteSwap32(offeree);
}

void proOfferLinkingBookEventData::IRead(hsStream* stream,  hsResMgr* mgr)
{
    offerer = mgr->ReadKey(stream);
    targetAge = stream->ReadSwap32();
    offeree = stream->ReadSwap32();
}

enum ProOfferFlags
{
    kProOfferOfferer,
    kProOfferTargetAge,
    kProOfferOfferee,
};

void proOfferLinkingBookEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProOfferOfferer);
    contentFlags.SetBit(kProOfferTargetAge);
    contentFlags.SetBit(kProOfferOfferee);
    contentFlags.Write(s);

    mgr->WriteKey(s, offerer);
    s->WriteSwap32(targetAge);
    s->WriteSwap32(offeree);
}

void proOfferLinkingBookEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if(contentFlags.IsBitSet(kProOfferOfferer))
        offerer = mgr->ReadKey(s);
    if(contentFlags.IsBitSet(kProOfferTargetAge))
        targetAge = s->ReadSwap32();
    if(contentFlags.IsBitSet(kProOfferOfferee))
        offeree = s->ReadSwap32();
}

void proBookEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    stream->WriteSwap32(fEvent);
    stream->WriteSwap32(fLinkID);
}

void proBookEventData::IRead(hsStream* stream,  hsResMgr* mgr)
{
    fEvent = stream->ReadSwap32();
    fLinkID = stream->ReadSwap32();
}

enum ProBookFlags
{
    kProBookEvent,
    kProBookLinkID,
};

void proBookEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProBookEvent);
    contentFlags.SetBit(kProBookLinkID);
    contentFlags.Write(s);

    s->WriteSwap32( fEvent );
    s->WriteSwap32( fLinkID );
}

void proBookEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if(contentFlags.IsBitSet(kProBookEvent))
        fEvent = s->ReadSwap32();
    if(contentFlags.IsBitSet(kProBookLinkID))
        fLinkID = s->ReadSwap32();
}


void proClimbingBlockerHitEventData::IRead(hsStream* stream, hsResMgr* mgr)
{
    fBlockerKey = mgr->ReadKey(stream);
}

void proClimbingBlockerHitEventData::IWrite(hsStream* stream, hsResMgr* mgr)
{
    mgr->WriteKey(stream, fBlockerKey);
}

enum proClimbingBlockerHitFlags
{
    
    kProClimbingBlockerKey,
};

void proClimbingBlockerHitEventData::IReadVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.Read(s);

    if (contentFlags.IsBitSet(kProClimbingBlockerKey))
        fBlockerKey = mgr->ReadKey(s);;
}

void proClimbingBlockerHitEventData::IWriteVersion(hsStream* s, hsResMgr* mgr)
{
    hsBitVector contentFlags;
    contentFlags.SetBit(kProClimbingBlockerKey);
    contentFlags.Write(s);

    // kProClimbingBlockerKey
    mgr->WriteKey(s, fBlockerKey);
}