/*

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

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_IQ_H_
#define _JABBER_IQ_H_

#include "jabber_xml.h"

class CJabberIqInfo;

struct CJabberProto;
typedef void (*IQ_USER_DATA_FREE_FUNC)(void *pUserData);

// 2 minutes, milliseconds
#define JABBER_DEFAULT_IQ_REQUEST_TIMEOUT		120000

typedef void (CJabberProto::*JABBER_IQ_HANDLER)(HXML iqNode, CJabberIqInfo *pInfo);
typedef BOOL (CJabberProto::*JABBER_PERMANENT_IQ_HANDLER)(HXML iqNode, CJabberIqInfo *pInfo);

#define JABBER_IQ_PARSE_CHILD_TAG_NODE  (1)
#define JABBER_IQ_PARSE_CHILD_TAG_NAME  ((1<<1)|JABBER_IQ_PARSE_CHILD_TAG_NODE)
#define JABBER_IQ_PARSE_CHILD_TAG_XMLNS ((1<<2)|JABBER_IQ_PARSE_CHILD_TAG_NODE)
#define JABBER_IQ_PARSE_FROM             (1<<3)
#define JABBER_IQ_PARSE_HCONTACT        ((1<<4)|JABBER_IQ_PARSE_FROM)
#define JABBER_IQ_PARSE_TO               (1<<5)
#define JABBER_IQ_PARSE_ID_STR           (1<<6)

#define JABBER_IQ_PARSE_DEFAULT (JABBER_IQ_PARSE_CHILD_TAG_NODE | JABBER_IQ_PARSE_CHILD_TAG_NAME | JABBER_IQ_PARSE_CHILD_TAG_XMLNS)

class CJabberIqInfo : public MZeroedObject
{
protected:
	friend class CJabberIqManager;
	JABBER_IQ_HANDLER m_pHandler;
	CJabberIqInfo *m_pNext;

	int    m_nIqId;
	DWORD  m_dwParamsToParse;
	DWORD  m_dwRequestTime;
	DWORD  m_dwTimeout;
	TCHAR *m_szReceiver;
	int    m_iPriority;

public:
	void  *m_pUserData;
	int    m_nIqType;
	TCHAR *m_szFrom;
	TCHAR *m_szChildTagXmlns;
	TCHAR *m_szChildTagName;
	HXML   m_pChildNode;
	HANDLE m_hContact;
	TCHAR *m_szTo;
	TCHAR *m_szId;

public:
	__forceinline void SetReceiver(const TCHAR *szReceiver)
	{	replaceStrT(m_szReceiver, szReceiver);
	}
	__forceinline TCHAR* GetReceiver()
	{	return m_szReceiver;
	}
	__forceinline void SetParamsToParse(DWORD dwParamsToParse)
	{	m_dwParamsToParse = dwParamsToParse;
	}
	__forceinline void SetTimeout(DWORD dwTimeout)
	{	m_dwTimeout = dwTimeout;
	}
	__forceinline int GetIqId()
	{	return m_nIqId;
	}
	__forceinline DWORD GetRequestTime()
	{	return m_dwRequestTime;
	}
	__forceinline int GetIqType()
	{	return m_nIqType;
	}
	__forceinline void* GetUserData()
	{	return m_pUserData;
	}
	__forceinline TCHAR* GetFrom()
	{	return m_szFrom;
	}
	__forceinline TCHAR* GetTo()
	{	return m_szTo;
	}
	__forceinline TCHAR* GetIdStr()
	{	return m_szId;
	}
	__forceinline HANDLE GetHContact()
	{	return m_hContact;
	}
	__forceinline HXML GetChildNode()
	{	return m_pChildNode;
	}
	__forceinline TCHAR* GetChildNodeName()
	{	return m_szChildTagName;
	}
	
	char* GetCharIqType()
	{
		switch (m_nIqType) {
			case JABBER_IQ_TYPE_SET: return "set";
			case JABBER_IQ_TYPE_GET: return "get";
			case JABBER_IQ_TYPE_ERROR: return "error";
			case JABBER_IQ_TYPE_RESULT: return "result";
		}
		return NULL;
	}
};

class CJabberIqPermanentInfo : public MZeroedObject
{
	friend class CJabberIqManager;

	CJabberIqPermanentInfo *m_pNext;

	JABBER_PERMANENT_IQ_HANDLER m_pHandler;
	DWORD m_dwParamsToParse;
	int m_nIqTypes;
	TCHAR *m_szXmlns;
	TCHAR *m_szTag;
	BOOL m_bAllowPartialNs;
	void *m_pUserData;
	IQ_USER_DATA_FREE_FUNC m_pUserDataFree;
	int m_iPriority;

public:
	~CJabberIqPermanentInfo()
	{
		if (m_pUserDataFree)
			m_pUserDataFree(m_pUserData);
		mir_free(m_szXmlns);
		mir_free(m_szTag);
	}
};

class CJabberIqManager
{
protected:
	CJabberProto *ppro;
	CRITICAL_SECTION m_cs;
	DWORD m_dwLastUsedHandle;
	CJabberIqInfo *m_pIqs; // list of iqs ordered by priority
	HANDLE m_hExpirerThread;
	BOOL m_bExpirerThreadShutdownRequest;

	CJabberIqPermanentInfo* m_pPermanentHandlers;

	CJabberIqInfo* DetachInfo(int nIqId);
	CJabberIqInfo* DetachInfo(void *pUserData);
	CJabberIqInfo* DetachExpired();

	void ExpireInfo(CJabberIqInfo *pInfo, void *pUserData = NULL);
	
	// inserts pInfo at a place determined by pInfo->m_iPriority
	BOOL InsertIq(CJabberIqInfo *pInfo);

public:
	CJabberIqManager(CJabberProto* proto);
	~CJabberIqManager();

	BOOL Start();
	BOOL Shutdown();

	// fucking params, maybe just return CJabberIqRequestInfo pointer ?
	CJabberIqInfo* AddHandler(JABBER_IQ_HANDLER pHandler, int nIqType, const TCHAR *szReceiver, DWORD dwParamsToParse, int nIqId, void *pUserData, int iPriority);
	CJabberIqPermanentInfo* AddPermanentHandler(JABBER_PERMANENT_IQ_HANDLER pHandler, int nIqTypes, DWORD dwParamsToParse, const TCHAR *szXmlns, BOOL bAllowPartialNs, const TCHAR *szTag, void *pUserData = NULL, IQ_USER_DATA_FREE_FUNC pUserDataFree = NULL, int iPriority = JH_PRIORITY_DEFAULT);

	// returns TRUE when pInfo found, or FALSE otherwise
	BOOL DeletePermanentHandler(CJabberIqPermanentInfo *pInfo);
	BOOL DeleteHandler(CJabberIqInfo *pInfo);

	BOOL HandleIq(int nIqId, HXML pNode);
	BOOL HandleIqPermanent(HXML pNode);

	BOOL ExpireIq(int nIqId);
	void ExpirerThread(void);
	BOOL ExpireByUserData(void *pUserData);
	BOOL ExpireAll(void *pUserData = NULL);
	BOOL FillPermanentHandlers();
};

#endif