/*==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==*/
#ifndef plNetMessage_h_inc
#define plNetMessage_h_inc

#include "hsUtils.h"
#include "hsTypes.h"
#include "hsStlUtils.h"
#include "hsStream.h"
#include "hsBitVector.h"
#include "hsTemplates.h"
#include "plGeneric.h"
#include "pnNetCommon/plNetServers.h"
#include "pnNetCommon/plNetGroup.h"
#include "pnFactory/plCreatable.h"
#include "pnFactory/plFactory.h"
#include "plUnifiedTime/plClientUnifiedTime.h"
#include "plNetCommon/plNetServerSessionInfo.h"
#include "plNetCommon/plNetCommon.h"
#include "plNetCommon/plNetCommonHelpers.h"
#include "plNetCommon/plNetCommonConstants.h"

#include "plStreamLogger/plStreamLogger.h"

#include "plNetMsgHelpers.h"

#include "pnNetBase/pnNetBase.h"

#include <sstream>

class plMessage;
class plUUID;


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

//
// Base class for application network messages.
// These become the data in a plNetCommonMessage when sent over the network.
//
class plNetCommonMessage;
class plKey;

class plNetMessage : public plCreatable
{   
    friend class plNetServerMsgPlsRoutableMsg;

    UInt32 fFlags;      // indicates what is present in the message, always transmitted 
    plUnifiedTime fTimeSent;    // time msg was sent (in sender's unified timeSpace), always transmitted to and from the client
    double fTimeRecvd;      // time msg was recvd (in rcvrs timeSpace), never transmitted 
    UInt32 fBytesRead;      // amount of data we've already read, never transmitted 
    UInt32 fContext;        // set by sender, included in reply. Only written if kHasContext flag set
    UInt32 fTransactionID;  // set by originator, included in reply. Only written if kHasTransactionID flag set
    UInt32 fPlayerID;       // set by originator. Only written if kHasPlayerID flag set
    plUUID fAcctUUID;       // set by sender (app level). Only written if kHasAcctUUID flag set
    const plNetCommonMessage* fNetCoreMsg;  // not sent, set by the receiver
    UInt32 fPeekStatus;     // not sent. set on PeekBuffer, cleared on PokeBuffer
    UInt8   fProtocolVerMajor;  // conditionally sent
    UInt8   fProtocolVerMinor;  // conditionally sent
    ENetProtocol fNetProtocol;  // the server this msg should be sent to. this value is not sent over wire.

    enum ContentFlags
    {
        kNetMsgFlags,
        kNetMsgTimeSent,
        kNetMsgContext,
        kNetMsgTransactionID,
        kNetMsgPlayerID,
        kNetMsgVersion,
    };

protected:  
    virtual int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    virtual int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);

    void IWriteClassIndex(hsStream* stream);
    void IReadClassIndex(hsStream* stream);

    bool IPeeked() const { return fPeekStatus==kFullyPeeked;}
    void ISetPeekStatus(UInt32 s) { fPeekStatus=s;  }

public:
    typedef UInt16 plStrLen;
    static const UInt8 kVerMajor, kVerMinor;    // version of the networking code

    typedef UInt16 ClassIndexType;      // the type returned by plCreatable::ClassIndex()
    enum
    {
        kMaxNameLen=32
    };
    enum BitVectorFlags     // indicates what is present in the message, always transmitted
    {
        kHasTimeSent        = 0x1,      // means fTimeSent need sending
        kHasGameMsgRcvrs    = 0x2,      // means that this is a direct (not bcast) game msg
        kEchoBackToSender   = 0x4,      // if broadcasting, echo packet back to sender
        kRequestP2P         = 0x8,      // sent to gameServer on joinReq
        kAllowTimeOut       = 0x10,     // sent to gameServer on joinReq (if release code)
        kIndirectMember     = 0x20,     // sent to client on joinAck if he is behind a firewall
        // This flag is used when the servers are firewalled and NAT'ed
        // It tells a game or lobby server to ask the lookup for an external address
        kPublicIPClient     = 0x40,     // set on a client coming from a public IP
        kHasContext         = 0x80,     // whether or not to write fContext field
        // Used with StartProcess server msgs. Whether or not the ServerAgent
        // must first ask the vault for a game state associated with the
        // game about to be instanced.
        kAskVaultForGameState   =0x100,
        kHasTransactionID   = 0x200,    // whether or not to write fTransactionID field
        kNewSDLState        = 0x400,    // set by client on first state packet sent, may not be necessary anymore
        kInitialAgeStateRequest = 0x800,// initial request for the age state
        kHasPlayerID        = 0x1000,   // is fPlayerID set
        kUseRelevanceRegions= 0x2000,   // if players use relevance regions are used, this will be filtered by region, currently set on avatar physics and control msgs
        kHasAcctUUID        = 0x4000,   // is fAcctUUID set
        kInterAgeRouting    = 0x8000,   // give to pls for routing.
        kHasVersion         = 0x10000,  // if version is set
        kIsSystemMessage    = 0x20000,
        kNeedsReliableSend  = 0x40000,
        kRouteToAllPlayers  = 0x80000,  // send this message to all online players.
    };
    enum PeekOptions        // options for partial peeking
    {
        kSkipStream         = 1<<0, // means we should not read the actual stream, just the stream info
        kBaseClassOnly      = 1<<1, // only peek or poke the baseclass info
        kPartialPeekMask    = kSkipStream | kBaseClassOnly,
        // plNetServerMsgWithRoutingInfo derived classes need to check this
        // in their PeekBuffer methods.
        kJustRoutingInfo    = 1<<2,
        kDontClearBuffer    = 1<<3,
        // don't call base class peek/poke. plNetMsgOmnibus/plNetMsgVault uses this.
        kDEAD_NoBaseClass   = 1<<4,
        // don't worry about compressing/uncompressing things. used by plNetMsgStreamHelper, and plNetMsgOmnibus.
        kDontCompress       = 1<<5,
        kWantVersion        = 1<<6,
        kFullyPeeked        = 0xffffffff    // default fPeekStatus, set if not partial peeking
    };
    enum CompressionType    // currently only used for plNetMsgStreams
    {
        kCompressionNone,       // not compressed
        kCompressionFailed,     // failed to compress
        kCompressionZlib,       // zlib compressed
        kCompressionDont        // don't compress
    };

    CLASSNAME_REGISTER( plNetMessage );
    GETINTERFACE_ANY( plNetMessage, plCreatable );

    // plCreatable
    void Read(hsStream* s, hsResMgr* mgr);
    void Write(hsStream* s, hsResMgr* mgr);

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    // ctor
    plNetMessage();
    virtual ~plNetMessage();

    static plNetMessage* CreateAndRead(const plNetCommonMessage*, plStreamLogger::EventList* el = nil);
    static plNetMessage* Create(const plNetCommonMessage*);
    int PokeBuffer(char* buf, int bufLen, UInt32 peekOptions=0);            // put msg in buffer
    int PeekBuffer(const char* buf, int bufLen, UInt32 peekOptions=0, bool forcePeek=false, plStreamLogger::EventList* el = nil);   // get msg out of buffer
    bool NeedsReliableSend() const { return IsBitSet(kNeedsReliableSend); }
    bool IsSystemMessage() const { return IsBitSet(kIsSystemMessage);   }
    virtual void ValidatePoke() const;
    virtual void ValidatePeek() const;
    virtual bool NeedsBroadcast() const { return false; }           // should game server broadcast this message to other clients?
    virtual int ValidationSchemeID() const { return 1; }

    // getters
    int GetPackSize();
    const plUnifiedTime& GetTimeSent() const { return fTimeSent; }
    bool GetHasTimeSent() const { return IsBitSet(kHasTimeSent); }
    double GetTimeReceived() const { return fTimeRecvd; }
    bool IsBitSet(int b) const { return (fFlags & b) != 0; }
    const plNetCommonMessage* GetNetCoreMsg() const { return fNetCoreMsg; }
    UInt32 GetNetCoreMsgLen() const;
    bool GetHasContext() const { return IsBitSet(kHasContext);}
    UInt32 GetContext() const { return fContext;}
    bool GetHasTransactionID() const { return IsBitSet(kHasTransactionID);}
    UInt32 GetTransactionID() const { return fTransactionID;}
    bool GetHasPlayerID() const { return IsBitSet(kHasPlayerID);}
    UInt32 GetPlayerID() const { hsAssert(GetHasPlayerID(), "uninit playerID"); return fPlayerID; }
    UInt32 JustGetPlayerID() const { return fPlayerID; }
    bool GetHasAcctUUID() const { return IsBitSet(kHasAcctUUID); }
    const plUUID * GetAcctUUID() const { return &fAcctUUID; }
    UInt8 GetVersionMajor() const { return fProtocolVerMajor;   }
    UInt8 GetVersionMinor() const { return fProtocolVerMinor;   }
    ENetProtocol GetNetProtocol () const { return fNetProtocol; }

    // setters
    void SetTimeSent(const plUnifiedTime& t) { fTimeSent=t;SetHasTimeSent(true); }
    void SetHasTimeSent(bool value) { SetBit( kHasTimeSent, value ); }
    void SetTimeReceived(double t) { fTimeRecvd=t; }
    void SetBit(int b, bool on=true) { if (on) fFlags |= b; else fFlags &= ~b; }
    void SetNetCoreMsg(const plNetCommonMessage* ncmsg) { fNetCoreMsg=ncmsg; }
    void SetHasContext(bool value) { SetBit(kHasContext,value);}
    void SetContext(UInt32 value) { fContext=value; SetHasContext(true);}
    void SetHasTransactionID(bool value) { SetBit(kHasTransactionID,value);}
    void SetTransactionID(UInt32 value) { fTransactionID=value; SetHasTransactionID(true);}
    void SetHasPlayerID(bool value) { SetBit(kHasPlayerID,value);}
    void SetPlayerID(UInt32 value) { fPlayerID=value; SetHasPlayerID(true);}
    void SetHasAcctUUID( bool v ) { SetBit( kHasAcctUUID,v ); }
    void SetAcctUUID(const plUUID * v ) { fAcctUUID.CopyFrom(v); SetHasAcctUUID(true); }
    void SetVersion(UInt8 maj=kVerMajor, UInt8 min=kVerMinor) { SetBit(kHasVersion); fProtocolVerMajor=maj; fProtocolVerMinor=min;  }
    void SetNetProtocol (ENetProtocol v ) { fNetProtocol = v; }

    // init fContext, fTransactionID, etc. if needed.
    void InitReplyFieldsFrom(plNetMessage * msg);

    // debug
    virtual std::string AsStdString() const
    {
        char * delim = "";

        std::stringstream ss;
        if ( GetHasPlayerID() )
        {
            ss << delim << "p:" << GetPlayerID();
            delim = ",";
        }
        if ( GetHasTransactionID() )
        {
            ss << delim << "x:" << GetTransactionID();
            delim = ",";
        }
        if ( GetHasAcctUUID() )
        {
            ss << delim << "a:" << GetAcctUUID()->AsStdString();
            delim = ",";
        }
        if ( IsBitSet(kHasVersion) )
        {
            ss << delim << "v:" << (int)fProtocolVerMajor << "." << (int)fProtocolVerMinor;
            delim = ",";
        }

        return ss.str().c_str();
    }
};

// 
// for msgs which only go from the gameserver to the client
//
class plNetMsgServerToClient : public plNetMessage
{
public:
    plNetMsgServerToClient() { SetBit(kIsSystemMessage|kNeedsReliableSend); }

    CLASSNAME_REGISTER( plNetMsgServerToClient );
    GETINTERFACE_ANY( plNetMsgServerToClient, plNetMessage );
};

////////////////////////////////////////////////////////////////////
// Stream msg - abstract base class built with plNetMsgStreamHelper
//
class plNetMsgStream : public plNetMessage
{
protected:
    plNetMsgStreamHelper fStreamHelper;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    CLASSNAME_REGISTER( plNetMsgStream );
    GETINTERFACE_ANY_AUX(plNetMsgStream,plNetMessage,plNetMsgStreamHelper,fStreamHelper)

    plNetMsgStreamHelper* StreamInfo() { return &fStreamHelper; }
    const plNetMsgStreamHelper* StreamInfo() const { return &fStreamHelper; }
};

//
// Object info msg - abstract base class built with plNetMsgObjectHelper
//
class plNetMsgObject : public plNetMessage
{
private:
    enum ContentFlags
    {
        kNetMsgObjectHelper,
    };
protected:
    plNetMsgObjectHelper fObjectHelper;
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    CLASSNAME_REGISTER( plNetMsgObject );
    GETINTERFACE_ANY_AUX(plNetMsgObject,plNetMessage,plNetMsgObjectHelper,fObjectHelper)

    plNetMsgObjectHelper* ObjectInfo() { return &fObjectHelper; }
    const plNetMsgObjectHelper* ObjectInfo() const { return &fObjectHelper; }

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    // debug
    std::string AsStdString() const
    {
        std::string s;
        char tmp[256];
        xtl::format(s,"object=%s, %s",fObjectHelper.GetUoid().StringIze(tmp), plNetMessage::AsStdString().c_str());
        return s;
    }

};

//
// abstract baseclass which has both object and stream info
//
class plNetMsgStreamedObject : public plNetMsgObject
{
private:
    enum ContentFlags
    {
        kStreamHelper
    };
protected:
    plNetMsgStreamHelper fStreamHelper;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgStreamedObject() {}
    ~plNetMsgStreamedObject() {}

    CLASSNAME_REGISTER( plNetMsgStreamedObject );
    GETINTERFACE_ANY_AUX(plNetMsgStreamedObject,plNetMsgObject,plNetMsgStreamHelper,fStreamHelper)

    plNetMsgStreamHelper* StreamInfo() { return &fStreamHelper; }
    const plNetMsgStreamHelper* StreamInfo() const { return &fStreamHelper; }

    //virtuals
    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

};

//
// New SaveState system
//
class plNetMsgSDLState : public plNetMsgStreamedObject
{
private:
    enum ContentFlags
    {
        kSDLStateStream,
        kSDLIsInitialState,
        kSDLPersist,
        kSDLAvatarState,
    };

    void ISetDescName() const;
    bool    fIsInitialState;

protected:
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);

    bool fPersistOnServer;
    bool fIsAvatarState;
    mutable std::string fDescName;      // for debugging output only, not read/written
public:
    CLASSNAME_REGISTER( plNetMsgSDLState );
    GETINTERFACE_ANY(plNetMsgSDLState, plNetMsgStreamedObject);

    plNetMsgSDLState() : fIsInitialState(0), fPersistOnServer(true), fIsAvatarState(false) { SetBit(kNeedsReliableSend); }

    bool PersistOnServer() const { return fPersistOnServer != 0; }
    void SetPersistOnServer(bool b) { fPersistOnServer = b; }

    bool IsAvatarState() const { return fIsAvatarState != 0; }
    void SetIsAvatarState(bool b) { fIsAvatarState = b; }
    
    // debug
    std::string AsStdString() const;
    bool IsInitialState() const {return fIsInitialState!=0; }
    void SetIsInitialState( bool v ) { fIsInitialState=v; }

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);
};

//
// New SaveState system
//
class plNetMsgSDLStateBCast : public plNetMsgSDLState
{
protected:  
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public: 
    CLASSNAME_REGISTER( plNetMsgSDLStateBCast );
    GETINTERFACE_ANY(plNetMsgSDLStateBCast, plNetMsgSDLState);

    // virtuals 
    bool NeedsBroadcast() const { return true; }

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);
};

//
//  Object state request msg
//
class plNetMsgObjStateRequest : public plNetMsgObject
{
public:
    plNetMsgObjStateRequest() { SetBit(kIsSystemMessage|kNeedsReliableSend);    }

    CLASSNAME_REGISTER( plNetMsgObjStateRequest );
    GETINTERFACE_ANY( plNetMsgObjStateRequest, plNetMsgObject );
};


//
// abstract message class which contains a list of rooms
//
class plNetMsgRoomsList : public plNetMessage
{
protected:
    std::vector<plLocation> fRooms; 
    std::vector<char*> fRoomNames;      // for debug usage only

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgRoomsList() {}
    ~plNetMsgRoomsList();

    CLASSNAME_REGISTER( plNetMsgRoomsList );
    GETINTERFACE_ANY( plNetMsgRoomsList, plNetMessage );

    void AddRoom(plKey rmKey);
    void AddRoomLocation(plLocation loc, const char* rmName);
    int FindRoomLocation(plLocation loc);

    int GetNumRooms() const { return fRooms.size(); }
    plLocation GetRoomLoc(int i) const { return fRooms[i]; }
    const char* GetRoomName(int i) const { return fRoomNames[i]; }      // debug
};

//
// Game msg - wraps a plMessage.
//
class hsResMgr;

class plNetMsgGameMessage: public plNetMsgStream
{
protected:
    plClientUnifiedTime fDeliveryTime;  // for future timestamping

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgGameMessage() { SetBit(kNeedsReliableSend);     }

    CLASSNAME_REGISTER( plNetMsgGameMessage );
    GETINTERFACE_ANY( plNetMsgGameMessage, plNetMsgStream );
    enum ContentsFlags
    {
        kNetGameMsgDeliveryTime,
        kNetGameMsgGameMsg,
    };
    plClientUnifiedTime& GetDeliveryTime() { return fDeliveryTime; }
    void SetDeliveryTime(plClientUnifiedTime& ut) { fDeliveryTime=ut; }

    plMessage* GetContainedMsg(hsResMgr* resmgr = nil);

    // virtuals
    bool NeedsBroadcast() const { return true; }
    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    // debug
    std::string AsStdString() const
    {
        std::string s;
        const char* noc=plFactory::GetTheFactory()->GetNameOfClass(StreamInfo()->GetStreamType());
        xtl::format(s,"%s %s",plNetMsgStream::AsStdString().c_str(), noc ? noc : "?");
        return s;
    }
};

//
// special game msg for loading clones/avatars
//
class plNetMsgLoadClone : public plNetMsgGameMessage
{
private:
    enum ContentFlags
    {
        kObjectHelper,
        kIsPlayer,
        kIsLoading,
        kIsInitialState,
    };
protected:
    bool fIsPlayer;
    bool fIsLoading;
    bool fIsInitialState;
    plNetMsgObjectHelper fObjectHelper;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);    
public:

    CLASSNAME_REGISTER( plNetMsgLoadClone );
    GETINTERFACE_ANY_AUX(plNetMsgLoadClone, plNetMsgGameMessage,plNetMsgObjectHelper,fObjectHelper)

    plNetMsgLoadClone() : fIsPlayer(true),fIsLoading(true),fIsInitialState(false) {}

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    plNetMsgObjectHelper* ObjectInfo() { return &fObjectHelper; }
    const plNetMsgObjectHelper* ObjectInfo() const { return &fObjectHelper; }

    bool GetIsPlayer() const    { return fIsPlayer!=0;  }
    bool GetIsLoading() const   { return fIsLoading!=0; }
    bool GetIsInitialState() const { return fIsInitialState!=0; }

    void SetIsPlayer(bool p)    { fIsPlayer=p;  }
    void SetIsLoading(bool p)   { fIsLoading=p; }
    void SetIsInitialState(bool p) {fIsInitialState=p; }


    // debug
    std::string AsStdString() const
    {
        std::string s;
        char tmp[256];
        xtl::format(s,"object=%s initial=%d, %s",fObjectHelper.GetUoid().StringIze(tmp), fIsInitialState,
            plNetMsgGameMessage::AsStdString().c_str());
        return s;
    }
};

//
// special msg when a player is paged in/out
//
class plNetMsgPlayerPage : public plNetMessage
{
protected:
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);    
public:
    plUoid  fUoid;
    bool fUnload;

    plNetMsgPlayerPage() : fUnload(false) { SetBit(kNeedsReliableSend); }
    plNetMsgPlayerPage(plUoid uoid, hsBool unload) : fUoid(uoid),   fUnload(unload) { }

    CLASSNAME_REGISTER( plNetMsgPlayerPage );
    GETINTERFACE_ANY( plNetMsgPlayerPage, plNetMessage);
};

//
// a game message that takes a list of other clients as receivers
//
class plNetMsgGameMessageDirected : public plNetMsgGameMessage
{
private:
    enum ContentFlags
    {
        kRecievers,
    };
protected:
    plNetMsgReceiversListHelper fReceivers;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);    
public:
    CLASSNAME_REGISTER( plNetMsgGameMessageDirected );
    GETINTERFACE_ANY_AUX(plNetMsgGameMessageDirected,plNetMsgGameMessage,
        plNetMsgReceiversListHelper,fReceivers)

    plNetMsgReceiversListHelper* Receivers() { return &fReceivers; }    

    // virtuals
    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);
};

//
// Message when a room is paged in/out
//
class plNetMsgPagingRoom : public plNetMsgRoomsList
{
public:
    enum PageFlags
    {
        kPagingOut=0x1,     // else paging in
        kResetList=0x2,     // server should reset his existing list before using this msg
        kRequestState=0x4,  // also want current room state sent to me
        kFinalRoomInAge=0x8 // done paging in the age
    };
protected:
    UInt8 fPageFlags;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgPagingRoom() : fPageFlags(0) { SetBit(kIsSystemMessage|kNeedsReliableSend); }
    ~plNetMsgPagingRoom() {}

    CLASSNAME_REGISTER( plNetMsgPagingRoom );
    GETINTERFACE_ANY( plNetMsgPagingRoom, plNetMsgRoomsList );

    void SetPageFlags(UInt8 f) { fPageFlags=f; }
    UInt8 GetPageFlags() const { return fPageFlags; }
    
    void SetPagingOut(hsBool b) { if (b) fPageFlags |= kPagingOut; else fPageFlags&=~kPagingOut; }
    hsBool GetPagingOut() const { return (fPageFlags & kPagingOut) != 0; }

    void SetResetList(hsBool b) { if (b) fPageFlags |= kResetList; else fPageFlags &=~kResetList; }
    hsBool GetResetList() const { return (fPageFlags & kResetList) != 0; }

    void SetRequestingState(hsBool b) { if (b) fPageFlags |= kRequestState; else fPageFlags &=~kRequestState; }
    hsBool GetRequestingState() const { return (fPageFlags & kRequestState) != 0; } 

    // debug
    std::string AsStdString() const
    {
        std::string s;
        xtl::format(s,"pageFlags:%02X, paging %s, requestingState:%s, resetting=%d",fPageFlags,
            (fPageFlags&kPagingOut)?"out":"in", (fPageFlags&kRequestState)?"yes":"no",
            (fPageFlags & kResetList)!=0);
        return s;
    }
};

//
// Client requests game state update by rooms.
// an empty rooms list means ALL rooms I have loaded.
//
class plNetMsgGameStateRequest : public plNetMsgRoomsList
{
public:
    plNetMsgGameStateRequest() { SetBit(kIsSystemMessage|kNeedsReliableSend); }

    CLASSNAME_REGISTER( plNetMsgGameStateRequest );
    GETINTERFACE_ANY( plNetMsgGameStateRequest, plNetMsgRoomsList );    
};

//
// sent by game server to change clients ownership of a netGroup
//
class plNetMsgGroupOwner: public plNetMsgServerToClient
{
public:
    class GroupInfo
    {
    public:
        plNetGroupId fGroupID;
        bool fOwnIt;    // else not the owner

        void Read(hsStream* s) { fGroupID.Read(s); s->LogReadSwap(&fOwnIt,"GroupOwner OwnIt"); }
        void Write(hsStream* s) { fGroupID.Write(s); s->WriteSwap(fOwnIt); }

      GroupInfo() : fGroupID(plNetGroup::kNetGroupUnknown), fOwnIt(false) {}
        GroupInfo(plNetGroupId gID, hsBool o) : fGroupID(gID),fOwnIt(o) {}
    };
protected:
    std::vector<GroupInfo> fGroups; 

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    CLASSNAME_REGISTER( plNetMsgGroupOwner );
    GETINTERFACE_ANY( plNetMsgGroupOwner, plNetMsgServerToClient );

    // getters
    int GetNumGroups() const { return fGroups.size(); }
    GroupInfo GetGroupInfo(int i) const { return fGroups[i]; }

    // setters
    void AddGroupInfo(GroupInfo gi) { fGroups.push_back(gi); }
    void ClearGroupInfo() { fGroups.clear(); }

    bool IsOwner() { return fGroups[0].fOwnIt; }
};

//
// voice recording buffer
//
class plNetMsgVoice: public plNetMessage
{
private:
    enum ContentFlags
    {
        kDead_FrameSize,
        kReceivers,
        kVoiceFlags,
        kVoiceData
    };
protected:
    UInt8 fFlags;       // voice flags
    UInt8 fNumFrames; // number of frames encoded
    std::string fVoiceData;

    plNetMsgReceiversListHelper fReceivers;
 
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgVoice(): fFlags(0), fNumFrames(0) {  }
    ~plNetMsgVoice() {}

    CLASSNAME_REGISTER( plNetMsgVoice );
    GETINTERFACE_ANY_AUX(plNetMsgVoice,plNetMessage,plNetMsgReceiversListHelper,fReceivers)
        
    void SetFlag(int f) { fFlags |= f; }
    int GetFlags() { return fFlags;  }
    
    void SetNumFrames(UInt8 f) { fNumFrames = f; }
    UInt8 GetNumFrames() const { return fNumFrames; }
    
    void SetVoiceData(char *data, int len );
    int GetVoiceDataLen() const { return fVoiceData.length(); }
    const char *GetVoiceData() const;
    
    plNetMsgReceiversListHelper* Receivers() { return &fReceivers; }

    // virtuals
    bool NeedsBroadcast() const { return true; }

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    // debug
    std::string AsStdString() const
    {
        std::string s;
        xtl::format(s,"len=%d",fVoiceData.size());
        return s;
    }
};

//
// base class for dealing with plNetSharedState
//
class plNetSharedState;
class plNetMsgSharedState : public plNetMsgStreamedObject
{
private:
    enum ContentFlags
    {
        kLockRequest,
    };
protected:
    bool fLockRequest;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgSharedState() : fLockRequest(false) {}
    ~plNetMsgSharedState() {}

    CLASSNAME_REGISTER( plNetMsgSharedState );
    GETINTERFACE_ANY(plNetMsgSharedState, plNetMsgStreamedObject);

    void CopySharedState(plNetSharedState* ss);

    void SetLockRequest(hsBool b) { fLockRequest=b; }
    hsBool GetLockRequest() const { return fLockRequest; }  

    void ReadVersion(hsStream* s, hsResMgr* mgr);
    void WriteVersion(hsStream* s, hsResMgr* mgr);

    // debug
    std::string AsStdString() const
    {
        std::string s;
        xtl::format(s,"lockReq=%d, %s",fLockRequest, plNetMsgStreamedObject::AsStdString().c_str());
        return s;
    }
};

//
// attempt to lock/unlock and set generic shared state on server.
// lock attempts will generate server reply messages confirming or denying the action.
//
class plNetMsgTestAndSet : public plNetMsgSharedState
{
public:
    plNetMsgTestAndSet() { SetBit(kNeedsReliableSend); }

    CLASSNAME_REGISTER( plNetMsgTestAndSet );
    GETINTERFACE_ANY(plNetMsgTestAndSet, plNetMsgSharedState);
};

//
// provides a way to query sharedState on the server
//
class plNetMsgGetSharedState : public plNetMsgObject
{
protected:
    char fSharedStateName[kMaxNameLen];

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgGetSharedState()  { *fSharedStateName=0; SetBit(kNeedsReliableSend); }
    ~plNetMsgGetSharedState() {}

    CLASSNAME_REGISTER( plNetMsgGetSharedState );
    GETINTERFACE_ANY( plNetMsgGetSharedState, plNetMsgObject );

    void SetSharedStateName(char* n) { if (n) hsStrncpy(fSharedStateName, n, kMaxNameLen); }
    char* GetSharedStateName() { return fSharedStateName; }
};

//
// msg which sets the update frequency for a group of objects on the server
//
class plNetMsgObjectUpdateFilter : public plNetMessage
{
protected:
    plNetMsgObjectListHelper fObjectListHelper;
    float fMaxUpdateFreq;   // in secs

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgObjectUpdateFilter() : fMaxUpdateFreq(-1) {}
    ~plNetMsgObjectUpdateFilter() {}

    CLASSNAME_REGISTER( plNetMsgObjectUpdateFilter );
    GETINTERFACE_ANY_AUX(plNetMsgObjectUpdateFilter,plNetMessage,plNetMsgObjectListHelper,fObjectListHelper)

    plNetMsgObjectListHelper* ObjectListInfo() { return &fObjectListHelper; }

    void SetMaxUpdateFreq(float f) { fMaxUpdateFreq=f; }
    float GetMaxUpdateFreq() const { return fMaxUpdateFreq; }
};

//
// Client wants a list of all members in the session
//
class plNetMsgMembersListReq : public plNetMessage
{
public:
    plNetMsgMembersListReq() { SetBit(kIsSystemMessage|kNeedsReliableSend); }

    CLASSNAME_REGISTER( plNetMsgMembersListReq );
    GETINTERFACE_ANY( plNetMsgMembersListReq, plNetMessage );
};

//
// Server returns a list of all members in the session
//
class plNetMsgMembersList : public plNetMsgServerToClient
{
protected:
    plNetMsgMemberListHelper fMemberListHelper;
protected:
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    CLASSNAME_REGISTER( plNetMsgMembersList );
    GETINTERFACE_ANY_AUX(plNetMsgMembersList,plNetMsgServerToClient,plNetMsgMemberListHelper,fMemberListHelper)

    plNetMsgMemberListHelper* MemberListInfo() { return &fMemberListHelper; }
};

//
// server tells client to add or remove a session member
//
class plNetMsgMemberUpdate : public plNetMsgServerToClient
{
protected:
    plNetMsgMemberInfoHelper fMemberInfo;
    bool fAddMember;        // else remove member
protected:
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    CLASSNAME_REGISTER( plNetMsgMemberUpdate );
    GETINTERFACE_ANY_AUX(plNetMsgMemberUpdate,plNetMsgServerToClient,plNetMsgMemberInfoHelper,fMemberInfo)

    bool AddingMember() { return fAddMember; }
    void SetAddingMember(bool b) { fAddMember=b; }

    plNetMsgMemberInfoHelper* MemberInfo() { return &fMemberInfo; }
    bool NeedsBroadcast() const { return true; }    // send to all clients
};


//
// ListenList updater msgs.  For voice-broadcasting purposes.
// Contains a list of other players which I am [not] listening to.
// Sent client-client or client-server.
//
class plNetMsgListenListUpdate : public plNetMessage
{
private:
    plNetMsgReceiversListHelper fReceivers;     // used by server, the players we're listening to 
    bool fAdding;                           // else removing

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgListenListUpdate() : fAdding(false) {}
    ~plNetMsgListenListUpdate() {}

    CLASSNAME_REGISTER( plNetMsgListenListUpdate );
    GETINTERFACE_ANY_AUX(plNetMsgListenListUpdate,plNetMessage,plNetMsgReceiversListHelper,fReceivers)
    
    plNetMsgReceiversListHelper* Receivers() { return &fReceivers; }
    
    bool GetAdding() const { return fAdding;    }
    void SetAdding(bool a) { fAdding=a; }
            
    // virtuals
    bool NeedsBroadcast() const { return true; }        // use rcvrs list
};


///////////////////////////////////////////////////////////////////
class plNetMsgInitialAgeStateSent : public plNetMsgServerToClient
{
    UInt32  fNumInitialSDLStates;
    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgInitialAgeStateSent():fNumInitialSDLStates(0){}
    CLASSNAME_REGISTER( plNetMsgInitialAgeStateSent );
    GETINTERFACE_ANY( plNetMsgInitialAgeStateSent, plNetMsgServerToClient);
    void SetNumInitialSDLStates( UInt32 n ) { fNumInitialSDLStates=n; }
    UInt32 GetNumInitialSDLStates() const { return fNumInitialSDLStates; }
};

//
// msg which sets the update frequency for a group of objects on the server
//
class plNetMsgRelevanceRegions : public plNetMessage
{
protected:
    hsBitVector fRegionsImIn;
    hsBitVector fRegionsICareAbout;

    int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0);
    int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0);
public:
    plNetMsgRelevanceRegions() { SetBit(kNeedsReliableSend); }
    ~plNetMsgRelevanceRegions() {}

    CLASSNAME_REGISTER( plNetMsgRelevanceRegions );
    GETINTERFACE_ANY(plNetMsgRelevanceRegions, plNetMessage)

    void SetRegionsICareAbout(const hsBitVector& r) { fRegionsICareAbout=r; }
    void SetRegionsImIn(const hsBitVector& r)       { fRegionsImIn=r; }

    const hsBitVector& GetRegionsICareAbout() const { return fRegionsICareAbout;    }
    const hsBitVector& GetRegionsImIn() const       { return fRegionsImIn;  }

    std::string AsStdString() const
    {
        std::string s;
        std::string b1, b2;
        int i;
        for(i=0;i<fRegionsImIn.GetNumBitVectors(); i++)
            b1 += xtl::format("0x%x ", fRegionsImIn.GetBitVector(i)).c_str();
        for(i=0;i<fRegionsICareAbout.GetNumBitVectors(); i++)
            b2 += xtl::format("0x%x ", fRegionsICareAbout.GetBitVector(i)).c_str();
        xtl::format( s, "rgnsImIn:%s, rgnsICareAbout:%s, %s",
            b1.c_str(), b2.c_str(), plNetMessage::AsStdString().c_str() );
        return s;
    }
};

#endif  // plNetMessage_h_inc
////////////////////////////////////////////////////////////////////
// End.