/*
 * OpenClonk, http://www.openclonk.org
 *
 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
 *
 * Distributed under the terms of the ISC license; see accompanying file
 * "COPYING" for details.
 *
 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
 * See accompanying file "TRADEMARK" for details.
 *
 * To redistribute this file separately, substitute the full license texts
 * for the above references.
 */

/* engine handler of league system */

#ifndef C4LEAGUE_H_INCLUDED
#define C4LEAGUE_H_INCLUDED

#include "lib/SHA1.h"
#include "gui/C4Gui.h"
#include "network/C4Network2Reference.h"

#define C4League_Name_Valid_Characters "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD9\xDA\xDB\xDC\xDD\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF\x20\x2E\x2D\x5F"

// maximum league count a game can run in
const int32_t C4NetMaxLeagues = 10;

enum C4LeagueAction
{
	C4LA_None,      // no action

	C4LA_Start,     // [host] Game registration
	C4LA_Update,    // [host] Game status update (heartbeat)
	C4LA_End,       // [host] Final game status
	C4LA_PlrAuthCheck,// [host] Player authentication check

	C4LA_RefQuery,  // [client] Query reference list
	C4LA_PlrAuth,   // [client] Player authentication request

	C4LA_ReportDisconnect // [both] Sent by host and client when a client is disconnected irregularly
};

class C4LeagueRequestHead
{
public:
	C4LeagueRequestHead(C4LeagueAction eAction, const char *szCSID = "", const char *szAUID = "")
			: eAction(eAction), CSID(szCSID), AUID(szAUID), fRememberLogin(false)
	{ }

private:
	C4LeagueAction eAction;
	StdCopyStrBuf CSID;
	StdCopyStrBuf AUID;

	// Auth
	StdCopyStrBuf Account;
	StdCopyStrBuf Password;
	StdCopyStrBuf NewAccount;
	StdCopyStrBuf NewPassword;
	bool fRememberLogin;

public:
	void SetAuth(const char *szAccount, const char *szPassword, bool fRememberLogin);
	void SetNewAccount(const char *szNewAccount);
	void SetNewPassword(const char *szNewPassword);

	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueReportDisconnectHead : public C4LeagueRequestHead
{
private:
	C4LeagueDisconnectReason eReason;
public:
	C4LeagueReportDisconnectHead(const char *szCSID, C4LeagueDisconnectReason eReason) : C4LeagueRequestHead(C4LA_ReportDisconnect, szCSID, nullptr), eReason(eReason) {}

public:
	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueRequestHeadEnd : public C4LeagueRequestHead
{
public:
	C4LeagueRequestHeadEnd(C4LeagueAction eAction, const char *szCSID, const char *szRecordName = nullptr, const BYTE *pRecordSHA = nullptr)
			: C4LeagueRequestHead(eAction, szCSID), RecordName(szRecordName)
	{
		if (pRecordSHA)
			memcpy(RecordSHA, pRecordSHA, SHA_DIGEST_LENGTH);
	}

private:
	StdCopyStrBuf RecordName;
	uint8_t RecordSHA[SHA_DIGEST_LENGTH];

public:
	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueResponseHead
{
public:
	C4LeagueResponseHead() = default;

private:
	StdCopyStrBuf Status;
	StdCopyStrBuf CSID;
	StdCopyStrBuf Message;

	// Auth
	StdCopyStrBuf Account;
	StdCopyStrBuf AUID;
	StdCopyStrBuf FBID;
	StdCopyStrBuf LoginToken;

public:
	const char *getCSID() const { return CSID.getData(); }
	const char *getMessage() const { return Message.getData(); }
	bool isSuccess() const { return SEqualNoCase(Status.getData(), "Success"); }
	bool isStatusRegister() const { return SEqualNoCase(Status.getData(), "Register"); }
	const char *getAccount() const { return Account.getData(); }
	const char *getAUID() const { return AUID.getData(); }
	const char *getFBID() const { return FBID.getData(); }
	const char *getLoginToken() const { return LoginToken.getData(); }

	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueResponseHeadStart : public C4LeagueResponseHead
{
private:
	StdCopyStrBuf League;
	StdCopyStrBuf StreamingAddr;
	int32_t fHaveSeed;
	int32_t iSeed;
	int32_t iMaxPlayers;

public:
	const char *getLeague() const { return League.getData(); }
	const char *getStreamingAddr() const { return StreamingAddr.getData(); }
	bool haveSeed() const { return !!fHaveSeed; }
	int32_t getSeed() const { return iSeed; }
	int32_t getMaxPlayers() const { return iMaxPlayers; }

	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueResponseHeadUpdate : public C4LeagueResponseHead
{
private:
	StdCopyStrBuf League;
	C4ClientPlayerInfos PlrInfos;

public:
	const char *getLeague() const { return League.getData(); }
	const C4ClientPlayerInfos &GetPlrInfos() const { return PlrInfos; }

	void CompileFunc(StdCompiler *pComp);
};

class C4LeagueResponseHeadAuthCheck : public C4LeagueResponseHead
{

private:
	StdCopyStrBuf Leagues[C4NetMaxLeagues];
	int32_t Scores[C4NetMaxLeagues];
	int32_t Ranks[C4NetMaxLeagues];
	int32_t RankSymbols[C4NetMaxLeagues];
	StdCopyStrBuf ClanTag;
	StdCopyStrBuf ProgressData;

public:
	int32_t getScore(const char *szLeague) const;
	int32_t getRank(const char *szLeague) const;
	int32_t getRankSymbol(const char *szLeague) const;
	const char *getClanTag() const { return ClanTag.getData(); }
	const char *getProgressData(const char *szLeague) const;

	void CompileFunc(StdCompiler *pComp);
};

// a linked list of all known feedback IDs, associated to player IDs
class C4LeagueFBIDList
{
private:
	struct FBIDItem
	{
		StdCopyStrBuf Account;
		StdCopyStrBuf FBID;
		FBIDItem *pNext;
	} *pFirst{nullptr};

public:
	C4LeagueFBIDList() = default;
	~C4LeagueFBIDList() { Clear(); }
	void Clear();
	void RemoveFBIDByAccount(const char *szAccount);
	bool FindFBIDByAccount(const char *szAccount, StdStrBuf *pFBIDOut);

	void AddFBID(const char *szFBID, const char *szAccount);
};

class C4LeagueClient : public C4Network2RefClient
{

private:
	StdCopyStrBuf CSID;
	C4LeagueAction eCurrAction{C4LA_None};
	C4LeagueFBIDList FBIDList;

public:
	C4LeagueClient() : C4Network2RefClient(), CSID() { }
	const char *getCSID() const { return CSID.getData(); }
	C4LeagueAction getCurrentAction() const { return eCurrAction; }
	void ResetCurrentAction() { eCurrAction = C4LA_None; }

	// Action "Start"
	bool Start(const C4Network2Reference &Ref);
	bool GetStartReply(StdStrBuf *pMessage, StdStrBuf *pLeague, StdStrBuf *pStreamingAddr, int32_t *pSeed, int32_t *pMaxPlayers);

	// Action "Update"
	bool Update(const C4Network2Reference &Ref);
	bool GetUpdateReply(StdStrBuf *pMessage, C4ClientPlayerInfos *pPlayerLeagueInfos);

	// Action "End"
	bool End(const C4Network2Reference &Ref, const char *szRecordName, const BYTE *pRecordSHA);
	bool GetEndReply(StdStrBuf *pMessage, class C4RoundResultsPlayers *pRoundResults);

	// Action "Auth"
	bool Auth(const C4PlayerInfo &PlrInfo, const char *szAccount, const char *szPassword, const char *szNewAccount = nullptr, const char *szNewPassword = nullptr, bool fRememberLogin = false);
	bool GetAuthReply(StdStrBuf *pMessage, StdStrBuf *pAUID, StdStrBuf *pAccount, bool *pRegister, StdStrBuf *pLoginToken);

	// Action "Join"
	bool AuthCheck(const C4PlayerInfo &PlrInfo);
	bool GetAuthCheckReply(StdStrBuf *pMessage, const char *szLeague, class C4PlayerInfo *pPlrInfo);

	// Action "Disconnect" - sent by host and client when one or more clients are disconnected irregularly
	bool ReportDisconnect(const C4ClientPlayerInfos &rSendPlayerFBIDs, C4LeagueDisconnectReason eReason);
	bool GetReportDisconnectReply(StdStrBuf *pMessage);
};

// dialog shown for each participating player to enter league authentication data
class C4LeagueSignupDialog : public C4GUI::Dialog
{
private:
	C4GUI::CheckBox *pChkPassword, *pChkRememberLogin;
	C4GUI::LabeledEdit *pEdtAccount, *pEdtPass, *pEdtPass2;
	C4GUI::Button *pBtnOK, *pBtnAbort;
	int32_t iEdtPassSpace;
	StdStrBuf strPlayerName;
	bool fRememberLogin;
public:
	C4LeagueSignupDialog(const char *szPlayerName, const char *szLeagueName, const char *szLeagueServerName, const char *szAccountPref, const char *szPassPref, bool fWarnThirdParty, bool fRegister, bool fRememberLogin);
	~C4LeagueSignupDialog() override = default;

	const char *GetAccount() { return pEdtAccount->GetText(); }
	bool HasPass() { return !pChkPassword || pChkPassword->GetChecked(); }
	const char *GetPass() { return pEdtPass->GetText(); }
	bool GetRememberLogin() { return pChkRememberLogin && pChkRememberLogin->GetChecked(); }

	// check for errors (overridden)
	void UserClose(bool fOK) override;

	// show modal league dialog to query password for player; return
	static bool ShowModal(const char *szPlayerName, const char *szLeagueName, const char *szLeagueServerName, StdStrBuf *psAccount, StdStrBuf *psPass, bool fWarnThirdParty, bool fRegister, bool *pfRememberLogin);

private:
	void OnChkPassword();
};


#endif // C4LEAGUE_H_INCLUDED
