/*

Jabber Protocol Plugin for Miranda NG

Copyright (c) 2002-04  Santithorn Bunchua
Copyright (c) 2005-12  George Hazan
Copyright (c) 2007     Maxim Mluhov
Copyright (C) 2012-20 Miranda NG team

XEP-0146 support for Miranda IM

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 _JABBER_RC_H_
#define _JABBER_RC_H_

class CJabberAdhocSession;

#define JABBER_ADHOC_HANDLER_STATUS_EXECUTING            1
#define JABBER_ADHOC_HANDLER_STATUS_COMPLETED            2
#define JABBER_ADHOC_HANDLER_STATUS_CANCEL               3
#define JABBER_ADHOC_HANDLER_STATUS_REMOVE_SESSION       4
typedef int (CJabberProto::*JABBER_ADHOC_HANDLER)(const TiXmlElement *iqNode, CJabberIqInfo *pInfo, CJabberAdhocSession *pSession);

// 5 minutes to fill out form :)
#define JABBER_ADHOC_SESSION_EXPIRE_TIME                 300000

class CJabberAdhocSession
{
protected:
	CMStringA m_szSessionId;
	CJabberAdhocSession* m_pNext = nullptr;
	DWORD m_dwStartTime;

	void* m_pUserData = nullptr;
	BOOL m_bAutofreeUserData;

	DWORD m_dwStage = 0;

public:
	CJabberProto *ppro;
	CJabberAdhocSession(CJabberProto* global);
	~CJabberAdhocSession()
	{
		if (m_bAutofreeUserData && m_pUserData)
			mir_free(m_pUserData);
		if (m_pNext)
			delete m_pNext;
	}

	CJabberAdhocSession* GetNext()
	{
		return m_pNext;
	}

	CJabberAdhocSession* SetNext(CJabberAdhocSession *pNext)
	{
		CJabberAdhocSession *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}

	DWORD GetSessionStartTime()
	{
		return m_dwStartTime;
	}

	const char* GetSessionId()
	{
		return m_szSessionId;
	}
	
	void SetUserData(void* pUserData, BOOL bAutofree = FALSE)
	{
		if (m_bAutofreeUserData && m_pUserData)
			mir_free(m_pUserData);
		m_pUserData = pUserData;
		m_bAutofreeUserData = bAutofree;
	}

	DWORD SetStage(DWORD dwStage)
	{
		DWORD dwRetVal = m_dwStage;
		m_dwStage = dwStage;
		return dwRetVal;
	}

	DWORD GetStage()
	{
		return m_dwStage;
	}
};

class CJabberAdhocNode
{
protected:
	char *m_szJid;
	char *m_szNode;
	char *m_szName;
	CJabberAdhocNode* m_pNext = nullptr;
	JABBER_ADHOC_HANDLER m_pHandler;
	CJabberProto *m_pProto;

public:
	CJabberAdhocNode(CJabberProto* pProto, const char *szJid, const char *szNode, const char *szName, JABBER_ADHOC_HANDLER pHandler) :
		m_pProto(pProto),
		m_pHandler(pHandler)
	{
		m_szJid = mir_strdup(szJid);
		m_szNode = mir_strdup(szNode);
		m_szName = mir_strdup(szName);
	}
	
	~CJabberAdhocNode()
	{
		mir_free(m_szJid);
		mir_free(m_szNode);
		mir_free(m_szName);

		if (m_pNext)
			delete m_pNext;
	}
	
	__inline CJabberAdhocNode* GetNext() const
	{
		return m_pNext;
	}
	
	__inline CJabberAdhocNode* SetNext(CJabberAdhocNode *pNext)
	{
		CJabberAdhocNode *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}
	
	__inline char* GetJid() const {
		return m_szJid;
	}
	__inline char* GetNode() const {
		return m_szNode;
	}
	__inline char* GetName() const {
		return m_szName;
	}
	
	bool CallHandler(const TiXmlElement *iqNode, CJabberIqInfo *pInfo, CJabberAdhocSession *pSession)
	{
		if (m_pHandler == nullptr)
			return false;
		return (m_pProto->*m_pHandler)(iqNode, pInfo, pSession);
	}
};

class CJabberAdhocManager
{
protected:
	CJabberProto *m_pProto;
	CJabberAdhocNode* m_pNodes = nullptr;
	CJabberAdhocSession* m_pSessions = nullptr;
	mir_cs m_cs;

	CJabberAdhocSession* FindSession(const char *szSession)
	{
		CJabberAdhocSession *pSession = m_pSessions;
		while (pSession) {
			if (!mir_strcmp(pSession->GetSessionId(), szSession))
				return pSession;
			pSession = pSession->GetNext();
		}
		return nullptr;
	}

	CJabberAdhocSession* AddNewSession()
	{
		CJabberAdhocSession *pSession = new CJabberAdhocSession(m_pProto);
		if (!pSession)
			return nullptr;

		pSession->SetNext(m_pSessions);
		m_pSessions = pSession;

		return pSession;
	}

	CJabberAdhocNode* FindNode(const char *szNode)
	{
		CJabberAdhocNode* pNode = m_pNodes;
		while (pNode) {
			if (!mir_strcmp(pNode->GetNode(), szNode))
				return pNode;
			pNode = pNode->GetNext();
		}
		return nullptr;
	}

	bool RemoveSession(CJabberAdhocSession *pSession)
	{
		if (!m_pSessions)
			return false;

		if (pSession == m_pSessions) {
			m_pSessions = m_pSessions->GetNext();
			pSession->SetNext(nullptr);
			delete pSession;
			return true;
		}

		CJabberAdhocSession* pTmp = m_pSessions;
		while (pTmp->GetNext()) {
			if (pTmp->GetNext() == pSession) {
				pTmp->SetNext(pSession->GetNext());
				pSession->SetNext(nullptr);
				delete pSession;
				return true;
			}
			pTmp = pTmp->GetNext();
		}
		return false;
	}

	bool _ExpireSession(DWORD dwExpireTime)
	{
		if (!m_pSessions)
			return false;

		CJabberAdhocSession *pSession = m_pSessions;
		if (pSession->GetSessionStartTime() < dwExpireTime) {
			m_pSessions = pSession->GetNext();
			pSession->SetNext(nullptr);
			delete pSession;
			return true;
		}

		while (pSession->GetNext()) {
			if (pSession->GetNext()->GetSessionStartTime() < dwExpireTime) {
				CJabberAdhocSession* pRetVal = pSession->GetNext();
				pSession->SetNext(pSession->GetNext()->GetNext());
				pRetVal->SetNext(nullptr);
				delete pRetVal;
				return true;
			}
			pSession = pSession->GetNext();
		}
		return false;
	}

public:
	CJabberAdhocManager(CJabberProto* pProto) :
		m_pProto(pProto)
	{		 
	}

	~CJabberAdhocManager()
	{
		delete m_pNodes;
		delete m_pSessions;
	}

	void FillDefaultNodes();
	
	void AddNode(const char *szJid, const char *szNode, const char *szName, JABBER_ADHOC_HANDLER pHandler)
	{
		CJabberAdhocNode* pNode = new CJabberAdhocNode(m_pProto, szJid, szNode, szName, pHandler);

		mir_cslock lck(m_cs);
		if (!m_pNodes)
			m_pNodes = pNode;
		else {
			CJabberAdhocNode* pTmp = m_pNodes;
			while (pTmp->GetNext())
				pTmp = pTmp->GetNext();
			pTmp->SetNext(pNode);
		}
	}

	CJabberAdhocNode* GetFirstNode()
	{
		return m_pNodes;
	}

	bool HandleItemsRequest(const TiXmlElement *iqNode, CJabberIqInfo *pInfo, const char *szNode);
	bool HandleInfoRequest(const TiXmlElement *iqNode, CJabberIqInfo *pInfo, const char *szNode);
	bool HandleCommandRequest(const TiXmlElement *iqNode, CJabberIqInfo *pInfo, const char *szNode);

	void ExpireSessions()
	{
		mir_cslock lck(m_cs);
		DWORD dwExpireTime = GetTickCount() - JABBER_ADHOC_SESSION_EXPIRE_TIME;
		while (_ExpireSession(dwExpireTime))
			;
	}
};

#endif //_JABBER_RC_H_