/*

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_XML_H_
#define _JABBER_XML_H_

#include <m_xml.h>

void    __fastcall xmlAddChild(HXML, HXML);
HXML    __fastcall xmlAddChild(HXML, LPCTSTR pszName);
HXML    __fastcall xmlAddChild(HXML, LPCTSTR pszName, LPCTSTR ptszValue);
HXML    __fastcall xmlAddChild(HXML, LPCTSTR pszName, int iValue);

LPCTSTR __fastcall xmlGetAttrValue(HXML, LPCTSTR key);
HXML    __fastcall xmlGetChild(HXML, int n = 0);
HXML    __fastcall xmlGetChild(HXML, LPCSTR key);
HXML    __fastcall xmlGetChild(HXML, LPCTSTR key);
int     __fastcall xmlGetChildCount(HXML);
HXML    __fastcall xmlGetChildByTag(HXML, LPCTSTR key, LPCTSTR attrName, LPCTSTR attrValue);
HXML    __fastcall xmlGetChildByTag(HXML, LPCSTR key, LPCSTR attrName, LPCTSTR attrValue);
HXML    __fastcall xmlGetNthChild(HXML, LPCTSTR key, int n = 0);

LPCTSTR __fastcall xmlGetName(HXML);
LPCTSTR __fastcall xmlGetText(HXML);

void    __fastcall xmlAddAttr(HXML, LPCTSTR pszName, LPCTSTR ptszValue);
void    __fastcall xmlAddAttr(HXML, LPCTSTR pszName, int value);
void    __fastcall xmlAddAttr(HXML hXml, LPCTSTR pszName, unsigned __int64 value);
void    __fastcall xmlAddAttrID(HXML, int id);

int     __fastcall xmlGetAttrCount(HXML);
LPCTSTR __fastcall xmlGetAttr(HXML, int n);
LPCTSTR __fastcall xmlGetAttrName(HXML, int n);
LPCTSTR __fastcall xmlGetAttrValue(HXML, LPCTSTR key);

struct XmlNode
{
	__forceinline XmlNode() { m_hXml = NULL; }

	__forceinline XmlNode(LPCTSTR pszString, int* numBytes, LPCTSTR ptszTag)
	{
		m_hXml = xi.parseString(pszString, numBytes, ptszTag);
	}

	XmlNode(const XmlNode& n);
	XmlNode(LPCTSTR name);
	XmlNode(LPCTSTR pszName, LPCTSTR ptszText);
	~XmlNode();

	XmlNode& operator =(const XmlNode& n);

	__forceinline operator HXML() const
	{	return m_hXml;
	}

private:
	HXML m_hXml;
};

class CJabberIqInfo;

struct XmlNodeIq : public XmlNode
{
	XmlNodeIq(const TCHAR *type, int id = -1, const TCHAR *to = NULL);
	XmlNodeIq(const TCHAR *type, const TCHAR *idStr, const TCHAR *to);
	XmlNodeIq(const TCHAR *type, HXML node, const TCHAR *to);
	// new request
	XmlNodeIq(CJabberIqInfo* pInfo);
	// answer to request
	XmlNodeIq(const TCHAR *type, CJabberIqInfo* pInfo);
};

typedef void (*JABBER_XML_CALLBACK)(HXML, void*);

/////////////////////////////////////////////////////////////////////////////////////////

struct XATTR
{
	LPCTSTR name, value;

	__forceinline XATTR(LPCTSTR _name, LPCTSTR _value) :
		name(_name),
		value(_value)
		{}
};

HXML __forceinline operator<<(HXML node, const XATTR& attr)
{	xmlAddAttr(node, attr.name, attr.value);
	return node;
}

/////////////////////////////////////////////////////////////////////////////////////////

struct XATTRI
{
	LPCTSTR name;
	int value;

	__forceinline XATTRI(LPCTSTR _name, int _value) :
		name(_name),
		value(_value)
		{}
};

HXML __forceinline operator<<(HXML node, const XATTRI& attr)
{	xmlAddAttr(node, attr.name, attr.value);
	return node;
}

/////////////////////////////////////////////////////////////////////////////////////////

struct XATTRI64
{
	LPCTSTR name;
	unsigned __int64 value;

	__forceinline XATTRI64(LPCTSTR _name, unsigned __int64 _value) :
		name(_name),
		value(_value)
		{}
};

HXML __forceinline operator<<(HXML node, const XATTRI64& attr)
{	xmlAddAttr(node, attr.name, attr.value);
	return node;
}

/////////////////////////////////////////////////////////////////////////////////////////

struct XATTRID
{
	int id;

	__forceinline XATTRID(int _value) :
		id(_value)
		{}
};

HXML __forceinline operator<<(HXML node, const XATTRID& attr)
{	xmlAddAttrID(node, attr.id);
	return node;
}

/////////////////////////////////////////////////////////////////////////////////////////

struct XCHILD
{
	LPCTSTR name, value;

	__forceinline XCHILD(LPCTSTR _name, LPCTSTR _value = NULL) :
		name(_name),
		value(_value)
		{}
};

HXML __forceinline operator<<(HXML node, const XCHILD& child)
{	return xmlAddChild(node, child.name, child.value);
}

/////////////////////////////////////////////////////////////////////////////////////////

struct XCHILDNS
{
	LPCTSTR name, ns;

	__forceinline XCHILDNS(LPCTSTR _name, LPCTSTR _ns = NULL) :
		name(_name),
		ns(_ns)
		{}
};

HXML __fastcall operator<<(HXML node, const XCHILDNS& child);

/////////////////////////////////////////////////////////////////////////////////////////

struct XQUERY
{
	LPCTSTR ns;

	__forceinline XQUERY(LPCTSTR _ns) :
		ns(_ns)
		{}
};

HXML __fastcall operator<<(HXML node, const XQUERY& child);

/////////////////////////////////////////////////////////////////////////////////////////
// Limited XPath support
//     path should look like: "node-spec/node-spec/.../result-spec"
//     where "node-spec" can be "node-name", "node-name[@attr-name='attr-value']" or "node-name[node-index]"
//     result may be either "node-spec", or "@attr-name"
//
// Samples:
//    LPCTSTR s = XPathT(node, "child/subchild[@attr='value']");          // get node text
//    LPCTSTR s = XPathT(node, "child/subchild[2]/@attr");                // get attribute value
//    XPathT(node, "child/subchild[@name='test']/@attr") = _T("Hello");   // create path if needed and set attribute value
//
//    XPathT(node, "child/subchild[@name='test']") = _T("Hello");         // TODO: create path if needed and set node text

#define XPathT(a,b) XPath(a, _T(b))

class XPath
{
public:
	__forceinline XPath(HXML hXml, TCHAR *path):
		m_type(T_UNKNOWN),
		m_hXml(hXml),
		m_szPath(path),
		m_szParam(NULL)
		{}

	// Read data
	operator HXML()
	{
		switch (Lookup())
		{
			case T_NODE: return m_hXml;
			case T_NODESET: return xmlGetNthChild(m_hXml, m_szParam, 1);
		}
		return NULL;
	}
	operator LPTSTR()
	{
		switch (Lookup())
		{
			case T_ATTRIBUTE: return (TCHAR *)xmlGetAttrValue(m_hXml, m_szParam);
			case T_NODE: return (TCHAR *)xmlGetText(m_hXml);
			case T_NODESET: return (TCHAR *)xmlGetText(xmlGetNthChild(m_hXml, m_szParam, 1));
		}
		return NULL;
	}
	operator int()
	{
		if (TCHAR *s = *this) return _ttoi(s);
		return 0;
	}
	__forceinline bool operator== (TCHAR *str)
	{
		return !lstrcmp((LPCTSTR)*this, str);
	}
	__forceinline bool operator!= (TCHAR *str)
	{
		return lstrcmp((LPCTSTR)*this, str) ? true : false;
	}
	HXML operator[] (int idx)
	{
		return (Lookup() == T_NODESET) ? xmlGetNthChild(m_hXml, m_szParam, idx) : NULL;
	}

	// Write data
	void operator= (LPCTSTR value)
	{
		switch (Lookup(true))
		{
			case T_ATTRIBUTE: xmlAddAttr(m_hXml, m_szParam, value); break;
			case T_NODE: break; // TODO: set node text
		}
	}
	void operator= (int value)
	{
		TCHAR buf[16];
		_itot(value, buf, 10);
		*this = buf;
	}

private:
	enum PathType
	{
		T_UNKNOWN,
		T_ERROR,
		T_NODE,
		T_ATTRIBUTE,
		T_NODESET
	};

	__forceinline PathType Lookup(bool bCreate=false)
	{
		return (m_type == T_UNKNOWN) ? LookupImpl(bCreate) : m_type;
	}

	enum LookupState
	{
		S_START,
		S_ATTR_STEP,
		S_NODE_NAME,
		S_NODE_OPENBRACKET,
		S_NODE_INDEX,
		S_NODE_ATTRNAME,
		S_NODE_ATTREQUALS,
		S_NODE_ATTRVALUE,
		S_NODE_ATTRCLOSEVALUE,
		S_NODE_CLOSEBRACKET,

		S_FINAL,
		S_FINAL_ERROR,
		S_FINAL_ATTR,
		S_FINAL_NODESET,
		S_FINAL_NODE
	};

	struct LookupString
	{
		void Begin(const TCHAR *p_) { p = p_; }
		void End(const TCHAR *p_) { length = p_ - p; }
		operator bool() { return p ? true : false; }

		const TCHAR *p;
		int length;

	};

	struct LookupInfo
	{
		void Reset() { memset(this, 0, sizeof(*this)); }
		LookupString nodeName;
		LookupString attrName;
		LookupString attrValue;
		LookupString nodeIndex;
	};

	void ProcessPath(LookupInfo &info, bool bCreate);
	PathType LookupImpl(bool bCreate);

	PathType m_type;
	HXML m_hXml;
	LPCTSTR m_szPath;
	LPCTSTR m_szParam;
};

class XPathFmt: public XPath
{
public:
	enum { BUFSIZE = 512 };
	XPathFmt(HXML hXml, TCHAR *path, ...): XPath(hXml, m_buf)
	{
		*m_buf = 0;

		va_list args;
		va_start(args, path);
		_vsntprintf(m_buf, BUFSIZE, path, args);
		m_buf[BUFSIZE-1] = 0;
		va_end(args);
	}

	XPathFmt(HXML hXml, char *path, ...): XPath(hXml, m_buf)
	{
		*m_buf = 0;
		char buf[BUFSIZE];

		va_list args;
		va_start(args, path);
		_vsnprintf(buf, BUFSIZE, path, args);
		buf[BUFSIZE-1] = 0;
		MultiByteToWideChar(CP_ACP, 0, buf, -1, m_buf, BUFSIZE);
		va_end(args);
	}

private:
	TCHAR m_buf[BUFSIZE];
};

#endif