/*==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/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

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