#ifndef __INC_METIN_II_GAME_INPUT_PROCESSOR__
#define __INC_METIN_II_GAME_INPUT_PROCESSOR__

#include <unordered_map>
#include "packet_info.h"

enum
{
	INPROC_CLOSE,
	INPROC_HANDSHAKE,
	INPROC_LOGIN,
	INPROC_MAIN,
	INPROC_DEAD,
	INPROC_DB,
	INPROC_P2P,
	INPROC_AUTH,
};

void LoginFailure(LPDESC d, const char * c_pszStatus);
void RecordLoginFailure(const char* hostName);
void ClearLoginFailure(const char* hostName);
void BroadcastGuildMarkUpdate(DWORD dwGuildID, WORD wImgIdx);


class CInputProcessor
{
	public:
		CInputProcessor();
		virtual ~CInputProcessor() {};

		virtual bool Process(LPDESC d, const void * c_pvOrig, int iBytes, int & r_iBytesProceed);
		virtual BYTE GetType() = 0;

		void BindPacketInfo(CPacketInfo * pPacketInfo);
		void Pong(LPDESC d);
		void Version(LPCHARACTER ch, const char* c_pData);

	protected:
		virtual int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) = 0;

		CPacketInfo * m_pPacketInfo;
		int	m_iBufferLeft;

		CPacketInfoCG 	m_packetInfoCG;
};

class CInputClose : public CInputProcessor
{
	public:
		BYTE	GetType() override { return INPROC_CLOSE; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override { return m_iBufferLeft; }
};

class CInputHandshake : public CInputProcessor
{
	public:
		CInputHandshake();
		~CInputHandshake() override;

		BYTE	GetType() override { return INPROC_HANDSHAKE; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

		using HSHandler = int (CInputHandshake::*)(LPDESC, const char*);
		std::unordered_map<uint16_t, HSHandler> m_handlers;
		void RegisterHandlers();

		int HandleText(LPDESC d, const char* c_pData);
		int HandleMarkLogin(LPDESC d, const char* c_pData);
		int HandleStateChecker(LPDESC d, const char* c_pData);
		int HandlePong(LPDESC d, const char* c_pData);
		int HandleKeyResponse(LPDESC d, const char* c_pData);

	protected:
		void		GuildMarkLogin(LPDESC d, const char* c_pData);

		CPacketInfo *	m_pMainPacketInfo;
};

class CInputLogin : public CInputProcessor
{
	public:
		CInputLogin();
		BYTE	GetType() override { return INPROC_LOGIN; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

		using LoginHandler = int (CInputLogin::*)(LPDESC, const char*);
		std::unordered_map<uint16_t, LoginHandler> m_handlers;
		void RegisterHandlers();

		template<void (CInputLogin::*fn)(LPDESC, const char*)>
		int SimpleHandler(LPDESC d, const char* p) { (this->*fn)(d, p); return 0; }

		int HandlePong(LPDESC d, const char*);
		int HandleMarkLogin(LPDESC d, const char*);
		int HandleGuildSymbolUpload(LPDESC d, const char*);
		int HandleVersion(LPDESC d, const char*);

	protected:
		void		LoginByKey(LPDESC d, const char * data);

		void		CharacterSelect(LPDESC d, const char * data);
		void		CharacterCreate(LPDESC d, const char * data);
		void		CharacterDelete(LPDESC d, const char * data);
		void		Entergame(LPDESC d, const char * data);
		void		Empire(LPDESC d, const char * c_pData);
		void		GuildMarkCRCList(LPDESC d, const char* c_pData);
		// MARK_BUG_FIX
		void		GuildMarkIDXList(LPDESC d, const char* c_pData);
		// END_OF_MARK_BUG_FIX
		void		GuildMarkUpload(LPDESC d, const char* c_pData);
		int			GuildSymbolUpload(LPDESC d, const char* c_pData, size_t uiBytes);
		void		GuildSymbolCRC(LPDESC d, const char* c_pData);
		void		ChangeName(LPDESC d, const char * data);
};

class CInputMain : public CInputProcessor
{
	public:
		CInputMain();
		BYTE	GetType() override { return INPROC_MAIN; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

		using MainHandler = int (CInputMain::*)(LPDESC, const char*);
		struct HandlerEntry {
			MainHandler handler;
			bool blockInObserverMode;
		};
		std::unordered_map<uint16_t, HandlerEntry> m_handlers;
		virtual void RegisterHandlers();

		template<void (CInputMain::*fn)(LPCHARACTER, const char*)>
		int SimpleHandler(LPDESC d, const char* p);

		template<void (CInputMain::*fn)(LPCHARACTER, const void*)>
		int SimpleHandlerV(LPDESC d, const char* p);

		int HandlePong(LPDESC d, const char* p);
		int HandleChat(LPDESC d, const char* p);
		int HandleWhisper(LPDESC d, const char* p);
		int HandleMove(LPDESC d, const char* p);
		int HandleAttack(LPDESC d, const char* p);
		int HandleShoot(LPDESC d, const char* p);
		int HandleShop(LPDESC d, const char* p);
		int HandleMessenger(LPDESC d, const char* p);
		int HandleSyncPosition(LPDESC d, const char* p);
		int HandleFlyTargeting(LPDESC d, const char* p);
		int HandleAddFlyTargeting(LPDESC d, const char* p);
		int HandleQuestCancel(LPDESC d, const char* p);
		int HandleSafeboxCheckout(LPDESC d, const char* p);
		int HandleMallCheckout(LPDESC d, const char* p);
		int HandleGuild(LPDESC d, const char* p);
		int HandleMyShop(LPDESC d, const char* p);
		int HandleClientVersion(LPDESC d, const char* p);
		int HandleDragonSoulRefine(LPDESC d, const char* p);

	protected:
		void		Attack(LPCHARACTER ch, const uint16_t header, const char* data);

		int			Whisper(LPCHARACTER ch, const char * data, size_t uiBytes);
		int			Chat(LPCHARACTER ch, const char * data, size_t uiBytes);
		void		ItemUse(LPCHARACTER ch, const char * data);
		void		ItemDrop(LPCHARACTER ch, const char * data);
		void		ItemDrop2(LPCHARACTER ch, const char * data);
		void		ItemMove(LPCHARACTER ch, const char * data);
		void		ItemPickup(LPCHARACTER ch, const char * data);
		void		ItemToItem(LPCHARACTER ch, const char * pcData);
		void		QuickslotAdd(LPCHARACTER ch, const char * data);
		void		QuickslotDelete(LPCHARACTER ch, const char * data);
		void		QuickslotSwap(LPCHARACTER ch, const char * data);
		int			Shop(LPCHARACTER ch, const char * data, size_t uiBytes);
		void		OnClick(LPCHARACTER ch, const char * data);
		void		Exchange(LPCHARACTER ch, const char * data);
		void		Position(LPCHARACTER ch, const char * data);
		void		Move(LPCHARACTER ch, const char * data);
		int			SyncPosition(LPCHARACTER ch, const char * data, size_t uiBytes);
		void		FlyTarget(LPCHARACTER ch, const char * pcData, uint16_t wHeader);
		void		UseSkill(LPCHARACTER ch, const char * pcData);

		void		ScriptAnswer(LPCHARACTER ch, const void * pvData);
		void		ScriptButton(LPCHARACTER ch, const void * pvData);
		void		ScriptSelectItem(LPCHARACTER ch, const void * pvData);

		void		QuestInputString(LPCHARACTER ch, const void * pvData);
		void		QuestConfirm(LPCHARACTER ch, const void* pvData);
		void		QuestCancel(LPCHARACTER ch);
		void		Target(LPCHARACTER ch, const char * pcData);
		void		Warp(LPCHARACTER ch, const char * pcData);
		void		SafeboxCheckin(LPCHARACTER ch, const char * c_pData);
		void		SafeboxCheckout(LPCHARACTER ch, const char * c_pData, bool bMall);
		void		SafeboxItemMove(LPCHARACTER ch, const char * data);
		int			Messenger(LPCHARACTER ch, const char* c_pData, size_t uiBytes);

		void 		PartyInvite(LPCHARACTER ch, const char * c_pData);
		void 		PartyInviteAnswer(LPCHARACTER ch, const char * c_pData);
		void		PartyRemove(LPCHARACTER ch, const char * c_pData);
		void		PartySetState(LPCHARACTER ch, const char * c_pData);
		void		PartyUseSkill(LPCHARACTER ch, const char * c_pData);
		void		PartyParameter(LPCHARACTER ch, const char * c_pData);

		int			Guild(LPCHARACTER ch, const char * data, size_t uiBytes);
		void		AnswerMakeGuild(LPCHARACTER ch, const char* c_pData);

		// Guild sub-handlers (dispatched from Guild())
		int			GuildSub_DepositMoney(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_WithdrawMoney(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_AddMember(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_RemoveMember(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_ChangeGradeName(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_ChangeGradeAuthority(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_Offer(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_ChargeGSP(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_PostComment(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_DeleteComment(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_RefreshComment(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_ChangeMemberGrade(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_UseSkill(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_ChangeMemberGeneral(LPCHARACTER ch, const char* data, size_t uiBytes);
		int			GuildSub_InviteAnswer(LPCHARACTER ch, const char* data, size_t uiBytes);

		void		Fishing(LPCHARACTER ch, const char* c_pData);
		void		ItemGive(LPCHARACTER ch, const char* c_pData);
		void		Hack(LPCHARACTER ch, const char * c_pData);
		int			MyShop(LPCHARACTER ch, const char * c_pData, size_t uiBytes);

		void		Refine(LPCHARACTER ch, const char* c_pData);
};

class CInputDead : public CInputMain
{
	public:
		CInputDead();
		BYTE	GetType() override { return INPROC_DEAD; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;
		void RegisterHandlers() override;
};

class CInputDB : public CInputProcessor
{
public:
	CInputDB();
	bool Process(LPDESC d, const void * c_pvOrig, int iBytes, int & r_iBytesProceed) override;
	BYTE GetType() override { return INPROC_DB; }

protected:
	int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

	using DBHandler = int (CInputDB::*)(LPDESC, const char*);
	std::unordered_map<uint16_t, DBHandler> m_handlers;
	void RegisterHandlers();
	LPDESC FindByHandle() const;

	template<void (CInputDB::*fn)(const char*)>
	int DataHandler(LPDESC, const char* p) { (this->*fn)(p); return 0; }

	template<void (CInputDB::*fn)(LPDESC, const char*)>
	int DescHandler(LPDESC, const char* p) { (this->*fn)(FindByHandle(), p); return 0; }

	template<class T, void (CInputDB::*fn)(T*)>
	int TypedHandler(LPDESC, const char* p) { (this->*fn)((T*)p); return 0; }
	int HandleLoginSuccess(LPDESC, const char*);
	int HandleLoginNotExist(LPDESC, const char*);
	int HandleLoginWrongPasswd(LPDESC, const char*);
	int HandlePlayerCreateFailed(LPDESC, const char*);
	int HandlePlayerCreateAlready(LPDESC, const char*);
	int HandlePlayerDeleteFail(LPDESC, const char*);
	int HandlePlayerLoadFailed(LPDESC, const char*);
	int HandleSafeboxWrongPassword(LPDESC, const char*);
	int HandleGuildSkillRecharge(LPDESC, const char*);
	int HandleGuildWarReserveDelete(LPDESC, const char*);
	int HandleSpareItemIDRange(LPDESC, const char*);
	int HandleHorseName(LPDESC, const char*);
	int HandleMyshopPricelistRes(LPDESC, const char*);
	int HandleRespondChannelStatus(LPDESC, const char*);

protected:
	void		MapLocations(const char * c_pData);
	void		LoginSuccess(DWORD dwHandle, const char *data);
	void		PlayerCreateFailure(LPDESC d, BYTE bType);	// 0 = 일반 실패 1 = 이미 있음
	void		PlayerDeleteSuccess(LPDESC d, const char * data);
	void		PlayerDeleteFail(LPDESC d);
	void		PlayerLoad(LPDESC d, const char* data);
	void		PlayerCreateSuccess(LPDESC d, const char * data);
	void		Boot(const char* data);
	void		QuestLoad(LPDESC d, const char * c_pData);
	void		SafeboxLoad(LPDESC d, const char * c_pData);
	void		SafeboxChangeSize(LPDESC d, const char * c_pData);
	void		SafeboxWrongPassword(LPDESC d);
	void		SafeboxChangePasswordAnswer(LPDESC d, const char* c_pData);
	void		MallLoad(LPDESC d, const char * c_pData);
	void		EmpireSelect(LPDESC d, const char * c_pData);
	void		P2P(const char * c_pData);
	void		ItemLoad(LPDESC d, const char * c_pData);
	void		AffectLoad(LPDESC d, const char * c_pData);

	void		GuildLoad(const char * c_pData);
	void		GuildSkillUpdate(const char* c_pData);
	void		GuildSkillRecharge();
	void		GuildExpUpdate(const char* c_pData);
	void		GuildAddMember(const char* c_pData);
	void		GuildRemoveMember(const char* c_pData);
	void		GuildChangeGrade(const char* c_pData);
	void		GuildChangeMemberData(const char* c_pData);
	void		GuildDisband(const char* c_pData);
	void		GuildLadder(const char* c_pData);
	void		GuildWar(const char* c_pData);
	void		GuildWarScore(const char* c_pData);
	void		GuildSkillUsableChange(const char* c_pData);
	void		GuildMoneyChange(const char* c_pData);
	void		GuildWithdrawMoney(const char* c_pData);
	void		GuildWarReserveAdd(TGuildWarReserve * p);
	void		GuildWarReserveUpdate(TGuildWarReserve * p);
	void		GuildWarReserveDelete(DWORD dwID);
	void		GuildWarBet(TPacketGDGuildWarBet * p);
	void		GuildChangeMaster(TPacketChangeGuildMaster* p);

	void		LoginAlready(LPDESC d, const char * c_pData);

	void		PartyCreate(const char* c_pData);
	void		PartyDelete(const char* c_pData);
	void		PartyAdd(const char* c_pData);
	void		PartyRemove(const char* c_pData);
	void		PartyStateChange(const char* c_pData);
	void		PartySetMemberLevel(const char* c_pData);

	void		Time(const char * c_pData);

	void		ReloadProto(const char * c_pData);
	void		ChangeName(LPDESC d, const char * data);

	void		AuthLogin(LPDESC d, const char * c_pData);
	void		ItemAward(const char * c_pData);

	void		ChangeEmpirePriv(const char* c_pData);
	void		ChangeGuildPriv(const char* c_pData);
	void		ChangeCharacterPriv(const char* c_pData);

	void		MoneyLog(const char* c_pData);

	void		SetEventFlag(const char* c_pData);

	void		CreateObject(const char * c_pData);
	void		DeleteObject(const char * c_pData);
	void		UpdateLand(const char * c_pData);

	void		Notice(const char * c_pData);

	void		MarriageAdd(TPacketMarriageAdd * p);
	void		MarriageUpdate(TPacketMarriageUpdate * p);
	void		MarriageRemove(TPacketMarriageRemove * p);

	void		WeddingRequest(TPacketWeddingRequest* p);
	void		WeddingReady(TPacketWeddingReady* p);
	void		WeddingStart(TPacketWeddingStart* p);
	void		WeddingEnd(TPacketWeddingEnd* p);

	// MYSHOP_PRICE_LIST
	void		MyshopPricelistRes( LPDESC d, const TPacketMyshopPricelistHeader* p );
	// END_OF_MYSHOP_PRICE_LIST

	//RELOAD_ADMIN
	void ReloadAdmin( const char * c_pData );
	//END_RELOAD_ADMIN

	void		DetailLog(const TPacketNeedLoginLogInfo* info);
	void		ItemAwardInformer(TPacketItemAwardInfromer* data);

	void		RespondChannelStatus(LPDESC desc, const char* pcData);

	protected:
		DWORD		m_dwHandle;
};

class CInputP2P : public CInputProcessor
{
	public:
		CInputP2P();
		BYTE	GetType() override { return INPROC_P2P; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

		using P2PHandler = int (CInputP2P::*)(LPDESC, const char*);
		std::unordered_map<uint16_t, P2PHandler> m_handlers;
		void RegisterHandlers();

		template<void (CInputP2P::*fn)(LPDESC, const char*)>
		int DescHandler(LPDESC d, const char* p) { (this->*fn)(d, p); return 0; }

		template<void (CInputP2P::*fn)(const char*)>
		int DataHandler(LPDESC, const char* p) { (this->*fn)(p); return 0; }

		int HandleRelay(LPDESC d, const char*);
		int HandleNotice(LPDESC d, const char*);
		int HandleGuild(LPDESC d, const char*);
		int HandleShutdown(LPDESC d, const char*);
		int HandleSiege(LPDESC d, const char*);
		int HandleReloadCRC(LPDESC d, const char*);
		int HandleCheckClientVersion(LPDESC d, const char*);

	public:
		void		Setup(LPDESC d, const char * c_pData);
		void		Login(LPDESC d, const char * c_pData);
		void		Logout(LPDESC d, const char * c_pData);
		int			Relay(LPDESC d, const char * c_pData, size_t uiBytes);
		int			Notice(LPDESC d, const char * c_pData, size_t uiBytes);
		int			Guild(LPDESC d, const char* c_pData, size_t uiBytes);
		void		Shout(const char * c_pData);
		void		Disconnect(const char * c_pData);
		void		MessengerAdd(const char * c_pData);
		void		MessengerRemove(const char * c_pData);
		void		FindPosition(LPDESC d, const char* c_pData);
		void		WarpCharacter(const char* c_pData);
		void		GuildWarZoneMapIndex(const char* c_pData);
		void		Transfer(const char * c_pData);
		void		XmasWarpSanta(const char * c_pData);
		void		XmasWarpSantaReply(const char * c_pData);
		void		LoginPing(LPDESC d, const char * c_pData);
		void		BlockChat(const char * c_pData);
		void		IamAwake(LPDESC d, const char * c_pData);
		void		MessengerRequestAdd(const char* c_pData);
		void		MessengerResponse(const char* c_pData);
		void		GuildMarkUpdate(const char * c_pData);

	protected:
		CPacketInfoGG 	m_packetInfoGG;
};

class CInputAuth : public CInputProcessor
{
	public:
		CInputAuth();
		BYTE GetType() override { return INPROC_AUTH; }

	protected:
		int	Analyze(LPDESC d, uint16_t wHeader, const char * c_pData) override;

		using AuthHandler = int (CInputAuth::*)(LPDESC, const char*);
		std::unordered_map<uint16_t, AuthHandler> m_handlers;
		void RegisterHandlers();

		int HandlePong(LPDESC d, const char*);
		int HandleLogin3(LPDESC d, const char*);

	public:
		void		Login(LPDESC d, const char * c_pData);

};

#endif /* __INC_METIN_II_GAME_INPUT_PROCESSOR__ */
