/*

Jabber Protocol Plugin for Miranda NG

Copyright (c) 2002-04  Santithorn Bunchua
Copyright (c) 2005-12  George Hazan
Copyright (c) 2005-07  Maxim Mluhov
Copyright (�) 2012-17 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_DISCO_H_
#define _JABBER_DISCO_H_

#define	CHR_BULLET	((WCHAR)0x2022)
//	#define	STR_BULLET	L" \u2022 "

#define JABBER_DISCO_RESULT_NOT_REQUESTED			0
#define JABBER_DISCO_RESULT_ERROR					-1
#define JABBER_DISCO_RESULT_OK						-2

class CJabberSDIdentity
{
protected:
	wchar_t *m_szCategory;
	wchar_t *m_szType;
	wchar_t *m_szName;
	CJabberSDIdentity *m_pNext;
public:
	CJabberSDIdentity(const wchar_t *szCategory, const wchar_t *szType, const wchar_t *szName)
	{
		m_szCategory = mir_wstrdup(szCategory);
		m_szType = mir_wstrdup(szType);
		m_szName = mir_wstrdup(szName);
		m_pNext = NULL;
	}
	~CJabberSDIdentity()
	{
		mir_free(m_szCategory);
		mir_free(m_szType);
		mir_free(m_szName);
		if (m_pNext)
			delete m_pNext;
	}
	wchar_t *GetCategory()
	{
		return m_szCategory;
	}
	wchar_t *GetType()
	{
		return m_szType;
	}
	wchar_t *GetName()
	{
		return m_szName;
	}
	CJabberSDIdentity* GetNext()
	{
		return m_pNext;
	}
	CJabberSDIdentity* SetNext(CJabberSDIdentity *pNext)
	{
		CJabberSDIdentity *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}
};

class CJabberSDFeature;
class CJabberSDFeature
{
protected:
	wchar_t *m_szVar;
	CJabberSDFeature *m_pNext;
public:
	CJabberSDFeature(const wchar_t *szVar)
	{
		m_szVar = szVar ? mir_wstrdup(szVar) : NULL;
		m_pNext = NULL;
	}
	~CJabberSDFeature()
	{
		mir_free(m_szVar);
		if (m_pNext)
			delete m_pNext;
	}
	wchar_t *GetVar()
	{
		return m_szVar;
	}
	CJabberSDFeature* GetNext()
	{
		return m_pNext;
	}
	CJabberSDFeature* SetNext(CJabberSDFeature *pNext)
	{
		CJabberSDFeature *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}
};

class CJabberSDNode
{
protected:
	wchar_t *m_szJid;
	wchar_t *m_szNode;
	wchar_t *m_szName;
	CJabberSDIdentity *m_pIdentities;
	CJabberSDFeature *m_pFeatures;
	CJabberSDNode *m_pNext;
	CJabberSDNode *m_pChild;
	DWORD m_dwInfoRequestTime;
	DWORD m_dwItemsRequestTime;
	int m_nInfoRequestId;
	int m_nItemsRequestId;
	HTREELISTITEM m_hTreeItem;
	wchar_t *m_szInfoError;
	wchar_t *m_szItemsError;
public:
	CJabberSDNode(const wchar_t *szJid = NULL, const wchar_t *szNode = NULL, const wchar_t *szName = NULL)
	{
		m_szJid = mir_wstrdup(szJid);
		m_szNode = mir_wstrdup(szNode);
		m_szName = mir_wstrdup(szName);
		m_pIdentities = NULL;
		m_pFeatures = NULL;
		m_pNext = NULL;
		m_pChild = NULL;
		m_dwInfoRequestTime = 0;
		m_dwItemsRequestTime = 0;
		m_nInfoRequestId = 0;
		m_nItemsRequestId = 0;
		m_hTreeItem = NULL;
		m_szInfoError = NULL;
		m_szItemsError = NULL;
	}
	~CJabberSDNode()
	{
		RemoveAll();
	}
	BOOL RemoveAll()
	{
		replaceStrW(m_szJid, NULL);
		replaceStrW(m_szNode, NULL);
		replaceStrW(m_szName, NULL);
		replaceStrW(m_szInfoError, NULL);
		replaceStrW(m_szItemsError, NULL);
		if (m_pIdentities)
			delete m_pIdentities;
		m_pIdentities = NULL;
		if (m_pFeatures)
			delete m_pFeatures;
		m_pFeatures = NULL;
		if (m_pNext)
			delete m_pNext;
		m_pNext = NULL;
		if (m_pChild)
			delete m_pChild;
		m_pChild = NULL;
		m_nInfoRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
		m_nItemsRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
		m_dwInfoRequestTime = 0;
		m_dwItemsRequestTime = 0;
		m_hTreeItem = NULL;
		return TRUE;
	}
	BOOL ResetInfo()
	{
		replaceStrW(m_szInfoError, NULL);
		replaceStrW(m_szItemsError, NULL);
		if (m_pIdentities)
			delete m_pIdentities;
		m_pIdentities = NULL;
		if (m_pFeatures)
			delete m_pFeatures;
		m_pFeatures = NULL;
		if (m_pChild)
			delete m_pChild;
		m_pChild = NULL;
		m_nInfoRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
		m_nItemsRequestId = JABBER_DISCO_RESULT_NOT_REQUESTED;
		m_dwInfoRequestTime = 0;
		m_dwItemsRequestTime = 0;
		return TRUE;
	}
	BOOL SetTreeItemHandle(HTREELISTITEM hItem)
	{
		m_hTreeItem = hItem;
		return TRUE;
	}
	HTREELISTITEM GetTreeItemHandle()
	{
		return m_hTreeItem;
	}
	BOOL SetInfoRequestId(int nId)
	{
		m_nInfoRequestId = nId;
		m_dwInfoRequestTime = GetTickCount();
		return TRUE;
	}
	int GetInfoRequestId()
	{
		return m_nInfoRequestId;
	}
	BOOL SetItemsRequestId(int nId)
	{
		m_nItemsRequestId = nId;
		m_dwItemsRequestTime = GetTickCount();
		return TRUE;
	}
	int GetItemsRequestId()
	{
		return m_nItemsRequestId;
	}
	BOOL SetJid(wchar_t *szJid)
	{
		replaceStrW(m_szJid, szJid);
		return TRUE;
	}
	wchar_t *GetJid()
	{
		return m_szJid;
	}
	BOOL SetNode(wchar_t *szNode)
	{
		replaceStrW(m_szNode, szNode);
		return TRUE;
	}
	wchar_t *GetNode()
	{
		return m_szNode;
	}
	wchar_t *GetName()
	{
		return m_szName;
	}
	CJabberSDIdentity* GetFirstIdentity()
	{
		return m_pIdentities;
	}
	CJabberSDFeature* GetFirstFeature()
	{
		return m_pFeatures;
	}
	CJabberSDNode* GetFirstChildNode()
	{
		return m_pChild;
	}
	CJabberSDNode* GetNext()
	{
		return m_pNext;
	}
	CJabberSDNode* SetNext(CJabberSDNode *pNext)
	{
		CJabberSDNode *pRetVal = m_pNext;
		m_pNext = pNext;
		return pRetVal;
	}
	CJabberSDNode* FindByIqId(int nIqId, BOOL bInfoId = TRUE)
	{
		if ((m_nInfoRequestId == nIqId && bInfoId) || (m_nItemsRequestId == nIqId && !bInfoId))
			return this;

		CJabberSDNode *pNode = NULL;
		if (m_pChild && (pNode = m_pChild->FindByIqId(nIqId, bInfoId)))
			return pNode;

		CJabberSDNode *pTmpNode = NULL;
		pNode = m_pNext;
		while (pNode) {
			if ((pNode->m_nInfoRequestId == nIqId && bInfoId) || (pNode->m_nItemsRequestId == nIqId && !bInfoId))
				return pNode;
			if (pNode->m_pChild && (pTmpNode = pNode->m_pChild->FindByIqId(nIqId, bInfoId)))
				return pTmpNode;
			pNode = pNode->GetNext();
		}
		return NULL;
	}
	BOOL AddFeature(const wchar_t *szFeature)
	{
		if (!szFeature)
			return FALSE;

		CJabberSDFeature *pFeature = new CJabberSDFeature(szFeature);
		if (!pFeature)
			return FALSE;

		pFeature->SetNext(m_pFeatures);
		m_pFeatures = pFeature;

		return TRUE;
	}
	BOOL AddIdentity(const wchar_t *szCategory, const wchar_t *szType, const wchar_t *szName)
	{
		if (!szCategory || !szType)
			return FALSE;

		CJabberSDIdentity *pIdentity = new CJabberSDIdentity(szCategory, szType, szName);
		if (!pIdentity)
			return FALSE;

		pIdentity->SetNext(m_pIdentities);
		m_pIdentities = pIdentity;

		return TRUE;
	}
	BOOL AddChildNode(const wchar_t *szJid, const wchar_t *szNode, const wchar_t *szName)
	{
		if (!szJid)
			return FALSE;

		CJabberSDNode *pNode = new CJabberSDNode(szJid, szNode, szName);
		if (!pNode)
			return FALSE;

		pNode->SetNext(m_pChild);
		m_pChild = pNode;
		return TRUE;
	}

	BOOL SetItemsRequestErrorText(wchar_t *szError)
	{
		replaceStrW(m_szItemsError, szError);
		return TRUE;
	}

	BOOL SetInfoRequestErrorText(wchar_t *szError)
	{
		replaceStrW(m_szInfoError, szError);
		return TRUE;
	}

	BOOL GetTooltipText(wchar_t *szText, int nMaxLength)
	{
		CMStringW tszTmp;

		tszTmp.AppendFormat(L"Jid: %s\r\n", m_szJid);

		if (m_szNode)
			tszTmp.AppendFormat(L"%s: %s\r\n", TranslateT("Node"), m_szNode);

		if (m_pIdentities) {
			tszTmp.AppendFormat(L"\r\n%s:\r\n", TranslateT("Identities"));

			CJabberSDIdentity *pIdentity = m_pIdentities;
			while (pIdentity) {
				if (pIdentity->GetName())
					tszTmp.AppendFormat(L" %c %s (%s: %s, %s: %s)\r\n",
						CHR_BULLET, pIdentity->GetName(),
							TranslateT("category"), pIdentity->GetCategory(),
							TranslateT("type"), pIdentity->GetType());
				else
					tszTmp.AppendFormat(L" %c %s: %s, %s: %s\r\n",
						CHR_BULLET,
						TranslateT("Category"), pIdentity->GetCategory(),
						TranslateT("Type"), pIdentity->GetType());

				pIdentity = pIdentity->GetNext();
			}
		}

		if (m_pFeatures) {
			tszTmp.AppendFormat(L"\r\n%s:\r\n", TranslateT("Supported features"));

			for (CJabberSDFeature *pFeature = m_pFeatures; pFeature; pFeature = pFeature->GetNext())
				tszTmp.AppendFormat(L" %c %s\r\n", CHR_BULLET, pFeature->GetVar());
		}

		if (m_szInfoError)
			tszTmp.AppendFormat(L"\r\n%s: %s\r\n", TranslateT("Info request error"), m_szInfoError);

		if (m_szItemsError)
			tszTmp.AppendFormat(L"\r\n%s: %s\r\n", TranslateT("Items request error"), m_szItemsError);

		tszTmp.TrimRight();
		wcsncpy_s(szText, nMaxLength, tszTmp, _TRUNCATE);
		return TRUE;
	}
};

class CJabberSDManager
{
protected:
	mir_cs m_cs;
	CJabberSDNode *m_pPrimaryNodes;

public:
	CJabberSDManager()
	{
		m_pPrimaryNodes = NULL;
	}

	~CJabberSDManager()
	{
		RemoveAll();
	}

	mir_cs& cs() { return m_cs; }

	void RemoveAll()
	{
		delete m_pPrimaryNodes;
		m_pPrimaryNodes = NULL;
	}

	CJabberSDNode* GetPrimaryNode()
	{
		return m_pPrimaryNodes;
	}

	CJabberSDNode* AddPrimaryNode(const wchar_t *szJid, const wchar_t *szNode, const wchar_t *szName)
	{
		if (!szJid)
			return NULL;

		CJabberSDNode *pNode = new CJabberSDNode(szJid, szNode, szName);
		if (!pNode)
			return NULL;

		pNode->SetNext(m_pPrimaryNodes);
		m_pPrimaryNodes = pNode;
		return pNode;
	}

	CJabberSDNode* FindByIqId(int nIqId, BOOL bInfoId = TRUE)
	{
		for (CJabberSDNode *pNode = m_pPrimaryNodes; pNode; pNode = pNode->GetNext())
			if (CJabberSDNode *pTmpNode = pNode->FindByIqId(nIqId, bInfoId))
				return pTmpNode;

		return NULL;
	}
};

#undef STR_BULLET // used for formatting

#endif // _JABBER_DISCO_H_