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

#include "hsConfig.h"
#include "hsUtils.h"
#include "hsStlUtils.h"

#include "plNetClientGroup.h"
#include "plNetVoiceList.h"
#include "plNetClientMsgHandler.h"
#include "plNetClientStats.h"       // STATS Counters

#include "pnNetCommon/plNetApp.h"

#include "plNetTransport/plNetTransport.h"
#include "plEncryption/plChecksum.h"
#include "plNetCommon/plNetServerSessionInfo.h"
#include "plNetClientComm/plNetClientComm.h"
#include "plUnifiedTime/plUnifiedTime.h"
#pragma warning(disable: 4284)

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

class plUoid;
class hsStream;
class plKey;
class plNetMessage;
class plSynchedObject;
struct DistSqInfo;
class plStatusLog;
class plOperationProgress;
class plIDataServer;
class plPlate;
class plLoadCloneMsg;
class plPlayerPageMsg;
class plNetClientRecorder;
class plVaultPlayerNode;
class plVaultAgeNode;
class plNetVoiceListMsg;
class plStateDataRecord;
class plCCRPetitionMsg;
class plNetMsgPagingRoom;


struct plNetClientCommMsgHandler : plNetClientComm::MsgHandler {
    int HandleMessage( plNetMessage* msg ); 
};

class plNetClientMgr : public plNetClientApp
{
private:
    typedef std::vector<plKey> plKeyVec;
public:

    enum NetChannels
    {
        kNetChanDefault,
        kNetChanVoice,
        kNetChanListenListUpdate,
        kNetChanDirectedMsg,
        kNetNumChannels
    };

    enum DirectedSendFlags
    {
        kInterAgeMsg = 0x1
    };
    
    enum ListenListMode
    {
        kListenList_Distance    = 0,
        kListenList_Forced,
        kListenList_End,
    };

    enum RefContext
    {
        kVaultImage = 0,
        kAgeSDLHook = 1
    };

    struct PendingLoad
    {
        // must be set by user
        plStateDataRecord* fSDRec;  // the sdl data record
        plUoid  fUoid;              // the object it's meant for
        UInt32  fPlayerID;          // the player that originally sent the state

        // set by NetClient
        plKey fKey;                 // the key of the object it's meant for
        double fQueuedTime;
        int     fQueueTimeResets;

        PendingLoad() : fSDRec(nil),fPlayerID(0),fKey(nil),fQueuedTime(0.0),fQueueTimeResets(0) {}
        ~PendingLoad();
    };

private:
    // plOperationProgress  *fProgressBar;
    plOperationProgress *fTaskProgBar;
    
    typedef std::list<PendingLoad*> PendingLoadsList;
    PendingLoadsList fPendingLoads;
            
    // pending room page msgs
    std::vector<plNetMsgPagingRoom*>    fPendingPagingRoomMsgs;

    plNetTransport fTransport;

    // groups of objects in the game.  Each group is mastered by a single client.
    plNetClientGroups       fNetGroups;

    // cached char info
    plKey       fLocalPlayerKey;
    plKeyVec    fRemotePlayerKeys;
    // plKeyVec fNPCKeys;
    
    class plNetClientMgrMsg *   fDisableMsg;

    // ini info
    std::string         fIniAccountName;
    std::string         fIniAccountPass;
    std::string         fIniAuthServer;
    UInt32              fIniPlayerID;   // the player we want to load from vault.
    std::string         fSPDesiredPlayerName;   // SP: the player we want to load from vault.
    
    // server info
    double              fServerTimeOffset;      // diff between our unified time and server's unified time
    UInt32              fTimeSamples;
    double              fLastTimeUpdate;

    UInt8               fJoinOrder;         // returned by the server
    
    // voice lists
    int     fListenListMode;            // how we are generating our listen list    
    plNetListenList fListenList;        // other players I'm listening to
    plNetTalkList fTalkList;            // other players I'm talking to

    plNetClientMsgHandler   fMsgHandler;

    // recorder support
    plNetClientRecorder* fMsgRecorder;
    std::vector<plNetClientRecorder*> fMsgPlayers;

    plKey   fAgeSDLObjectKey;
    UInt8 fExperimentalLevel;
    plNetClientStats fNetClientStats;
    UInt8   fPingServerType;        // non-zero if we're pinging someone
    float   fOverrideAgeTimeOfDayPercent;   // for console debugging

    int fNumInitialSDLStates;
    int fRequiredNumInitialSDLStates;

    // simplification of object ownership...one player owns all non-physical objects in the world
    // physical objects are owned by whoever touched them most recently (or the "owner" if nobody
    // has touched it yet)
    bool fIsOwner;

    //
    void ICheckPendingStateLoad(double secs);
    int IDeduceLocallyOwned(const plUoid& loc) const;
    bool IHandlePlayerPageMsg(plPlayerPageMsg *playerMsg);  // *** 

    void IShowLists();
    void IShowRooms();
    void IShowAvatars();
    void IShowRelevanceRegions();
    
    int ISendDirtyState(double secs);
    int ISendMembersListRequest();
    int ISendRoomsReset();
    void ISendCCRPetition(plCCRPetitionMsg* petMsg);    
    void ISendCameraReset(hsBool bEnteringAge);
    
    hsBool IUpdateListenList(double secs);
    void IHandleNetVoiceListMsg(plNetVoiceListMsg* msg);
    hsBool IApplyNewListenList(std::vector<DistSqInfo>& newListenList, hsBool forceSynch);
    int IPrepMsg(plNetMessage* msg);
    void IPlayerChangeAge(hsBool exiting, Int32 spawnPt);   
    
    void IAddCloneRoom();
    void IRemoveCloneRoom();

    void IUnloadRemotePlayers();
    
    plKey ILoadClone(plLoadCloneMsg *cloneMsg);

    bool IFindModifier(plSynchedObject* obj, Int16 classIdx);
    void IClearPendingLoads();

    // recorder
    bool IIsRecordableMsg(plNetMessage* msg);
    void IPlaybackMsgs();

    void IRequestAgeState();

    void    IDumpOSVersionInfo() const;

    int ISendGameMessage(plMessage* msg);
    void IDisableNet ();

public:
    plNetClientMgr();
    ~plNetClientMgr();

    CLASSNAME_REGISTER( plNetClientMgr );
    GETINTERFACE_ANY( plNetClientMgr, plNetClientApp );

    static plNetClientMgr* GetInstance() { return plNetClientMgr::ConvertNoRef(fInstance); }

    void StartLinkOutFX();
    void StartLinkInFX();

    hsBool MsgReceive(plMessage* msg);
    void Shutdown();
    int  Init();

    void QueueDisableNet (bool showDlg, const char msg[]);

    int SendMsg(plNetMessage* msg);
    int Update(double secs);
    int IsLocallyOwned(const plSynchedObject* obj) const;   // returns yes/no/maybe
    int IsLocallyOwned(const plUoid&) const;        // for special cases, like sceneNodes. returns yes/no/maybe 
    plNetGroupId GetEffectiveNetGroup(const plSynchedObject*& obj) const;
    plNetGroupId SelectNetGroup(plSynchedObject* objIn, plKey groupKey);

    void SendLocalPlayerAvatarCustomizations();
    void SendApplyAvatarCustomizationsMsg(const plKey msgReceiver, bool netPropagate=true, bool localPropagate=true);

    // plLoggable
    bool Log(const char* str) const;

    // setters
    void SetIniAuthServer(const char * value)  { fIniAuthServer=value;}
    void SetIniAccountName(const char * value) { fIniAccountName=value;}
    void SetIniAccountPass(const char * value) { fIniAccountPass=value;}
    void SetIniPlayerID(UInt32 value)          { fIniPlayerID=value;}

    void SetSPDesiredPlayerName( const char * value ) { fSPDesiredPlayerName=value;}
    const char * GetSPDesiredPlayerName() const { return fSPDesiredPlayerName.c_str(); }

    void SetLocalPlayerKey(plKey l, hsBool pageOut=false);
    void SetNullSend(hsBool on);        // turn null send on/off
    void SetPingServer(UInt8 serverType) { fPingServerType = serverType; }
    
    // getters
    UInt32              GetPlayerID( void ) const;
    const char *        GetPlayerName( const plKey avKey=nil ) const;
    const char *        GetPlayerNameById (unsigned playerId) const;
    unsigned            GetPlayerIdByName(const char name[]) const;

    UInt8 GetJoinOrder()                const { return fJoinOrder; }    // only valid at join time

    plKey GetLocalPlayerKey()           const { return fLocalPlayerKey; }
    plSynchedObject* GetLocalPlayer(hsBool forceLoad=false) const;
    
    hsBool IsPeerToPeer()               const { return false; }
    hsBool IsConnected()                const { return true; }

    void IncNumInitialSDLStates();
    void ResetNumInitialSDLStates() { fNumInitialSDLStates=0; }
    int GetNumInitialSDLStates() const { return fNumInitialSDLStates; }
    void SetRequiredNumInitialSDLStates( int v ) { fRequiredNumInitialSDLStates=v; }
    int GetRequiredNumInitialSDLStates() const { return fRequiredNumInitialSDLStates; }

    // Linking progress
    void    StartTaskProgress( const char *msg, int numSteps );
    void    IncTaskProgress( const char *msg );

    // avatar vault actions
    int UploadPlayerVault(UInt32 vaultFlags);
    
    // remote players
    const std::vector<plKey>& RemotePlayerKeys() const { return fRemotePlayerKeys;  }
    plSynchedObject* GetRemotePlayer(int i) const;
    void AddRemotePlayerKey(plKey p);
    hsBool IsRemotePlayerKey(const plKey p, int* idx=nil);
    bool IsAPlayerKey(const plKey pKey) { return (pKey==GetLocalPlayerKey() || IsRemotePlayerKey(pKey));    }

    void SetConsoleOutput( bool b ) { SetFlagsBit(kConsoleOutput, b); }
    bool GetConsoleOutput() const { return GetFlagsBit(kConsoleOutput); }
    
    // Net groups
    const plNetClientGroups* GetNetGroups()     const { return &fNetGroups; }
    plNetClientGroups* GetNetGroups()   { return &fNetGroups;   }

    // Voice Lists
    plNetListenList* GetListenList() { return &fListenList; }
    plNetTalkList* GetTalkList() { return &fTalkList; }
    void SetListenListMode (int i); 
    void SynchTalkList();
    int GetListenListMode() { return fListenListMode; }
        
    // network activity-generated events, passed to current task
    bool CanSendMsg(plNetMessage * msg);

    const plNetTransport& TransportMgr() const { return fTransport; }
    plNetTransport& TransportMgr() { return fTransport; }
    
    bool ObjectInLocalAge(const plSynchedObject* obj) const;
    
    // time converters
    plUnifiedTime GetServerTime() const;
    const char* GetServerLogTimeAsString(std::string& ts) const;
    double GetCurrentAgeElapsedSeconds() const;
    float GetCurrentAgeTimeOfDayPercent() const;

    bool RecordMsgs(const char* recType, const char* recName);
    bool PlaybackMsgs(const char* recName);

    void MakeCCRInvisible(plKey avKey, int level);
    bool CCRVaultConnected() const { return GetFlagsBit(kCCRVaultConnected); }

    UInt8 GetExperimentalLevel() const { return fExperimentalLevel; }

    void AddPendingLoad(PendingLoad *pl);
    const plKey& GetAgeSDLObjectKey() const { return fAgeSDLObjectKey; }
    plUoid GetAgeSDLObjectUoid(const char* ageName) const;
    plNetClientComm& GetNetClientComm()  { return fNetClientComm; }
    const char* plNetClientMgr::GetNextAgeFilename();
    plNetClientStats& GetNetClientStats() { return fNetClientStats; }
    void SetOverrideAgeTimeOfDayPercent(float f) { fOverrideAgeTimeOfDayPercent=f;  }

    void AddPendingPagingRoomMsg( plNetMsgPagingRoom * msg );
    void MaybeSendPendingPagingRoomMsgs();
    void SendPendingPagingRoomMsgs();
    void ClearPendingPagingRoomMsgs();
    
    void NotifyRcvdAllSDLStates();

    plOperationProgress* GetTaskProgBar() { return fTaskProgBar;    }
    
    bool DebugMsgV(const char* fmt, va_list args) const;
    bool ErrorMsgV(const char* fmt, va_list args) const; 
    bool WarningMsgV(const char* fmt, va_list args) const; 
    bool AppMsgV(const char* fmt, va_list args) const;

    bool IsObjectOwner();
    void SetObjectOwner(bool own);

    void StoreSDLState(const plStateDataRecord* sdRec, const plUoid& uoid, UInt32 sendFlags, UInt32 writeOptions);

    void UpdateServerTimeOffset(plNetMessage* msg);
    void ResetServerTimeOffset();

private:
    plNetClientComm             fNetClientComm;
    plNetClientCommMsgHandler   fNetClientCommMsgHandler;
    
    int IInitNetClientComm();
    int IDeInitNetClientComm();
    void INetClientCommOpStarted(UInt32 context);
    void INetClientCommOpComplete(UInt32 context, int resultCode);

    friend struct plNCAgeJoiner;
    friend struct plNCAgeLeaver;
    friend class plNetDniInfoSource;
    friend class plNetTalkList;
    friend class plNetClientMsgHandler;
    friend struct plNetClientCommMsgHandler;
};

#define plCheckNetMgrResult_VoidReturn(r,s)     if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return; }
// returns int
#define plCheckNetMgrResult_ValReturn(r,s)      if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return r; }
// returns bool
#define plCheckNetMgrResult_BoolReturn(r,s)     if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return false; }

#endif  // PL_NET_CLIENT_inc