/*
IRC plugin for Miranda IM

Copyright (C) 2003-05 Jurgen Persson
Copyright (C) 2007-09 George Hazan

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef _IRCPROTO_H_
#define _IRCPROTO_H_

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

typedef bool (CIrcProto::*PfnIrcMessageHandler)(const CIrcMessage *pmsg);

#pragma pack(4)

struct CIrcHandler
{
	CIrcHandler(const wchar_t* _name, PfnIrcMessageHandler _handler) :
		m_name(_name),
		m_handler(_handler)
	{
	}

	const wchar_t* m_name;
	PfnIrcMessageHandler m_handler;
};
#pragma pack()

struct CIrcProto : public PROTO<CIrcProto>
{
	CIrcProto(const char*, const wchar_t*);
	~CIrcProto();

	// Protocol interface
	MCONTACT AddToList(int flags, PROTOSEARCHRESULT* psr) override;
			   
	int      Authorize(MEVENT hDbEvent) override;
	int      AuthDeny(MEVENT hDbEvent, const wchar_t* szReason) override;
			   
	HANDLE   FileAllow(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szPath) override;
	int      FileCancel(MCONTACT hContact, HANDLE hTransfer) override;
	int      FileDeny(MCONTACT hContact, HANDLE hTransfer, const wchar_t *szReason) override;
	int      FileResume(HANDLE hTransfer, int *action, const wchar_t **szFilename) override;
			   
	INT_PTR  GetCaps(int type, MCONTACT hContact = NULL) override;
			   
	HANDLE   SearchBasic(const wchar_t* id) override;
			   
	HANDLE   SendFile(MCONTACT hContact, const wchar_t *szDescription, wchar_t **ppszFiles) override;
	int      SendMsg(MCONTACT hContact, int flags, const char* msg) override;
			   
	int      SetStatus(int iNewStatus) override;
			   
	HANDLE   GetAwayMsg(MCONTACT hContact) override;
	int      SetAwayMsg(int m_iStatus, const wchar_t *msg) override;
			   
	void     OnBuildProtoMenu(void) override;
	void     OnContactDeleted(MCONTACT) override;
	void     OnModulesLoaded() override;
	void     OnShutdown() override;

	// Services
	INT_PTR __cdecl SvcCreateAccMgrUI(WPARAM, LPARAM);
	INT_PTR __cdecl GetMyAwayMsg(WPARAM, LPARAM);

	INT_PTR __cdecl OnChangeNickMenuCommand(WPARAM, LPARAM);
	INT_PTR __cdecl OnDoubleclicked(WPARAM, LPARAM);
	INT_PTR __cdecl OnJoinChat(WPARAM, LPARAM);
	INT_PTR __cdecl OnJoinMenuCommand(WPARAM, LPARAM);
	INT_PTR __cdecl OnLeaveChat(WPARAM, LPARAM);
	INT_PTR __cdecl OnMenuChanSettings(WPARAM, LPARAM);
	INT_PTR __cdecl OnMenuDisconnect(WPARAM, LPARAM);
	INT_PTR __cdecl OnMenuIgnore(WPARAM, LPARAM);
	INT_PTR __cdecl OnMenuWhois(WPARAM, LPARAM);
	INT_PTR __cdecl OnQuickConnectMenuCommand(WPARAM, LPARAM);
	INT_PTR __cdecl OnShowListMenuCommand(WPARAM, LPARAM);
	INT_PTR __cdecl OnShowServerMenuCommand(WPARAM, LPARAM);

	// Events
	int __cdecl OnInitOptionsPages(WPARAM, LPARAM);
	int __cdecl OnInitUserInfo(WPARAM, LPARAM);
	int __cdecl OnMenuPreBuild(WPARAM, LPARAM);
	int __cdecl OnDbSettingChanged(WPARAM, LPARAM);

	int __cdecl GCEventHook(WPARAM, LPARAM);
	int __cdecl GCMenuHook(WPARAM, LPARAM);

	// Data

	char     m_serverName[100];
	char     m_password[500];
	wchar_t  m_identSystem[10];
	char     m_network[30];
	char     m_portStart[10];
	char     m_portEnd[10];
	int      m_iSSL;
	wchar_t  m_identPort[10];
	wchar_t  m_nick[30], m_pNick[30];
	wchar_t  m_alternativeNick[30];
	wchar_t  m_name[200];
	wchar_t  m_userID[200];
	wchar_t  m_quitMessage[400];
	wchar_t  m_userInfo[500];
	char     m_myHost[50];
	char     m_mySpecifiedHost[500];
	char     m_mySpecifiedHostIP[50];
	char     m_myLocalHost[50];
	WORD     m_myLocalPort;
	wchar_t *m_alias;
	int      m_serverComboSelection;
	int      m_quickComboSelection;
	int      m_onlineNotificationTime;
	int      m_onlineNotificationLimit;
	BYTE     m_scriptingEnabled;
	BYTE     m_IPFromServer;
	BYTE     m_showAddresses;
	BYTE     m_disconnectDCCChats;
	BYTE     m_disableErrorPopups;
	BYTE     m_rejoinChannels;
	BYTE     m_rejoinIfKicked;
	BYTE     m_hideServerWindow;
	BYTE     m_ident;
	BYTE     m_identTimer;
	BYTE     m_disableDefaultServer;
	BYTE     m_autoOnlineNotification;
	BYTE     m_sendKeepAlive;
	BYTE     m_joinOnInvite;
	BYTE     m_perform;
	BYTE     m_forceVisible;
	BYTE     m_ignore;
	BYTE     m_ignoreChannelDefault;
	BYTE     m_useServer;
	BYTE     m_DCCFileEnabled;
	BYTE     m_DCCChatEnabled;
	BYTE     m_DCCChatAccept;
	BYTE     m_DCCChatIgnore;
	BYTE     m_DCCPassive;
	BYTE     m_DCCMode;
	WORD     m_DCCPacketSize;
	BYTE     m_manualHost;
	BYTE     m_oldStyleModes;
	BYTE     m_channelAwayNotification;
	BYTE     m_sendNotice;
	BYTE     m_utfAutodetect;
	BYTE     m_bUseSASL;
	int      m_codepage;
	COLORREF colors[16];
	HICON    hIcon[13];

	OBJLIST<CMStringW> vUserhostReasons;
	OBJLIST<CMStringW> vWhoInProgress;

	mir_cs   cs;
	mir_cs   m_gchook;
	mir_cs   m_resolve;
	HANDLE   m_evWndCreate;

	CMStringW m_statusMessage;
	int      m_iTempCheckTime;

	CIrcSessionInfo si;

	int       m_portCount;
	DWORD     m_bConnectRequested;
	DWORD     m_bConnectThreadRunning;

	HGENMENU    hMenuQuick, hMenuServer, hMenuJoin, hMenuNick, hMenuList;
	HNETLIBUSER hNetlibDCC;

	bool  bTempDisableCheck, bTempForceCheck, bEcho;
	bool	nickflag;

	bool     bPerformDone;

	CJoinDlg *m_joinDlg;
	CListDlg *m_listDlg;
	CNickDlg *m_nickDlg;
	CWhoisDlg *m_whoisDlg;
	CQuickDlg *m_quickDlg;
	CManagerDlg *m_managerDlg;
	CIgnorePrefsDlg *m_ignoreDlg;

	int m_noOfChannels, m_manualWhoisCount;
	CMStringA sChannelModes, sUserModes;
	CMStringW sChannelPrefixes, sUserModePrefixes, WhoisAwayReply;

	// clist.cpp
	MCONTACT CList_AddContact(CONTACT *user, bool InList, bool SetOnline);
	bool     CList_SetAllOffline(BYTE ChatsToo);
	MCONTACT CList_SetOffline(CONTACT *user);
	MCONTACT CList_FindContact(CONTACT *user);
	BOOL     CList_AddDCCChat(const CMStringW &name, const CMStringW &hostmask, unsigned long adr, int port);

	// commandmonitor.cpp
	UINT_PTR IdentTimer, InitTimer, KeepAliveTimer, OnlineNotifTimer, OnlineNotifTimer3;

	int  AddOutgoingMessageToDB(MCONTACT hContact, const wchar_t *msg);
	bool DoOnConnect(const CIrcMessage *pmsg);
	int  DoPerform(const char *event);
	void __cdecl ResolveIPThread(void *di);

	bool AddIgnore(const wchar_t *mask, const wchar_t *mode, const wchar_t *network);
	int  IsIgnored(const CMStringW &nick, const CMStringW &address, const CMStringW &host, char type);
	int  IsIgnored(CMStringW user, char type);
	bool RemoveIgnore(const wchar_t *mask);

	// input.cpp
	CMStringW DoAlias(const wchar_t *text, wchar_t *window);
	BOOL      DoHardcodedCommand(CMStringW text, wchar_t *window, MCONTACT hContact);
	CMStringW DoIdentifiers(CMStringW text, const wchar_t *window);
	void      FormatMsg(CMStringW &text);
	bool      PostIrcMessageWnd(wchar_t *pszWindow, MCONTACT hContact, const wchar_t *szBuf);
	bool      PostIrcMessage(const wchar_t *fmt, ...);

	// irclib.cpp
	UINT_PTR	DCCTimer;
	void     SendIrcMessage(const wchar_t*, bool bNotify = true, int codepage = -1);

	// ircproto.cpp
	void __cdecl AckBasicSearch(void* param);
	void __cdecl AckMessageFail(void* info);
	void __cdecl AckMessageFailDcc(void* info);
	void __cdecl AckMessageSuccess(void* info);

	int  SetStatusInternal(int iNewStatus, bool bIsInternal);

	//options.cpp
	HWND m_hwndConnect;

	OBJLIST<CIrcIgnoreItem> m_ignoreItems;

	int       m_channelNumber;
	CMStringW m_whoReply;
	CMStringW sNamesList;
	CMStringW sTopic;
	CMStringW sTopicName;
	CMStringW sTopicTime;
	CMStringW m_namesToWho;
	CMStringW m_channelsToWho;
	CMStringW m_namesToUserhost;

	void    InitPrefs(void);
	void    InitIgnore(void);

	void    ReadSettings(TDbSetting* sets, int count);
	void    RewriteIgnoreSettings(void);
	void    WriteSettings(TDbSetting* sets, int count);

	// output
	BOOL   ShowMessage(const CIrcMessage *pmsg);

	// scripting.cpp
	INT_PTR  __cdecl Scripting_InsertRawIn(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl Scripting_InsertRawOut(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl Scripting_InsertGuiIn(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl Scripting_InsertGuiOut(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl Scripting_GetIrcData(WPARAM wparam, LPARAM lparam);

	// services.cpp
	void   ConnectToServer(void);
	void   DisconnectFromServer(void);

	void __cdecl ConnectServerThread(void*);
	void __cdecl DisconnectServerThread(void*);

	// tools.cpp
	void      AddToJTemp(wchar_t op, CMStringW& sCommand);
	bool      AddWindowItemData(CMStringW window, const wchar_t *pszLimit, const wchar_t *pszMode, const wchar_t *pszPassword, const wchar_t *pszTopic);
	INT_PTR   DoEvent(int iEvent, const wchar_t *pszWindow, const wchar_t *pszNick, const wchar_t *pszText, const wchar_t *pszStatus, const wchar_t *pszUserInfo, DWORD_PTR dwItemData, bool bAddToLog, bool bIsMe, time_t timestamp = 1);
	void      FindLocalIP(HNETLIBCONN con);
	bool      FreeWindowItemData(CMStringW window, CHANNELINFO* wis);
	bool      IsChannel(const char* sName);
	bool      IsChannel(const wchar_t* sName);
	void      KillChatTimer(UINT_PTR &nIDEvent);
	CMStringW MakeWndID(const wchar_t* sWindow);
	CMStringW ModeToStatus(int sMode);
	CMStringW PrefixToStatus(int cPrefix);
	int       SetChannelSBText(CMStringW sWindow, CHANNELINFO *wi);
	void      SetChatTimer(UINT_PTR &nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc);

	void      ClearUserhostReasons(int type);
	void      DoUserhostWithReason(int type, CMStringW reason, bool bSendCommand, const wchar_t *userhostparams, ...);
	CMStringW GetNextUserhostReason(int type);
	CMStringW PeekAtReasons(int type);

	////////////////////////////////////////////////////////////////////////////////////////
	// former CIrcSession class

	void AddDCCSession(MCONTACT hContact, CDccSession *dcc);
	void AddDCCSession(DCCINFO *pdci, CDccSession *dcc);
	void RemoveDCCSession(MCONTACT hContact);
	void RemoveDCCSession(DCCINFO *pdci);

	CDccSession* FindDCCSession(MCONTACT hContact);
	CDccSession* FindDCCSession(DCCINFO *pdci);
	CDccSession* FindDCCSendByPort(int iPort);
	CDccSession* FindDCCRecvByPortAndName(int iPort, const wchar_t *szName);
	CDccSession* FindPassiveDCCSend(int iToken);
	CDccSession* FindPassiveDCCRecv(CMStringW sName, CMStringW sToken);

	void DisconnectAllDCCSessions(bool Shutdown);
	void CheckDCCTimeout(void);

	bool Connect(const CIrcSessionInfo &info);
	void Disconnect(void);
	void KillIdent(void);

	int NLSend(const wchar_t* fmt, ...);
	int NLSend(const char* fmt, ...);
	int NLSend(const unsigned char* buf, int cbBuf);
	int NLSendNoScript(const unsigned char* buf, int cbBuf);
	int NLReceive(unsigned char* buf, int cbBuf);
	void InsertIncomingEvent(wchar_t* pszRaw);

	__inline bool IsConnected() const { return con != nullptr; }

	// send-to-stream operators
	int getCodepage() const;
	__inline void setCodepage(int aPage) { codepage = aPage; }

	CIrcSessionInfo m_info;

protected:
	int codepage;
	HNETLIBCONN con;
	HNETLIBBIND hBindPort;
	void DoReceive();
	LIST<CDccSession> m_dcc_chats;
	LIST<CDccSession> m_dcc_xfers;

private:
	mir_cs m_dcc; // protect the dcc objects

	void createMessageFromPchar(const char* p);
	void Notify(const CIrcMessage *pmsg);
	void __cdecl ThreadProc(void *pparam);

	////////////////////////////////////////////////////////////////////////////////////////
	// former CIrcMonitor class

	bool OnIrc_PING(const CIrcMessage *pmsg);
	bool OnIrc_WELCOME(const CIrcMessage *pmsg);
	bool OnIrc_YOURHOST(const CIrcMessage *pmsg);
	bool OnIrc_NICK(const CIrcMessage *pmsg);
	bool OnIrc_PRIVMSG(const CIrcMessage *pmsg);
	bool OnIrc_JOIN(const CIrcMessage *pmsg);
	bool OnIrc_QUIT(const CIrcMessage *pmsg);
	bool OnIrc_PART(const CIrcMessage *pmsg);
	bool OnIrc_KICK(const CIrcMessage *pmsg);
	bool OnIrc_MODE(const CIrcMessage *pmsg);
	bool OnIrc_USERHOST_REPLY(const CIrcMessage *pmsg);
	bool OnIrc_MODEQUERY(const CIrcMessage *pmsg);
	bool OnIrc_NAMES(const CIrcMessage *pmsg);
	bool OnIrc_ENDNAMES(const CIrcMessage *pmsg);
	bool OnIrc_INITIALTOPIC(const CIrcMessage *pmsg);
	bool OnIrc_INITIALTOPICNAME(const CIrcMessage *pmsg);
	bool OnIrc_TOPIC(const CIrcMessage *pmsg);
	bool OnIrc_TRYAGAIN(const CIrcMessage *pmsg);
	bool OnIrc_NOTICE(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_NAME(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_CHANNELS(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_SERVER(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_AWAY(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_IDLE(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_END(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_OTHER(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_AUTH(const CIrcMessage *pmsg);
	bool OnIrc_WHOIS_NO_USER(const CIrcMessage *pmsg);
	bool OnIrc_NICK_ERR(const CIrcMessage *pmsg);
	bool OnIrc_ENDMOTD(const CIrcMessage *pmsg);
	bool OnIrc_LISTSTART(const CIrcMessage *pmsg);
	bool OnIrc_LIST(const CIrcMessage *pmsg);
	bool OnIrc_LISTEND(const CIrcMessage *pmsg);
	bool OnIrc_BANLIST(const CIrcMessage *pmsg);
	bool OnIrc_BANLISTEND(const CIrcMessage *pmsg);
	bool OnIrc_SUPPORT(const CIrcMessage *pmsg);
	bool OnIrc_BACKFROMAWAY(const CIrcMessage *pmsg);
	bool OnIrc_SETAWAY(const CIrcMessage *pmsg);
	bool OnIrc_JOINERROR(const CIrcMessage *pmsg);
	bool OnIrc_UNKNOWN(const CIrcMessage *pmsg);
	bool OnIrc_ERROR(const CIrcMessage *pmsg);
	bool OnIrc_NOOFCHANNELS(const CIrcMessage *pmsg);
	bool OnIrc_PINGPONG(const CIrcMessage *pmsg);
	bool OnIrc_INVITE(const CIrcMessage *pmsg);
	bool OnIrc_WHO_END(const CIrcMessage *pmsg);
	bool OnIrc_WHO_REPLY(const CIrcMessage *pmsg);
	bool OnIrc_WHOTOOLONG(const CIrcMessage *pmsg);
	bool OnIrc_AUTHENTICATE(const CIrcMessage *pmsg);
	bool OnIrc_AUTH_OK(const CIrcMessage *pmsg);
	bool OnIrc_AUTH_FAIL(const CIrcMessage *pmsg);
	bool OnIrc_CAP(const CIrcMessage *pmsg);

	bool IsCTCP(const CIrcMessage *pmsg);

	void OnIrcDefault(const CIrcMessage *pmsg);
	void OnIrcDisconnected();

	static OBJLIST<CIrcHandler> m_handlers;

	PfnIrcMessageHandler FindMethod(const wchar_t* lpszName);

	void OnIrcMessage(const CIrcMessage *pmsg);
	CMStringW sNick4Perform;
};

struct CMPlugin : public ACCPROTOPLUGIN<CIrcProto>
{
	CMPlugin();

	int Load() override;
	int Unload() override;
};

#endif // _IRCPROTO_H_