/*

Jabber Protocol Plugin for Miranda IM
Copyright (C) 2002-04  Santithorn Bunchua
Copyright (C) 2005-12  George Hazan
Copyright (C) 2007     Maxim Mluhov
Copyright (C) 2012-13  Miranda NG Project

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)(HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession);

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

class CJabberAdhocSession
{
protected:
	CMString m_szSessionId;
	CJabberAdhocSession* m_pNext;
	DWORD m_dwStartTime;

	void* m_pUserData;
	BOOL m_bAutofreeUserData;

	DWORD m_dwStage;
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;
	}
	LPCTSTR GetSessionId()
	{
		return m_szSessionId;
	}
	BOOL SetUserData(void* pUserData, BOOL bAutofree = FALSE)
	{
		if (m_bAutofreeUserData && m_pUserData)
			mir_free(m_pUserData);
		m_pUserData = pUserData;
		m_bAutofreeUserData = bAutofree;
		return TRUE;
	}
	DWORD SetStage(DWORD dwStage)
	{
		DWORD dwRetVal = m_dwStage;
		m_dwStage = dwStage;
		return dwRetVal;
	}
	DWORD GetStage()
	{
		return m_dwStage;
	}
};

class CJabberAdhocNode;
class CJabberAdhocNode
{
protected:
	TCHAR* m_szJid;
	TCHAR* m_szNode;
	TCHAR* m_szName;
	CJabberAdhocNode* m_pNext;
	JABBER_ADHOC_HANDLER m_pHandler;
	CJabberProto* m_pProto;
public:
	CJabberAdhocNode(CJabberProto* pProto, TCHAR* szJid, TCHAR* szNode, TCHAR* szName, JABBER_ADHOC_HANDLER pHandler)
	{
		ZeroMemory(this, sizeof(CJabberAdhocNode));
		replaceStrT(m_szJid, szJid);
		replaceStrT(m_szNode, szNode);
		replaceStrT(m_szName, szName);
		m_pHandler = pHandler;
		m_pProto = pProto;
	}
	~CJabberAdhocNode()
	{
		if (m_szJid)
			mir_free(m_szJid);
		if (m_szNode)
			mir_free(m_szNode);
		if (m_szName)
			mir_free(m_szName);
		if (m_pNext)
			delete m_pNext;
	}
	CJabberAdhocNode* GetNext()
	{
		return m_pNext;
	}
	CJabberAdhocNode* SetNext(CJabberAdhocNode *pNext)
	{
		CJabberAdhocNode *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}
	TCHAR* GetJid()
	{
		return m_szJid;
	}
	TCHAR* GetNode()
	{
		return m_szNode;
	}
	TCHAR* GetName()
	{
		return m_szName;
	}
	BOOL CallHandler(HXML iqNode, CJabberIqInfo* pInfo, CJabberAdhocSession* pSession)
	{
		if (m_pHandler == NULL)
			return FALSE;
		return (m_pProto->*m_pHandler)(iqNode, pInfo, pSession);
	}
};

class CJabberAdhocManager
{
protected:
	CJabberProto* m_pProto;
	CJabberAdhocNode* m_pNodes;
	CJabberAdhocSession* m_pSessions;
	CRITICAL_SECTION m_cs;

	CJabberAdhocSession* FindSession(const TCHAR *szSession)
	{
		CJabberAdhocSession* pSession = m_pSessions;
		while (pSession) {
			if ( !_tcscmp(pSession->GetSessionId(), szSession))
				return pSession;
			pSession = pSession->GetNext();
		}
		return NULL;
	}

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

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

		return pSession;
	}

	CJabberAdhocNode* FindNode(const TCHAR *szNode)
	{
		CJabberAdhocNode* pNode = m_pNodes;
		while (pNode) {
			if ( !_tcscmp(pNode->GetNode(), szNode))
				return pNode;
			pNode = pNode->GetNext();
		}
		return NULL;
	}

	BOOL RemoveSession(CJabberAdhocSession* pSession)
	{
		if ( !m_pSessions)
			return FALSE;

		if (pSession == m_pSessions) {
			m_pSessions = m_pSessions->GetNext();
			pSession->SetNext(NULL);
			delete pSession;
			return TRUE;
		}

		CJabberAdhocSession* pTmp = m_pSessions;
		while (pTmp->GetNext()) {
			if (pTmp->GetNext() == pSession) {
				pTmp->SetNext(pSession->GetNext());
				pSession->SetNext(NULL);
				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(NULL);
			delete pSession;
			return TRUE;
		}

		while (pSession->GetNext()) {
			if (pSession->GetNext()->GetSessionStartTime() < dwExpireTime) {
				CJabberAdhocSession* pRetVal = pSession->GetNext();
				pSession->SetNext(pSession->GetNext()->GetNext());
				pRetVal->SetNext(NULL);
				delete pRetVal;
				return TRUE;
			}
			pSession = pSession->GetNext();
		}
		return FALSE;
	}

public:
	CJabberAdhocManager(CJabberProto* pProto)
	{
		ZeroMemory(this, sizeof(CJabberAdhocManager));
		m_pProto = pProto;
		InitializeCriticalSection(&m_cs);
	}
	~CJabberAdhocManager()
	{
		if (m_pNodes)
			delete m_pNodes;
		if (m_pSessions)
			delete m_pSessions;
		DeleteCriticalSection(&m_cs);
	}
	void Lock()
	{
		EnterCriticalSection(&m_cs);
	}
	void Unlock()
	{
		LeaveCriticalSection(&m_cs);
	}
	BOOL FillDefaultNodes();
	BOOL AddNode(TCHAR* szJid, TCHAR* szNode, TCHAR* szName, JABBER_ADHOC_HANDLER pHandler)
	{
		CJabberAdhocNode* pNode = new CJabberAdhocNode(m_pProto, szJid, szNode, szName, pHandler);
		if ( !pNode)
			return FALSE;

		Lock();
		if ( !m_pNodes)
			m_pNodes = pNode;
		else {
			CJabberAdhocNode* pTmp = m_pNodes;
			while (pTmp->GetNext())
				pTmp = pTmp->GetNext();
			pTmp->SetNext(pNode);
		}
		Unlock();

		return TRUE;
	}
	CJabberAdhocNode* GetFirstNode()
	{
		return m_pNodes;
	}
	BOOL HandleItemsRequest(HXML iqNode, CJabberIqInfo* pInfo, const TCHAR *szNode);
	BOOL HandleInfoRequest(HXML iqNode, CJabberIqInfo* pInfo, const TCHAR *szNode);
	BOOL HandleCommandRequest(HXML iqNode, CJabberIqInfo* pInfo, const TCHAR *szNode);

	BOOL ExpireSessions()
	{
		Lock();
		DWORD dwExpireTime = GetTickCount() - JABBER_ADHOC_SESSION_EXPIRE_TIME;
		while (_ExpireSession(dwExpireTime));
		Unlock();
		return TRUE;
	}
};

#endif //_JABBER_RC_H_