/*
Jabber Protocol Plugin for Miranda NG
Copyright (c) 2002-04  Santithorn Bunchua
Copyright (c) 2005-12  George Hazan
Copyright (C) 2012-20 Miranda NG team
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 "stdafx.h"
#include "jabber_iq.h"
#include "jabber_caps.h"
/////////////////////////////////////////////////////////////////////////////////////////
// Agent registration progress dialog
class CAgentRegProgressDlg : public CJabberDlgBase
{
	CCtrlButton m_ok;
public:
	CAgentRegProgressDlg(CJabberProto *_ppro, HWND _owner) :
		CJabberDlgBase(_ppro, IDD_OPT_REGISTER),
		m_ok(this, IDOK)
	{
		SetParent(_owner);
	}
	bool OnInitDialog() override
	{
		m_proto->m_pDlgReg = this;
		SetWindowText(m_hwnd, TranslateT("Jabber Agent Registration"));
		TranslateDialogDefault(m_hwnd);
		return true;
	}
	void OnDestroy() override
	{
		m_proto->m_pDlgReg = nullptr;
	}
	void Update(int progress, const wchar_t *pwszText)
	{
		if (this == nullptr)
			return;
		SetDlgItemText(m_hwnd, IDC_REG_STATUS, pwszText);
		SendDlgItemMessage(m_hwnd, IDC_PROGRESS_REG, PBM_SETPOS, progress, 0);
		if (progress >= 100)
			m_ok.SetText(TranslateT("OK"));
	}
};
void CJabberProto::OnIqAgentSetRegister(const TiXmlElement *iqNode, CJabberIqInfo *)
{
	// RECVED: result of registration process
	// ACTION: notify of successful agent registration
	debugLogA(" iqIdSetRegister");
	const char *type, *from;
	if ((type = XmlGetAttr(iqNode, "type")) == nullptr) return;
	if ((from = XmlGetAttr(iqNode, "from")) == nullptr) return;
	if (!mir_strcmp(type, "result")) {
		MCONTACT hContact = HContactFromJID(from);
		if (hContact != 0)
			setByte(hContact, "IsTransport", true);
		m_pDlgReg->Update(100, TranslateT("Registration successful"));
	}
	else if (!mir_strcmp(type, "error")) {
		m_pDlgReg->Update(100, JabberErrorMsg(iqNode).c_str());
	}
}
/////////////////////////////////////////////////////////////////////////////////////////
// Transport registration form
class CAgentRegDlg : public CJabberDlgBase
{
	int m_curPos;
	int m_formHeight, m_frameHeight;
	RECT m_frameRect;
	TiXmlDocument m_doc;
	TiXmlElement *m_agentRegIqNode;
	char *m_jid;
	HWND m_statusBar;
public:
	CAgentRegDlg(CJabberProto *_ppro, char *_jid) :
		CJabberDlgBase(_ppro, IDD_FORM),
		m_jid(_jid),
		m_agentRegIqNode(nullptr)
	{
	}
	bool OnInitDialog() override
	{
		m_proto->m_pDlgAgentReg = this;
		EnableWindow(GetParent(m_hwnd), FALSE);
		SetWindowText(m_hwnd, TranslateT("Jabber Agent Registration"));
		SetDlgItemText(m_hwnd, IDOK, TranslateT("Register"));
		m_statusBar = CreateWindowExW(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr);
		SendMessage(m_statusBar, WM_SIZE, 0, 0);
		SetWindowTextW(m_statusBar, TranslateT("Please wait..."));
		m_proto->m_ThreadInfo->send(
			XmlNodeIq(m_proto->AddIQ(&CJabberProto::OnIqAgentGetRegister, JABBER_IQ_TYPE_GET, m_jid)) << XQUERY(JABBER_FEAT_REGISTER));
		// Enable WS_EX_CONTROLPARENT on IDC_FRAME (so tab stop goes through all its children)
		LONG_PTR frameExStyle = GetWindowLongPtr(GetDlgItem(m_hwnd, IDC_FRAME), GWL_EXSTYLE);
		frameExStyle |= WS_EX_CONTROLPARENT;
		SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_FRAME), GWL_EXSTYLE, frameExStyle);
		return true;
	}
	bool OnApply() override
	{
		if (m_agentRegIqNode == nullptr)
			return true;
		auto *queryNode = XmlFirstChild(m_agentRegIqNode, "query");
		const char *from = XmlGetAttr(m_agentRegIqNode, "from");
		if (from == nullptr || queryNode == nullptr)
			return true;
		HWND hwndFrame = GetDlgItem(m_hwnd, IDC_FRAME);
		int id = 0;
		XmlNodeIq iq(m_proto->AddIQ(&CJabberProto::OnIqAgentSetRegister, JABBER_IQ_TYPE_SET, from));
		TiXmlElement *query = iq << XQUERY(JABBER_FEAT_REGISTER);
		if (auto *xNode = XmlFirstChild(queryNode, "x")) {
			// use new jabber:x:data form
			JabberFormGetData(hwndFrame, query, xNode);
		}
		else {
			// use old registration information form
			for (auto *n : TiXmlEnum(queryNode)) {
				const char *pszName = n->Name();
				if (pszName) {
					if (!mir_strcmp(pszName, "key")) {
						// field that must be passed along with the registration
						if (n->GetText())
							XmlAddChildA(query, pszName, n->GetText());
						else
							XmlAddChild(query, pszName);
					}
					else if (!mir_strcmp(pszName, "registered") || !mir_strcmp(pszName, "instructions")) {
						// do nothing, we will skip these
					}
					else {
						wchar_t str[128];
						GetDlgItemText(hwndFrame, id, str, _countof(str));
						XmlAddChildA(query, pszName, T2Utf(str).get());
						id++;
					}
				}
			}
		}
		m_proto->m_ThreadInfo->send(iq);
		CAgentRegProgressDlg(m_proto, m_hwnd).DoModal();
		return true;
	}
	void OnDestroy() override
	{
		JabberFormDestroyUI(GetDlgItem(m_hwnd, IDC_FRAME));
		m_proto->m_pDlgAgentReg = nullptr;
		EnableWindow(GetParent(m_hwnd), TRUE);
		SetActiveWindow(GetParent(m_hwnd));
	}
	void Success(const TiXmlElement *pNode)
	{
		HWND hwndFrame = GetDlgItem(m_hwnd, IDC_FRAME);
		SetWindowTextW(m_statusBar, L"");
		if (pNode == nullptr)
			return;
		m_agentRegIqNode = pNode->DeepClone(&m_doc)->ToElement();
		auto *queryNode = XmlFirstChild(m_agentRegIqNode, "query");
		if (queryNode == nullptr)
			return;
		m_curPos = 0;
		GetClientRect(GetDlgItem(m_hwnd, IDC_FRAME), &m_frameRect);
		RECT rect;
		GetClientRect(GetDlgItem(m_hwnd, IDC_VSCROLL), &rect);
		m_frameRect.right -= (rect.right - rect.left);
		GetClientRect(GetDlgItem(m_hwnd, IDC_FRAME), &rect);
		m_frameHeight = rect.bottom - rect.top;
		if (auto *xNode = XmlFirstChild(queryNode, "x")) {
			// use new jabber:x:data form
			if (const char *pszText = XmlGetChildText(xNode, "instructions"))
				JabberFormSetInstruction(m_hwnd, pszText);
			JabberFormCreateUI(hwndFrame, xNode, &m_formHeight);
		}
		else {
			// use old registration information form
			TJabberFormLayoutInfo layout_info(hwndFrame, false);
			for (auto *n : TiXmlEnum(queryNode)) {
				const char *pszName = n->Name();
				if (pszName) {
					if (!mir_strcmp(pszName, "instructions")) {
						JabberFormSetInstruction(m_hwnd, n->GetText());
					}
					else if (!mir_strcmp(pszName, "key") || !mir_strcmp(pszName, "registered")) {
						// do nothing
					}
					else if (!mir_strcmp(pszName, "password"))
						layout_info.AppendControl(JFORM_CTYPE_TEXT_PRIVATE, pszName, n->GetText());
					else 	// everything else is a normal text field
						layout_info.AppendControl(JFORM_CTYPE_TEXT_SINGLE, pszName, n->GetText());
				}
			}
			layout_info.OrderControls(&m_formHeight);
		}
		if (m_formHeight > m_frameHeight) {
			HWND hwndScroll = GetDlgItem(m_hwnd, IDC_VSCROLL);
			EnableWindow(hwndScroll, TRUE);
			SetScrollRange(hwndScroll, SB_CTL, 0, m_formHeight - m_frameHeight, FALSE);
			m_curPos = 0;
		}
		EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE);
	}
	void Fail(const CMStringW &wszErrMsg)
	{
		SetWindowText(m_statusBar, wszErrMsg);
	}
	INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
	{
		switch (msg) {
		case WM_CTLCOLORSTATIC:
			switch (GetDlgCtrlID((HWND)lParam)) {
			case IDC_WHITERECT: case IDC_INSTRUCTION: case IDC_TITLE:
				return (INT_PTR)GetStockObject(WHITE_BRUSH);
			default:
				return 0;
			}
		case WM_VSCROLL:
			int pos = m_curPos;
			switch (LOWORD(wParam)) {
				case SB_LINEDOWN:   pos += 10;                    break;
				case SB_LINEUP:     pos -= 10;                    break;
				case SB_PAGEDOWN:   pos += (m_frameHeight - 10);  break;
				case SB_PAGEUP:     pos -= (m_frameHeight - 10);  break;
				case SB_THUMBTRACK: pos = HIWORD(wParam);         break;
			}
			if (pos > (m_formHeight - m_frameHeight))
				pos = m_formHeight - m_frameHeight;
			if (pos < 0)
				pos = 0;
			if (m_curPos != pos) {
				ScrollWindow(GetDlgItem(m_hwnd, IDC_FRAME), 0, m_curPos - pos, nullptr, &(m_frameRect));
				SetScrollPos(GetDlgItem(m_hwnd, IDC_VSCROLL), SB_CTL, pos, TRUE);
				m_curPos = pos;
			}
		}
		return CJabberDlgBase::DlgProc(msg, wParam, lParam);
	}
};
void CJabberProto::RegisterAgent(HWND hwndParent, char *jid)
{
	auto *pDlg = new CAgentRegDlg(this, jid);
	pDlg->SetParent(hwndParent);
	pDlg->Show();
}
/////////////////////////////////////////////////////////////////////////////////////////
struct TAgentParam
{
	CAgentRegDlg *pDlg;
	bool bSuccess;
	const TiXmlElement *iqNode;
};
static INT_PTR CALLBACK sttFinish(void *param)
{
	auto *p = (TAgentParam *)param;
	if (p->pDlg) {
		if (p->bSuccess)
			p->pDlg->Success(p->iqNode);
		else
			p->pDlg->Fail(JabberErrorMsg(p->iqNode));
	}
	return 0;
}
void CJabberProto::OnIqAgentGetRegister(const TiXmlElement *iqNode, CJabberIqInfo *)
{
	// RECVED: result of the request for (agent) registration mechanism
	// ACTION: activate (agent) registration input dialog
	debugLogA(" iqIdGetRegister");
	const TiXmlElement *queryNode;
	const char *type;
	if ((type = XmlGetAttr(iqNode, "type")) == nullptr) return;
	if ((queryNode = XmlFirstChild(iqNode, "query")) == nullptr) return;
	if (!mir_strcmp(type, "result")) {
		TAgentParam param = { m_pDlgAgentReg, true, iqNode };
		CallFunctionSync(sttFinish, ¶m);
	}
	else if (!mir_strcmp(type, "error")) {
		TAgentParam param = { m_pDlgAgentReg, false, iqNode };
		CallFunctionSync(sttFinish, ¶m);
	}
}
void CJabberProto::AgentShutdown()
{
	UI_SAFE_CLOSE(m_pDlgReg);
	UI_SAFE_CLOSE(m_pDlgAgentReg);
}