/*

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

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_