/*

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

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.

*/

#include "jabber.h"
#include "jabber_caps.h"

#include "jabber_form2.h"


/////////////////////////////////////////////////////////////////////////////////////////
// FORM_TYPE Registry
namespace NSJabberRegistry
{
	// http://jabber.org/network/serverinfo
	static TJabberDataFormRegisry_Field form_type_serverinfo[] =
	{
		{ _T("abuse-addresses"),		JDFT_LIST_MULTI,		_T("One or more addresses for communication related to abusive traffic") },
		{ _T("feedback-addresses"),		JDFT_LIST_MULTI,		_T("One or more addresses for customer feedback") },
		{ _T("sales-addresses"),		JDFT_LIST_MULTI,		_T("One or more addresses for communication related to sales and marketing") },
		{ _T("security-addresses"),		JDFT_LIST_MULTI,		_T("One or more addresses for communication related to security concerns") },
		{ _T("support-addresses"),		JDFT_LIST_MULTI,		_T("One or more addresses for customer support") },
	};

	// http://jabber.org/protocol/admin
	static TJabberDataFormRegisry_Field form_type_admin[] =
	{
		{ _T("accountjid"),				JDFT_JID_SINGLE,	_T("The Jabber ID of a single entity to which an operation applies") },
		{ _T("accountjids"),			JDFT_JID_MULTI,		_T("The Jabber ID of one or more entities to which an operation applies") },
		{ _T("activeuserjids"),			JDFT_JID_MULTI,		_T("The Jabber IDs associated with active sessions") },
		{ _T("activeusersnum"),			JDFT_TEXT_SINGLE,	_T("The number of online entities that are active") },
		{ _T("adminjids"),				JDFT_JID_MULTI,		_T("A list of entities with administrative privileges") },
		{ _T("announcement"),			JDFT_TEXT_MULTI,	_T("The text of an announcement to be sent to active users or all users") },
		{ _T("blacklistjids"),			JDFT_JID_MULTI,		_T("A list of entities with whom communication is blocked") },
		{ _T("delay"),					JDFT_LIST_MULTI,	_T("The number of seconds to delay before applying a change") },
		{ _T("disableduserjids"),		JDFT_JID_MULTI,		_T("The Jabber IDs that have been disabled") },
		{ _T("disabledusersnum"),		JDFT_TEXT_SINGLE,	_T("The number of disabled entities") },
		{ _T("email"),					JDFT_TEXT_SINGLE,	_T("The email address for a user") },
		{ _T("given_name"),				JDFT_TEXT_SINGLE,	_T("The given (first) name of a user") },
		{ _T("idleusersnum"),			JDFT_TEXT_SINGLE,	_T("The number of online entities that are idle") },
		{ _T("ipaddresses"),			JDFT_LIST_MULTI,	_T("The IP addresses of an account's online sessions") },
		{ _T("lastlogin"),				JDFT_TEXT_SINGLE,	_T("The last login time (per XEP-0082) of a user") },
		{ _T("loginsperminute"),		JDFT_TEXT_SINGLE,	_T("The number of logins per minute for an account") },
		{ _T("max_items"),				JDFT_LIST_SINGLE,	_T("The maximum number of items associated with a search or list") },
		{ _T("motd"),					JDFT_TEXT_MULTI,	_T("The text of a message of the day") },
		{ _T("onlineresources"),		JDFT_TEXT_SINGLE,	_T("The names of an account's online sessions") },
		{ _T("onlineuserjids"),			JDFT_JID_MULTI,		_T("The Jabber IDs associated with online users") },
		{ _T("onlineusersnum"),			JDFT_TEXT_SINGLE,	_T("The number of online entities") },
		{ _T("password"),				JDFT_TEXT_PRIVATE,	_T("The password for an account") },
		{ _T("password-verify"),		JDFT_TEXT_PRIVATE,	_T("Password verification") },
		{ _T("registereduserjids"),		JDFT_JID_MULTI,		_T("A list of registered entities") },
		{ _T("registeredusersnum"),		JDFT_TEXT_SINGLE,	_T("The number of registered entities") },
		{ _T("rostersize"),				JDFT_TEXT_SINGLE,	_T("Number of roster items for an account") },
		{ _T("stanzaspersecond"),		JDFT_TEXT_SINGLE,	_T("The number of stanzas being sent per second by an account") },
		{ _T("surname"),				JDFT_TEXT_SINGLE,	_T("The family (last) name of a user") },
		{ _T("welcome"),				JDFT_TEXT_MULTI,	_T("The text of a welcome message") },
		{ _T("whitelistjids"),			JDFT_JID_MULTI,		_T("A list of entities with whom communication is allowed") },
	};

	// http://jabber.org/protocol/muc#register
	static TJabberDataFormRegisry_Field form_type_muc_register[] =
	{
		{ _T("muc#register_first"),		JDFT_TEXT_SINGLE,	_T("First Name") },
		{ _T("muc#register_last"),		JDFT_TEXT_SINGLE,	_T("Last Name") },
		{ _T("muc#register_roomnick"),	JDFT_TEXT_SINGLE,	_T("Desired Nickname") },
		{ _T("muc#register_url"),		JDFT_TEXT_SINGLE,	_T("Your URL") },
		{ _T("muc#register_email"),		JDFT_TEXT_SINGLE,	_T("Email Address") },
		{ _T("muc#register_faqentry"),	JDFT_TEXT_MULTI,	_T("FAQ Entry") },
	};

	// http://jabber.org/protocol/muc#roomconfig
	static TJabberDataFormRegisry_Field form_type_muc_roomconfig[] =
	{
		{ _T("muc#roomconfig_allowinvites"),			JDFT_BOOLEAN,		_T("Whether to Allow Occupants to Invite Others") },
		{ _T("muc#roomconfig_changesubject"),			JDFT_BOOLEAN,		_T("Whether to Allow Occupants to Change Subject") },
		{ _T("muc#roomconfig_enablelogging"),			JDFT_BOOLEAN,		_T("Whether to Enable Logging of Room Conversations") },
		{ _T("muc#roomconfig_lang"),					JDFT_TEXT_SINGLE,	_T("Natural Language for Room Discussions") },
		{ _T("muc#roomconfig_maxusers"),				JDFT_LIST_SINGLE,	_T("Maximum Number of Room Occupants") },
		{ _T("muc#roomconfig_membersonly"),				JDFT_BOOLEAN,		_T("Whether an Make Room Members-Only") },
		{ _T("muc#roomconfig_moderatedroom"),			JDFT_BOOLEAN,		_T("Whether to Make Room Moderated") },
		{ _T("muc#roomconfig_passwordprotectedroom"),	JDFT_BOOLEAN,		_T("Whether a Password is Required to Enter") },
		{ _T("muc#roomconfig_persistentroom"),			JDFT_BOOLEAN,		_T("Whether to Make Room Persistent") },
		{ _T("muc#roomconfig_presencebroadcast"),		JDFT_LIST_MULTI,	_T("Roles for which Presence is Broadcast") },
		{ _T("muc#roomconfig_publicroom"),				JDFT_BOOLEAN,		_T("Whether to Allow Public Searching for Room") },
		{ _T("muc#roomconfig_roomadmins"),				JDFT_JID_MULTI,		_T("Full List of Room Admins") },
		{ _T("muc#roomconfig_roomdesc"),				JDFT_TEXT_SINGLE,	_T("Short Description of Room") },
		{ _T("muc#roomconfig_roomname"),				JDFT_TEXT_SINGLE,	_T("Natural-Language Room Name") },
		{ _T("muc#roomconfig_roomowners"),				JDFT_JID_MULTI,		_T("Full List of Room Owners") },
		{ _T("muc#roomconfig_roomsecret"),				JDFT_TEXT_PRIVATE,	_T("The Room Password") },
		{ _T("muc#roomconfig_whois"),					JDFT_LIST_SINGLE,	_T("Affiliations that May Discover Real JIDs of Occupants") },
	};

	// http://jabber.org/protocol/pubsub#publish-options
	static TJabberDataFormRegisry_Field form_type_publish_options[] =
	{
		{ _T("pubsub#access_model"),	JDFT_LIST_SINGLE,	_T("Precondition: node configuration with the specified access model") },
	};

	// http://jabber.org/protocol/pubsub#subscribe_authorization
	static TJabberDataFormRegisry_Field form_type_subscribe_auth[] =
	{
		{ _T("pubsub#allow"),			JDFT_BOOLEAN,		_T("Whether to allow the subscription") },
		{ _T("pubsub#subid"),			JDFT_TEXT_SINGLE,	_T("The SubID of the subscription") },
		{ _T("pubsub#node"),			JDFT_TEXT_SINGLE,	_T("The NodeID of the relevant node") },
		{ _T("pubsub#subscriber_jid"),	JDFT_JID_SINGLE,	_T("The address (JID) of the subscriber") },
	};

	// http://jabber.org/protocol/pubsub#subscribe_options
	static TJabberDataFormRegisry_Field form_type_subscribe_options[] =
	{
		{ _T("pubsub#deliver"),				JDFT_BOOLEAN,		_T("Whether an entity wants to receive or disable notifications") },
		{ _T("pubsub#digest"),				JDFT_BOOLEAN,		_T("Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually") },
		{ _T("pubsub#digest_frequency"),	JDFT_TEXT_SINGLE,	_T("The minimum number of milliseconds between sending any two notification digests") },
		{ _T("pubsub#expire"),				JDFT_TEXT_SINGLE,	_T("The DateTime at which a leased subscription will end or has ended") },
		{ _T("pubsub#include_body"),		JDFT_BOOLEAN,		_T("Whether an entity wants to receive an XMPP message body in addition to the payload format") },
		{ _T("pubsub#show-values"),			JDFT_LIST_MULTI,	_T("The presence states for which an entity wants to receive notifications") },
		{ _T("pubsub#subscription_type"),	JDFT_LIST_SINGLE,	_T("") },
		{ _T("pubsub#subscription_depth"),	JDFT_LIST_SINGLE,	_T("") },
	};

	// http://jabber.org/protocol/pubsub#node_config
	static TJabberDataFormRegisry_Field form_type_node_config[] =
	{
		{ _T("pubsub#access_model"),					JDFT_LIST_SINGLE,	_T("Who may subscribe and retrieve items") },
		{ _T("pubsub#body_xslt"),						JDFT_TEXT_SINGLE,	_T("The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.") },
		{ _T("pubsub#collection"),						JDFT_TEXT_SINGLE,	_T("The collection with which a node is affiliated") },
		{ _T("pubsub#dataform_xslt"),					JDFT_TEXT_SINGLE,	_T("The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine") },
		{ _T("pubsub#deliver_payloads"),				JDFT_BOOLEAN,		_T("Whether to deliver payloads with event notifications") },
		{ _T("pubsub#itemreply"),						JDFT_LIST_SINGLE,	_T("Whether owners or publisher should receive replies to items") },
		{ _T("pubsub#children_association_policy"),		JDFT_LIST_SINGLE,	_T("Who may associate leaf nodes with a collection") },
		{ _T("pubsub#children_association_whitelist"),	JDFT_JID_MULTI,		_T("The list of JIDs that may associated leaf nodes with a collection") },
		{ _T("pubsub#children"),						JDFT_TEXT_MULTI,	_T("The child nodes (leaf or collection) associated with a collection") },
		{ _T("pubsub#children_max"),					JDFT_TEXT_SINGLE,	_T("The maximum number of child nodes that can be associated with a collection") },
		{ _T("pubsub#max_items"),						JDFT_TEXT_SINGLE,	_T("The maximum number of items to persist") },
		{ _T("pubsub#max_payload_size"),				JDFT_TEXT_SINGLE,	_T("The maximum payload size in bytes") },
		{ _T("pubsub#node_type"),						JDFT_LIST_SINGLE,	_T("Whether the node is a leaf (default) or a collection") },
		{ _T("pubsub#notify_config"),					JDFT_BOOLEAN,		_T("Whether to notify subscribers when the node configuration changes") },
		{ _T("pubsub#notify_delete"),					JDFT_BOOLEAN,		_T("Whether to notify subscribers when the node is deleted") },
		{ _T("pubsub#notify_retract"),					JDFT_BOOLEAN,		_T("Whether to notify subscribers when items are removed from the node") },
		{ _T("pubsub#persist_items"),					JDFT_BOOLEAN,		_T("Whether to persist items to storage") },
		{ _T("pubsub#presence_based_delivery"),			JDFT_BOOLEAN,		_T("Whether to deliver notifications to available users only") },
		{ _T("pubsub#publish_model"),					JDFT_LIST_SINGLE,	_T("The publisher model") },
		{ _T("pubsub#replyroom"),						JDFT_JID_MULTI,		_T("The specific multi-user chat rooms to specify for replyroom") },
		{ _T("pubsub#replyto"),							JDFT_JID_MULTI,		_T("The specific JID(s) to specify for replyto") },
		{ _T("pubsub#roster_groups_allowed"),			JDFT_LIST_MULTI,	_T("The roster group(s) allowed to subscribe and retrieve items") },
		{ _T("pubsub#send_item_subscribe"),				JDFT_BOOLEAN,		_T("Whether to send items to new subscribers") },
		{ _T("pubsub#subscribe"),						JDFT_BOOLEAN,		_T("Whether to allow subscriptions") },
		{ _T("pubsub#title"),							JDFT_TEXT_SINGLE,	_T("A friendly name for the node") },
		{ _T("pubsub#type"),							JDFT_TEXT_SINGLE,	_T("The type of node data, usually specified by the namespace of the payload (if any); MAY be list-single rather than text-single") },
	};

	// http://jabber.org/protocol/pubsub#meta-data
	static TJabberDataFormRegisry_Field form_type_metadata[] =
	{
		{ _T("pubsub#contact"),			JDFT_JID_MULTI,		_T("The JIDs of those to contact with questions") },
		{ _T("pubsub#creation_date"),	JDFT_TEXT_SINGLE,	_T("The datetime when the node was created") },
		{ _T("pubsub#creator"),			JDFT_JID_SINGLE,	_T("The JID of the node creator") },
		{ _T("pubsub#description"),		JDFT_TEXT_SINGLE,	_T("A description of the node") },
		{ _T("pubsub#language"),		JDFT_TEXT_SINGLE,	_T("The default language of the node") },
		{ _T("pubsub#num_subscribers"),	JDFT_TEXT_SINGLE,	_T("The number of subscribers to the node") },
		{ _T("pubsub#owner"),			JDFT_JID_MULTI,		_T("The JIDs of those with an affiliation of owner") },
		{ _T("pubsub#publisher"),		JDFT_JID_MULTI,		_T("The JIDs of those with an affiliation of publisher") },
		{ _T("pubsub#title"),			JDFT_TEXT_SINGLE,	_T("The name of the node") },
		{ _T("pubsub#type"),			JDFT_TEXT_SINGLE,	_T("Payload type") },
	};

	// http://jabber.org/protocol/rc
	static TJabberDataFormRegisry_Field form_type_rc[] =
	{
		{ _T("auto-auth"),			JDFT_BOOLEAN,		_T("Whether to automatically authorize subscription requests") },
		{ _T("auto-files"),			JDFT_BOOLEAN,		_T("Whether to automatically accept file transfers") },
		{ _T("auto-msg"),			JDFT_BOOLEAN,		_T("Whether to automatically open new messages") },
		{ _T("auto-offline"),		JDFT_BOOLEAN,		_T("Whether to automatically go offline when idle") },
		{ _T("sounds"),				JDFT_BOOLEAN,		_T("Whether to play sounds") },
		{ _T("files"),				JDFT_LIST_MULTI,	_T("A list of pending file transfers") },
		{ _T("groupchats"),			JDFT_LIST_MULTI,	_T("A list of joined groupchat rooms") },
		{ _T("status"),				JDFT_LIST_SINGLE,	_T("A presence or availability status") },
		{ _T("status-message"),		JDFT_TEXT_MULTI,	_T("The status message text") },
		{ _T("status-priority"),	JDFT_TEXT_SINGLE,	_T("The new priority for the client") },
	};

	// jabber:iq:register
	static TJabberDataFormRegisry_Field form_type_register[] =
	{
		{ _T("username"),	JDFT_TEXT_SINGLE,	_T("Account name associated with the user") },
		{ _T("nick"),		JDFT_TEXT_SINGLE,	_T("Familiar name of the user") },
		{ _T("password"),	JDFT_TEXT_PRIVATE,	_T("Password or secret for the user") },
		{ _T("name"),		JDFT_TEXT_SINGLE,	_T("Full name of the user") },
		{ _T("first"),		JDFT_TEXT_SINGLE,	_T("First name or given name of the user") },
		{ _T("last"),		JDFT_TEXT_SINGLE,	_T("Last name, surname, or family name of the user") },
		{ _T("email"),		JDFT_TEXT_SINGLE,	_T("Email address of the user") },
		{ _T("address"),	JDFT_TEXT_SINGLE,	_T("Street portion of a physical or mailing address") },
		{ _T("city"),		JDFT_TEXT_SINGLE,	_T("Locality portion of a physical or mailing address") },
		{ _T("state"),		JDFT_TEXT_SINGLE,	_T("Region portion of a physical or mailing address") },
		{ _T("zip"),		JDFT_TEXT_SINGLE,	_T("Postal code portion of a physical or mailing address") },
	};

	// jabber:iq:search
	static TJabberDataFormRegisry_Field form_type_search[] =
	{
		{ _T("first"),	JDFT_TEXT_SINGLE,	_T("First Name") },
		{ _T("last"),	JDFT_TEXT_SINGLE,	_T("Family Name") },
		{ _T("nick"),	JDFT_TEXT_SINGLE,	_T("Nickname") },
		{ _T("email"),	JDFT_TEXT_SINGLE,	_T("Email Address") },
	};

	// urn:xmpp:ssn
	static TJabberDataFormRegisry_Field form_type_ssn[] =
	{
		{ _T("accept"),									JDFT_BOOLEAN,		_T("Whether to accept the invitation") },
		{ _T("continue"),								JDFT_TEXT_SINGLE,	_T("Another resource with which to continue the session") },
		{ _T("disclosure"),								JDFT_LIST_SINGLE,	_T("Disclosure of content, decryption keys or identities") },
		{ _T("http://jabber.org/protocol/chatstates"),	JDFT_LIST_SINGLE,	_T("Whether may send Chat State Notifications per XEP-0085") },
		{ _T("http://jabber.org/protocol/xhtml-im"),	JDFT_LIST_SINGLE,	_T("Whether allowed to use XHTML-IM formatting per XEP-0071") },
		{ _T("language"),								JDFT_LIST_SINGLE,	_T("Primary written language of the chat (each value appears in order of preference and conforms to RFC 4646 and the IANA registry)") },
		{ _T("logging"),								JDFT_LIST_SINGLE,	_T("Whether allowed to log messages (i.e., whether Off-The-Record mode is required)") },
		{ _T("renegotiate"),							JDFT_BOOLEAN,		_T("Whether to renegotiate the session") },
		{ _T("security"),								JDFT_LIST_SINGLE,	_T("Minimum security level") },
		{ _T("terminate"),								JDFT_BOOLEAN,		_T("Whether to terminate the session") },
		{ _T("urn:xmpp:receipts"),						JDFT_BOOLEAN,		_T("Whether to enable Message Receipts per XEP-0184") },
	};

	TJabberDataFormRegisry_Form form_types[] = 
	{
	/*0157*/	{ _T("http://jabber.org/network/serverinfo"),						form_type_serverinfo,			SIZEOF(form_type_serverinfo) },
	/*0133*/	{ _T("http://jabber.org/protocol/admin"),							form_type_admin,				SIZEOF(form_type_admin) },
	/*0045*/	{ _T("http://jabber.org/protocol/muc#register"),					form_type_muc_register,			SIZEOF(form_type_muc_register) },
	/*0045*/	{ _T("http://jabber.org/protocol/muc#roomconfig"),					form_type_muc_roomconfig,		SIZEOF(form_type_muc_roomconfig) },
	/*0060*/	{ _T("http://jabber.org/protocol/pubsub#publish-options"),			form_type_publish_options,		SIZEOF(form_type_publish_options) },
	/*0060*/	{ _T("http://jabber.org/protocol/pubsub#subscribe_authorization"),	form_type_subscribe_auth,		SIZEOF(form_type_subscribe_auth) },
	/*0060*/	{ _T("http://jabber.org/protocol/pubsub#subscribe_options"),		form_type_subscribe_options,	SIZEOF(form_type_subscribe_options) },
	/*0060*/	{ _T("http://jabber.org/protocol/pubsub#node_config"),				form_type_node_config,			SIZEOF(form_type_node_config) },
	/*0060*/	{ _T("http://jabber.org/protocol/pubsub#meta-data"),				form_type_metadata,				SIZEOF(form_type_metadata) },
	/*0146*/	{ _T("http://jabber.org/protocol/rc"),								form_type_rc,					SIZEOF(form_type_rc) },
	/*0077*/	{ _T("jabber:iq:register"),											form_type_register,				SIZEOF(form_type_register) },
	/*0055*/	{ _T("jabber:iq:search"),											form_type_search,				SIZEOF(form_type_search) },
	/*0155*/	{ _T("urn:xmpp:ssn"),												form_type_ssn,					SIZEOF(form_type_ssn) },
	};
};

CJabberDataField::CJabberDataField(CJabberDataForm *form, XmlNode *node):
	m_node(node), m_options(5), m_values(1), m_descriptions(1)
{
	m_typeName = JabberXmlGetAttrValue(m_node, "type");
	m_var = JabberXmlGetAttrValue(m_node, "var");
	m_label = JabberXmlGetAttrValue(m_node, "label");
	m_required = JabberXmlGetChild(m_node, "required") ? true : false;

	if (m_typeName)
	{
		if (!lstrcmp(m_typeName, _T("text-private")))
			m_type = JDFT_TEXT_PRIVATE;
		else if (!lstrcmp(m_typeName, _T("text-multi")) || !lstrcmp(m_typeName, _T("jid-multi")))
			m_type = JDFT_TEXT_MULTI;
		else if (!lstrcmp(m_typeName, _T("boolean")))
			m_type = JDFT_BOOLEAN;
		else if (!lstrcmp(m_typeName, _T("list-single")))
			m_type = JDFT_LIST_SINGLE;
		else if (!lstrcmp(m_typeName, _T("list-multi")))
			m_type = JDFT_LIST_MULTI;
		else if (!lstrcmp(m_typeName, _T("fixed")))
			m_type = JDFT_FIXED;
		else if (!lstrcmp(m_typeName, _T("hidden")))
			m_type = JDFT_HIDDEN;
		else
			m_type = JDFT_TEXT_SINGLE;
	} else
	{
		m_typeName = _T("text-single");
		m_type = JDFT_TEXT_SINGLE;
	}

	for (int i = 0; i < m_node->numChild; ++i)
	{
		if (!lstrcmpA(m_node->child[i]->name, "value"))
		{
			m_values.insert(m_node->child[i]->text, m_values.getCount());
		} else
		if (!lstrcmpA(m_node->child[i]->name, "option"))
		{
			TOption *opt = new TOption;
			opt->label = JabberXmlGetAttrValue(m_node->child[i], "label");
			opt->value = NULL;
			if (XmlNode *p = JabberXmlGetChild(m_node->child[i], "value"))
				opt->value = p->text;
			m_options.insert(opt, m_options.getCount());
		} else
		if (!lstrcmpA(m_node->child[i]->name, "desc"))
		{
			m_descriptions.insert(m_node->child[i]->text, m_descriptions.getCount());
		}
	}
}

CJabberDataField::~CJabberDataField()
{
	m_values.destroy();
	m_descriptions.destroy();
}

CJabberDataFieldSet::CJabberDataFieldSet():
	m_fields(5)
{
}

CJabberDataField *CJabberDataFieldSet::GetField(TCHAR *var)
{
	for (int i = 0; i < m_fields.getCount(); ++i)
		if (!lstrcmp(m_fields[i].GetVar(), var))
			return &(m_fields[i]);
	return NULL;
}

CJabberDataForm::CJabberDataForm(XmlNode *node):
	m_node(node),
	m_form_type(0),
	m_form_type_info(0),
	m_title(0),
	m_instructions(1),
	m_items(1)
{
	m_typename = JabberXmlGetAttrValue(m_node, "type");

	for (int i = 0; i < m_node->numChild; ++i)
	{
		XmlNode *child = m_node->child[i];
		if (!lstrcmpA(child->name, "field"))
		{
			CJabberDataField *field = new CJabberDataField(this, child);
			m_fields.AddField(field);

			if ((field->GetType() == JDFT_HIDDEN) && !lstrcmp(field->GetVar(), _T("FORM_TYPE")))
			{
				using NSJabberRegistry::form_types;

				m_form_type = field->GetValue();
				for (int j = 0; j < SIZEOF(form_types); ++j)
					if (!lstrcmp(m_form_type, form_types[j].form_type))
					{
						m_form_type_info = form_types + j;
						break;
					}
			}
		} else
		if (!lstrcmpA(child->name, "title"))
		{
			m_title = child->text;
		} else
		if (!lstrcmpA(child->name, "instructions"))
		{
			m_instructions.insert(child->text, m_instructions.getCount());
		} else
		if (!lstrcmpA(child->name, "reported"))
		{
			if (m_reported.GetCount())
				continue;	// ignore second <reported/> -> error!!!!!!!!!!!

			for (int j = 0; j < child->numChild; ++j)
			{
				XmlNode *child2 = child->child[i];
				if (!lstrcmpA(child2->name, "field"))
				{
					CJabberDataField *field = new CJabberDataField(this, child2);
					m_reported.AddField(field);
				}
			}
		} else
		if (!lstrcmpA(child->name, "item"))
		{
			CJabberDataFieldSet *item = new CJabberDataFieldSet;
			m_items.insert(item);

			for (int j = 0; j < child->numChild; ++j)
			{
				XmlNode *child2 = child->child[i];
				if (!lstrcmpA(child2->name, "field"))
				{
					CJabberDataField *field = new CJabberDataField(this, child2);
					item->AddField(field);
				}
			}
		}
	}
}

CJabberDataForm::~CJabberDataForm()
{
}


/////////////////////////////////////////////////////////////////////////////////////////
// UI classes

#define FORM_CONTROL_MINWIDTH	100
#define FORM_CONTROL_HEIGHT		20

class CFormCtrlBase;

class CJabberDlgDataPage
{
public:
	CJabberDlgDataPage(HWND hwndParent);
	~CJabberDlgDataPage();

	void AddField(CJabberDataField *field);
	XmlNode *FetchData();

	HWND GetHwnd() { return m_hwnd; }
	void Layout();

	// internal usage
	int AddControl(CFormCtrlBase *ctrl)
	{
		m_controls.insert(ctrl, m_controls.getCount());
		return m_controls.getCount();
	}

private:
	HWND m_hwnd;
	OBJLIST<CFormCtrlBase> m_controls;
	int m_scrollPos, m_height, m_dataHeight;

	BOOL DlgProc(UINT msg, WPARAM wParam, LPARAM lParam);
	static BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

class CFormCtrlBase
{
public:
	CFormCtrlBase(CJabberDlgDataPage *parent, CJabberDataField *field):
		m_field(field), m_parent(parent),
		m_hwnd(NULL), m_hwndLabel(NULL)
	{
	}

	HWND GetHwnd() { return m_hwnd; }
	void Init();

	int Layout(HDWP hdwp, int x, int y, int w);
	virtual XmlNode *FetchData() = 0;

protected:
	int m_id;
	HWND m_hwnd, m_hwndLabel;
	CJabberDataField *m_field;
	CJabberDlgDataPage *m_parent;

	virtual void CreateControl() = 0;
	virtual int GetHeight(int width) = 0;
	SIZE GetControlTextSize(HWND hwnd, int w);

	void CreateLabel();
	void SetupFont();
	XmlNode *CreateNode();
};

void CFormCtrlBase::Init()
{
	m_id = m_parent->AddControl(this);
	CreateControl();
	SetupFont();
}

SIZE CFormCtrlBase::GetControlTextSize(HWND hwnd, int w)
{
	int length = GetWindowTextLength(hwnd) + 1;
	TCHAR *text = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
	GetWindowText(hwnd, text, length);

	RECT rc;
	SetRect(&rc, 0, 0, w, 0);
	HDC hdc = GetDC(hwnd);
	HFONT hfntSave = (HFONT)SelectObject(hdc, (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0));
	DrawText(hdc, text, -1, &rc, DT_CALCRECT|DT_WORDBREAK);
	SelectObject(hdc, hfntSave);
	ReleaseDC(hwnd, hdc);

	mir_free(text);

	SIZE res = { rc.right, rc.bottom };
	return res;
}

int CFormCtrlBase::Layout(HDWP hdwp, int x, int y, int w)
{
	SIZE szLabel = {0}, szCtrl = {0};
	int h = 0;

	if (m_hwndLabel)
	{
		SIZE szLabel = GetControlTextSize(m_hwndLabel, w);

		szCtrl.cx = w - szLabel.cx - 5;
		szCtrl.cy = GetHeight(szCtrl.cx);
		if ((szCtrl.cx >= FORM_CONTROL_MINWIDTH) && (szCtrl.cy <= FORM_CONTROL_HEIGHT))
		{
			DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y + (szCtrl.cy - szLabel.cy) / 2, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
			DeferWindowPos(hdwp, m_hwnd, NULL, x + szLabel.cx + 5, y, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);

			h = szCtrl.cy;
		} else
		{
			szCtrl.cx = w - 10;
			szCtrl.cy = GetHeight(szCtrl.cx);

			DeferWindowPos(hdwp, m_hwndLabel, NULL, x, y, szLabel.cx, szLabel.cy, SWP_NOZORDER|SWP_SHOWWINDOW);
			DeferWindowPos(hdwp, m_hwnd, NULL, x + 10, y + szLabel.cy + 2, szCtrl.cx, szCtrl.cy, SWP_NOZORDER|SWP_SHOWWINDOW);

			h = szLabel.cy + 2 + szCtrl.cy;
		}

	} else
	{
		h = GetHeight(w);
		DeferWindowPos(hdwp, m_hwnd, NULL, x, y, w, h, SWP_NOZORDER|SWP_SHOWWINDOW);
	}
	
	return h;
}

void CFormCtrlBase::CreateLabel()
{
	if (m_field->GetLabel())
	{
		m_hwndLabel = CreateWindow(_T("static"), m_field->GetLabel(),
			WS_CHILD|WS_VISIBLE/*|SS_CENTERIMAGE*/,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
	}
}

void CFormCtrlBase::SetupFont()
{
	if (m_hwnd)
	{
		HFONT hFont = (HFONT)SendMessage(GetParent(m_hwnd), WM_GETFONT, 0, 0);
		if (m_hwnd) SendMessage(m_hwnd, WM_SETFONT, (WPARAM)hFont, 0);
		if (m_hwndLabel) SendMessage(m_hwndLabel, WM_SETFONT, (WPARAM) hFont, 0);
	}
}

XmlNode *CFormCtrlBase::CreateNode()
{
	XmlNode* field = new XmlNode("field");
	field->addAttr("var", m_field->GetVar());
	field->addAttr("type", m_field->GetTypeName());
	return field;
}

class CFormCtrlBoolean: public CFormCtrlBase
{
public:
	CFormCtrlBoolean(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		m_hwnd = CreateWindowEx(0, _T("button"), m_field->GetLabel(),
			WS_CHILD|WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX|BS_MULTILINE,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
		if (m_field->GetValue() && !_tcscmp(m_field->GetValue(), _T("1")))
			SendMessage(m_hwnd, BM_SETCHECK, 1, 0);
	}

	int GetHeight(int width)
	{
		return GetControlTextSize(m_hwnd, width - 20).cy;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		field->addChild("value", (SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED) ? _T("1") : _T("0"));
		return field;
	}
};

class CFormCtrlFixed: public CFormCtrlBase
{
public:
	CFormCtrlFixed(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		m_hwnd = CreateWindow(_T("edit"), m_field->GetValue(),
			WS_CHILD|WS_VISIBLE|ES_MULTILINE|ES_READONLY,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)-1, hInst, NULL);
	}

	int GetHeight(int width)
	{
		return GetControlTextSize(m_hwnd, width - 2).cy;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		for (int i = 0; i < m_field->GetValueCount(); ++i)
			field->addChild("value", m_field->GetValue(i));
		return field;
	}
};

class CFormCtrlHidden: public CFormCtrlBase
{
public:
	CFormCtrlHidden(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
	}

	int GetHeight(int width)
	{
		return 0;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		for (int i = 0; i < m_field->GetValueCount(); ++i)
			field->addChild("value", m_field->GetValue(i));
		return field;
	}
};
/*
class CFormCtrlJidMulti: public CFormCtrlBase
{
public:
	CFormCtrlJidMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
	}

	int GetHeight(int width)
	{
		return 20;
	}

	XmlNode *FetchData()
	{
		return NULL;
	}
};

class CFormCtrlJidSingle: public CFormCtrlBase
{
public:
	CFormCtrlJidSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
	}

	int GetHeight(int width)
	{
		return 20;
	}

	XmlNode *FetchData()
	{
		return NULL;
	}
};
*/
class CFormCtrlListMulti: public CFormCtrlBase
{
public:
	CFormCtrlListMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("listbox"),
			NULL, WS_CHILD|WS_VISIBLE|WS_TABSTOP|LBS_MULTIPLESEL,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);

		for (int i = 0; i < m_field->GetOptionCount(); ++i)
		{
			DWORD dwIndex = SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
			SendMessage(m_hwnd, LB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
			for (int j = 0; j < m_field->GetValueCount(); ++j)
				if (!lstrcmp_null(m_field->GetValue(j), m_field->GetOption(i)->value))
				{
					SendMessage(m_hwnd, LB_SETSEL, TRUE, dwIndex);
					break;
				}
		}
	}

	int GetHeight(int width)
	{
		return 20 * 3;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		int count = SendMessage(m_hwnd, LB_GETCOUNT, 0, 0);
		for (int i = 0; i < count; ++i)
			if (SendMessage(m_hwnd, LB_GETSEL, i, 0) > 0)
				field->addChild("value", (TCHAR *)SendMessage(m_hwnd, LB_GETITEMDATA, i, 0));
		return field;
	}
};

class CFormCtrlListSingle: public CFormCtrlBase
{
public:
	CFormCtrlListSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("combobox"), NULL,
			WS_CHILD|WS_VISIBLE|WS_TABSTOP|CBS_DROPDOWNLIST,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);

		for (int i = 0; i < m_field->GetOptionCount(); ++i)
		{
			DWORD dwIndex = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)m_field->GetOption(i)->label);
			SendMessage(m_hwnd, CB_SETITEMDATA, dwIndex, (LPARAM)m_field->GetOption(i)->value);
			if (!lstrcmp_null(m_field->GetValue(), m_field->GetOption(i)->value))
				SendMessage(m_hwnd, CB_SETCURSEL, dwIndex, 0);
		}
	}

	int GetHeight(int width)
	{
		return 20;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		int sel = SendMessage(m_hwnd, CB_GETCURSEL, 0, 0);
		if (sel != CB_ERR)
			field->addChild("value", (TCHAR *)SendMessage(m_hwnd, CB_GETITEMDATA, sel, 0));
		return field;
	}
};

class CFormCtrlTextMulti: public CFormCtrlBase
{
public:
	CFormCtrlTextMulti(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		int i, length = 1;
		for (i = 0; i < m_field->GetValueCount(); ++i)
			length += lstrlen(m_field->GetValue(i)) + 2;

		TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * length);
		*str = 0;
		for (i = 0; i < m_field->GetValueCount(); ++i)
		{
			if (i) lstrcat(str, _T("\r\n"));
			lstrcat(str, m_field->GetValue(i));
		}

		m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), str,
			WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL|ES_WANTRETURN,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
	//	WNDPROC oldWndProc = (WNDPROC)SetWindowLongPtr(item->hCtrl, GWL_WNDPROC, (LPARAM)JabberFormMultiLineWndProc);
	//	SetWindowLongPtr(item->hCtrl, GWL_USERDATA, (LONG) oldWndProc);

		mir_free(str);
	}

	int GetHeight(int width)
	{
		return 20 * 3;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		int len = GetWindowTextLength(m_hwnd);
		TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
		GetWindowText(m_hwnd, str, len+1);
		TCHAR *p = str;
		while (p != NULL)
		{
			TCHAR *q = _tcsstr( p, _T("\r\n"));
			if (q) *q = '\0';
			field->addChild("value", p);
			p = q ? q+2 : NULL;
		}
		mir_free(str);
		return field;
	}
};

class CFormCtrlTextSingle: public CFormCtrlBase
{
public:
	CFormCtrlTextSingle(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
			WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
	}

	int GetHeight(int width)
	{
		return 20;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		int len = GetWindowTextLength(m_hwnd);
		TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
		GetWindowText(m_hwnd, str, len+1);
		field->addChild("value", str);
		mir_free(str);
		return field;
	}
};

class CFormCtrlTextPrivate: public CFormCtrlBase
{
public:
	CFormCtrlTextPrivate(CJabberDlgDataPage *parent, CJabberDataField *field): CFormCtrlBase(parent, field) {}

	void CreateControl()
	{
		CreateLabel();
		m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, _T("edit"), m_field->GetValue(),
			WS_CHILD|WS_VISIBLE|WS_TABSTOP|ES_LEFT|ES_AUTOHSCROLL|ES_PASSWORD,
			0, 0, 0, 0,
			m_parent->GetHwnd(), (HMENU)m_id, hInst, NULL);
	}

	int GetHeight(int width)
	{
		return 20;
	}

	XmlNode *FetchData()
	{
		XmlNode *field = CreateNode();
		int len = GetWindowTextLength(m_hwnd);
		TCHAR *str = (TCHAR *)mir_alloc(sizeof(TCHAR) * (len+1));
		GetWindowText(m_hwnd, str, len+1);
		field->addChild("value", str);
		mir_free(str);
		return field;
	}
};

CJabberDlgDataPage::CJabberDlgDataPage(HWND hwndParent):
	m_controls(5)
{
	m_hwnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DATAFORM_PAGE), hwndParent, DlgProc, (LPARAM)this);
	ShowWindow(m_hwnd, SW_SHOW);
}

CJabberDlgDataPage::~CJabberDlgDataPage()
{
	DestroyWindow(m_hwnd);
}

void CJabberDlgDataPage::AddField(CJabberDataField *field)
{
	CFormCtrlBase *ctrl = NULL;

	switch (field->GetType())
	{
		case JDFT_BOOLEAN:		ctrl = new CFormCtrlBoolean(this, field);		break;
		case JDFT_FIXED:		ctrl = new CFormCtrlFixed(this, field);			break;
		case JDFT_HIDDEN:		ctrl = new CFormCtrlHidden(this, field);		break;
		case JDFT_JID_MULTI:	ctrl = new CFormCtrlTextMulti(this, field);		break;
		case JDFT_JID_SINGLE:	ctrl = new CFormCtrlTextSingle(this, field);	break;
		case JDFT_LIST_MULTI:	ctrl = new CFormCtrlListMulti(this, field);		break;
		case JDFT_LIST_SINGLE:	ctrl = new CFormCtrlListSingle(this, field);	break;
		case JDFT_TEXT_MULTI:	ctrl = new CFormCtrlTextMulti(this, field);		break;
		case JDFT_TEXT_PRIVATE:	ctrl = new CFormCtrlTextPrivate(this, field);	break;
		case JDFT_TEXT_SINGLE:	ctrl = new CFormCtrlTextSingle(this, field);	break;
	}

	if (ctrl) ctrl->Init();
}

XmlNode *CJabberDlgDataPage::FetchData()
{
	XmlNode *result = new XmlNode("x");
	result->addAttr("xmlns", JABBER_FEAT_DATA_FORMS);
	result->addAttr("type", "submit");

	for (int i = 0; i < m_controls.getCount(); ++i)
		if (XmlNode *field = m_controls[i].FetchData())
			result->addChild(field);

	return result;
}

void CJabberDlgDataPage::Layout()
{
	RECT rc; GetClientRect(m_hwnd, &rc);
	int w = rc.right - 20;
	int x = 10;
	int y = 10;

	m_height = rc.bottom;
	m_scrollPos = GetScrollPos(m_hwnd, SB_VERT);

	HDWP hdwp = BeginDeferWindowPos(m_controls.getCount());
	for (int i = 0; i < m_controls.getCount(); ++i)
		if (int h = m_controls[i].Layout(hdwp, x, y - m_scrollPos, w))
			y += h + 5;
	EndDeferWindowPos(hdwp);

	m_dataHeight = y + 5;

	SCROLLINFO si = {0};
	si.cbSize = sizeof(si);
	si.fMask = SIF_DISABLENOSCROLL|SIF_PAGE|SIF_RANGE;
	si.nPage = m_height;
	si.nMin = 0;
	si.nMax = m_dataHeight;
	SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
}

BOOL CJabberDlgDataPage::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_INITDIALOG:
	{
		SCROLLINFO si = {0};
		si.cbSize = sizeof(si);
		si.fMask = SIF_DISABLENOSCROLL;
		SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
		m_scrollPos = 0;

		break;
	}

	case WM_MOUSEWHEEL:
	{
		int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
		if (zDelta)
		{
			int i, nScrollLines = 0;
			SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (void*)&nScrollLines, 0);
			for (i = 0; i < (nScrollLines + 1) / 2; i++ )
				SendMessage(m_hwnd, WM_VSCROLL, (zDelta < 0) ? SB_LINEDOWN : SB_LINEUP, 0);
		}

		SetWindowLongPtr(m_hwnd, DWL_MSGRESULT, 0);
		return TRUE;
	}

	case WM_VSCROLL:
	{
		int pos = m_scrollPos;
		switch (LOWORD(wParam))
		{
		case SB_LINEDOWN:
			pos += 15;
			break;
		case SB_LINEUP:
			pos -= 15;
			break;
		case SB_PAGEDOWN:
			pos += m_height - 10;
			break;
		case SB_PAGEUP:
			pos -= m_height - 10;
			break;
		case SB_THUMBTRACK:
			pos = HIWORD(wParam);
			break;
		}

		if (pos > m_dataHeight - m_height) pos = m_dataHeight - m_height;
		if (pos < 0) pos = 0;

		if (m_scrollPos != pos)
		{
			ScrollWindow(m_hwnd, 0, m_scrollPos - pos, NULL, NULL);
			SetScrollPos(m_hwnd, SB_VERT, pos, TRUE);
			m_scrollPos = pos;
		}
		break;
	}

	case WM_SIZE:
		Layout();
		break;
	}

	return FALSE;
}

BOOL CJabberDlgDataPage::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	CJabberDlgDataPage *pThis = NULL;

	if (msg == WM_INITDIALOG)
	{
		if (pThis = (CJabberDlgDataPage *)lParam)
			pThis->m_hwnd = hwnd;
		SetWindowLongPtr(hwnd, GWL_USERDATA, lParam);
	} else
	{
		pThis = (CJabberDlgDataPage *)GetWindowLongPtr(hwnd, GWL_USERDATA);
	}

	if (pThis)
	{
		BOOL result = pThis->DlgProc(msg, wParam, lParam);
		if (msg == WM_DESTROY)
		{
			pThis->m_hwnd = NULL;
			SetWindowLongPtr(hwnd, GWL_USERDATA, 0);
		}
		return result;
	}

	return FALSE;
}

/////////////////////////////////////////////////////////////////////////////////
// Data form control -- Window class support
const TCHAR *CCtrlJabberForm::ClassName = _T("JabberDataFormControl");
bool CCtrlJabberForm::ClassRegistered = false;

bool CCtrlJabberForm::RegisterClass()
{
	if (ClassRegistered) return true;

	WNDCLASSEX wcx = {0};
	wcx.cbSize = sizeof(wcx);
	wcx.lpszClassName = ClassName;
	wcx.lpfnWndProc = DefWindowProc;
	wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcx.hbrBackground = 0;
	wcx.style = CS_GLOBALCLASS;

	if (::RegisterClassEx(&wcx))
		ClassRegistered = true;

	return ClassRegistered;
}

bool CCtrlJabberForm::UnregisterClass()
{
	if (!ClassRegistered) return true;

	if (::UnregisterClass(ClassName, hInst) == 0)
		ClassRegistered = false;

	return !ClassRegistered;
}

/////////////////////////////////////////////////////////////////////////////////
// Data form control
CCtrlJabberForm::CCtrlJabberForm(CDlgBase* dlg, int ctrlId):
	CCtrlBase(dlg, ctrlId), m_pForm(NULL), m_pDlgPage(NULL)
{
}

CCtrlJabberForm::~CCtrlJabberForm()
{
	if (m_pDlgPage) delete m_pDlgPage;
}

void CCtrlJabberForm::OnInit()
{
	CSuper::OnInit();
	Subclass();
	SetupForm();
}

void CCtrlJabberForm::SetDataForm(CJabberDataForm *pForm)
{
	if (m_pDlgPage)
	{
		delete m_pDlgPage;
		m_pDlgPage = NULL;
	}

	m_pForm = pForm;
	SetupForm();
}

XmlNode *CCtrlJabberForm::FetchData()
{
	return m_pDlgPage ? m_pDlgPage->FetchData() : NULL;
}

void CCtrlJabberForm::SetupForm()
{
	if (!m_pForm || !m_hwnd) return;

	m_pDlgPage = new CJabberDlgDataPage(m_hwnd);
	for (int i = 0; i < m_pForm->GetFields()->GetCount(); ++i)
		m_pDlgPage->AddField(m_pForm->GetFields()->GetField(i));

	Layout();
}

void CCtrlJabberForm::Layout()
{
	if (!m_pDlgPage) return;

	RECT rc;
	GetClientRect(m_hwnd, &rc);
	SetWindowPos(m_pDlgPage->GetHwnd(), NULL, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
}

LRESULT CCtrlJabberForm::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
		case WM_SIZE:
		{
			Layout();
			break;
		}
	}

	return CSuper::CustomWndProc(msg, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////////
// testing
class CJabberDlgFormTest: public CDlgBase
{
public:
	CJabberDlgFormTest(CJabberDataForm *pForm):
	  CDlgBase(IDD_DATAFORM_TEST, NULL),
		m_frm(this, IDC_DATAFORM)
	{
		m_frm.SetDataForm(pForm);
	}

	int Resizer(UTILRESIZECONTROL *urc)
	{
		return RD_ANCHORX_WIDTH|RD_ANCHORY_HEIGHT;
	}

private:
	CCtrlJabberForm m_frm;
};

static VOID CALLBACK CreateDialogApcProc(void* param)
{
	XmlNode *node = (XmlNode *)param;

	CJabberDataForm form(node);

	CCtrlJabberForm::RegisterClass();
	CJabberDlgFormTest dlg(&form);
	dlg.DoModal();

	delete node;
}

void LaunchForm(XmlNode *node)
{
	node = JabberXmlCopyNode(node);
	CallFunctionAsync(CreateDialogApcProc, node);
}