//=============================================================================
//
//   File : RegisteredUsersDialog.cpp
//   Creation date : Tue Dec 26 2000 12:24:12 CEST by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
//
//   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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

#define _WANT_OPTION_FLAGS_

#include "RegisteredUserEntryDialog.h"
#include "RegistrationWizard.h"
#include "RegisteredUsersDialog.h"

#include "KviRegisteredUserDataBase.h"
#include "KviLocale.h"
#include "KviIrcMask.h"
#include "kvi_debug.h"
#include "KviMainWindow.h"
#include "KviIconManager.h"
#include "KviApplication.h"
#include "KviOptions.h"
#include "KviFile.h"
#include "KviFileDialog.h"
#include "KviMessageBox.h"
#include "KviFileUtils.h"
#include "kvi_settings.h"
#include "KviStringConversion.h"
#include "KviOptions.h"
#include "KviPointerHashTable.h"
#include "KviTalHBox.h"
#include "KviTalVBox.h"

#include <QLayout>
#include <QLabel>
#include <QImageWriter>
#include <QImageReader>
#include <QImage>
#include <QString>
#include <QComboBox>
#include <QToolTip>
#include <QStyle>
#include <QPainter>
#include <QInputDialog>
#include <QEvent>
#include <QCloseEvent>
#include <QAbstractItemView>
#include <QAbstractTextDocumentLayout>
#include <QShortcut>
#include <QScreen>

#include <memory>

#define LVI_ICON_SIZE 32
#define LVI_BORDER 4
#define LVI_SPACING 8
#define LVI_MINIMUM_TEXT_WIDTH 300
#define LVI_MINIMUM_CELL_WIDTH (LVI_MINIMUM_TEXT_WIDTH + LVI_BORDER + LVI_ICON_SIZE + LVI_SPACING + LVI_BORDER)

extern RegisteredUsersDialog * g_pRegisteredUsersDialog;

KviRegisteredUsersListView::KviRegisteredUsersListView(QWidget * par)
    : QTreeWidget(par)
{
	setColumnCount(2);

	QStringList labels;
	labels << __tr2qs_ctx("Name", "register") << __tr2qs_ctx("Flags", "register");
	setHeaderLabels(labels);
	setColumnWidth(0, 360);
	setColumnWidth(1, 20);

	setAllColumnsShowFocus(true);

	setSelectionBehavior(QAbstractItemView::SelectRows);
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	setRootIsDecorated(true);
	setSortingEnabled(true);

	setToolTip(__tr2qs_ctx("This is the list of registered users. "
	                       "KVIrc can automatically recognize and associate properties to them.<br>"
	                       "Use the buttons on the right to add, edit and remove entries. "
	                       "The \"notify\" column allows you to quickly add users to the notify list. "
	                       "Notify list fine-tuning can be performed by editing the entry properties.", "register"));
}

void KviRegisteredUsersListView::mousePressEvent(QMouseEvent * e)
{
	if(e->button() == Qt::RightButton)
	{
		QTreeWidgetItem * i = itemAt(e->pos());
		if(i)
			emit rightButtonPressed(i, QCursor::pos());
	}
	QTreeWidget::mousePressEvent(e);
}

RegisteredUsersDialogItem::RegisteredUsersDialogItem(QTreeWidgetItem * par, KviRegisteredUser * u)
    : RegisteredUsersDialogItemBase(User, par), m_pUser(u)
{
	QString szTmp;
	QString t = "<b>";
	t += u->name();
	t += "</b> [";
	szTmp = m_pUser->getProperty("notify");
	if(szTmp.isEmpty())
	{
		t += __tr2qs_ctx("Notify disabled", "register");
	}
	else
	{
		t += __tr2qs_ctx("Notify as:", "register");
		t += " ";
		t += szTmp;
	}
	t += "]";
	t += "<br><font size=\"-1\">";
	szTmp = m_pUser->getProperty("comment");
	if(szTmp.isEmpty())
	{
		t += __tr2qs_ctx("No comment set", "register");
	}
	else
	{
		t += __tr2qs_ctx("Comment:", "register");
		t += " ";
		t += m_pUser->getProperty("comment");
	}
	t += "</font>";
	m_pText.setHtml(t);
	m_pText.setTextWidth(-1);
	// 	setText(0,t);
}

RegisteredUsersDialogItem::~RegisteredUsersDialogItem()
    = default;

QString RegisteredUsersDialogItem::key(int, bool) const
{
	return m_pUser->name();
}

void RegisteredUsersDialogItemDelegate::paint(QPainter * p, const QStyleOptionViewItem & option, const QModelIndex & index) const
{

	RegisteredUsersDialogItemBase * item = static_cast<RegisteredUsersDialogItemBase *>(index.internalPointer());

	if(item->type() == RegisteredUsersDialogItemBase::Group)
	{
		//groups
		QStyledItemDelegate::paint(p, option, index);
	}
	else
	{
		QStyleOptionViewItem opt = option;
		initStyleOption(&opt, index);

		if(opt.state & QStyle::State_Selected)
			QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, p);

		RegisteredUsersDialogItem * it = (RegisteredUsersDialogItem *)item;

		if(index.column() == 0)
		{
			QPoint pt = opt.rect.topLeft();
			pt.setX(pt.x() + LVI_BORDER);
			pt.setY(pt.y() + LVI_BORDER);

			p->drawPixmap(pt, *(g_pIconManager->getBigIcon(KVI_BIGICON_REGUSERS)));
			QRect cliprect = QRect(QPoint(0, 0), QSize(opt.rect.width() - (LVI_ICON_SIZE + LVI_SPACING + LVI_BORDER), opt.rect.height()));
			p->save();

			p->translate(opt.rect.x() + LVI_ICON_SIZE + LVI_SPACING, opt.rect.y());
			it->m_pText.drawContents(p, cliprect);
			p->restore();
		}
		else
		{
			if(it->user())
			{
				if(!it->user()->getProperty("notify").isEmpty())
					p->drawPixmap(opt.rect.topLeft().x() + LVI_BORDER, opt.rect.topLeft().y() + LVI_BORDER, *(g_pIconManager->getSmallIcon(KviIconManager::NotifyOnLine)));
				else
					p->drawPixmap(opt.rect.topLeft().x() + LVI_BORDER, opt.rect.topLeft().y() + LVI_BORDER, *(g_pIconManager->getSmallIcon(KviIconManager::NotifyOffLine)));
				if(it->user()->ignoreEnabled())
					p->drawPixmap(opt.rect.topLeft().x() + LVI_BORDER, opt.rect.topLeft().y() + 2 * LVI_BORDER + 16, *(g_pIconManager->getSmallIcon(KviIconManager::Ignore)));
			}
		}
	}
}

QSize RegisteredUsersDialogItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
	RegisteredUsersDialogItemBase * item = static_cast<RegisteredUsersDialogItemBase *>(index.internalPointer());

	if(item->type() == RegisteredUsersDialogItemBase::Group)
	{
		//groups
		return QStyledItemDelegate::sizeHint(option, index);
	}
	else
	{
		//users
		// 		RegisteredUsersDialogItem *it=(RegisteredUsersDialogItem*)item;

		return { 300, LVI_ICON_SIZE + 2 * LVI_BORDER };
	}
}

RegisteredUsersDialog::RegisteredUsersDialog(QWidget * par)
    : QWidget(par)
{
	g_pRegisteredUsersDialog = this;

	g_pLocalRegisteredUserDataBase = new KviRegisteredUserDataBase();
	g_pLocalRegisteredUserDataBase->copyFrom(g_pRegisteredUserDataBase);

	setWindowIcon(*(g_pIconManager->getSmallIcon(KviIconManager::RegUsers)));
	setWindowTitle(__tr2qs_ctx("Registered Users and Configuration - KVIrc", "register"));

	QGridLayout * g = new QGridLayout(this);

	m_pListView = new KviRegisteredUsersListView(this);
	m_pListView->setItemDelegate(new RegisteredUsersDialogItemDelegate());

	connect(m_pListView, SIGNAL(itemPressed(QTreeWidgetItem *, int)), this, SLOT(itemPressed(QTreeWidgetItem *, int)));
	connect(m_pListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem *, int)));

	g->addWidget(m_pListView, 0, 0, 2, 2);

	KviTalVBox * vbox = new KviTalVBox(this);
	vbox->setSpacing(4);
	g->addWidget(vbox, 0, 2);

	m_pWizardAddButton = new QPushButton(__tr2qs_ctx("Add Wizard...", "register"), vbox);
	connect(m_pWizardAddButton, SIGNAL(clicked()), this, SLOT(addWizardClicked()));
	m_pWizardAddButton->setToolTip(__tr2qs_ctx("Add a registered user by means of a user-friendly wizard.", "register"));
	//m_pWizardAddButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::NewItemByWizard)));

	m_pAddButton = new QPushButton(__tr2qs_ctx("&Add...", "register"), vbox);
	connect(m_pAddButton, SIGNAL(clicked()), this, SLOT(addClicked()));
	m_pAddButton->setToolTip(__tr2qs_ctx("Open the edit dialog to create a new user entry.", "register"));
	//m_pAddButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::NewItem)));

	m_pAddGroupButton = new QPushButton(__tr2qs_ctx("&Add Group...", "register"), vbox);
	connect(m_pAddGroupButton, SIGNAL(clicked()), this, SLOT(addGroupClicked()));
	m_pAddGroupButton->setToolTip(__tr2qs_ctx("Adds a new group", "register"));
	//m_pAddGroupButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::NewItem)));

	m_pRemoveButton = new QPushButton(__tr2qs_ctx("Re&move", "register"), vbox);
	connect(m_pRemoveButton, SIGNAL(clicked()), this, SLOT(removeClicked()));
	m_pRemoveButton->setEnabled(false);
	m_pRemoveButton->setToolTip(__tr2qs_ctx("Remove the currently selected entries.", "register"));
	//m_pRemoveButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::DeleteItem)));

	m_pEditButton = new QPushButton(__tr2qs_ctx("&Edit...", "register"), vbox);
	connect(m_pEditButton, SIGNAL(clicked()), this, SLOT(editClicked()));
	m_pEditButton->setEnabled(false);
	m_pEditButton->setToolTip(__tr2qs_ctx("Edit the first selected entry.", "register"));
	//m_pEditButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::EditItem)));

	QFrame * f = new QFrame(vbox);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);

	m_pSelectAllButton = new QPushButton(__tr2qs_ctx("Select All", "register"), vbox);
	connect(m_pSelectAllButton, SIGNAL(clicked()), this, SLOT(selectAllClicked()));
	m_pSelectAllButton->setToolTip(__tr2qs_ctx("Select all the entries", "register"));
	//m_pSelectAllButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Plus)));

	m_pExportButton = new QPushButton(__tr2qs_ctx("Export to...", "register"), vbox);
	m_pExportButton->setEnabled(false);
	connect(m_pExportButton, SIGNAL(clicked()), this, SLOT(exportClicked()));
	m_pExportButton->setToolTip(__tr2qs_ctx("Export the selected entries to a file.<br>All the data associated with the selected registered users will be exported.<br>You (or anyone else) can later import the entries by using the \"Import\" button.", "register"));
	//m_pExportButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Save)));

	m_pImportButton = new QPushButton(__tr2qs_ctx("Import from...", "register"), vbox);
	connect(m_pImportButton, SIGNAL(clicked()), this, SLOT(importClicked()));
	m_pImportButton->setToolTip(__tr2qs_ctx("Import entries from a file exported earlier by the \"export\" function of this dialog.", "register"));
	//m_pImportButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Folder)));

	KviTalHBox * hbox = new KviTalHBox(this);
	hbox->setSpacing(4);
	g->addWidget(hbox, 3, 1, 1, 2);

	QPushButton * b;

	b = new QPushButton(__tr2qs_ctx("&OK", "register"), hbox);
	connect(b, SIGNAL(clicked()), this, SLOT(okClicked()));
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Accept)));
	//b->setMinimumWidth(120);

	b = new QPushButton(__tr2qs_ctx("Cancel", "register"), hbox);
	connect(b, SIGNAL(clicked()), this, SLOT(cancelClicked()));
	b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Discard)));
	//b->setMinimumWidth(120);

	g->addItem(new QSpacerItem(0, 15), 2, 0);
	g->setColumnStretch(0, 1);
	g->setRowStretch(1, 1);

	connect(m_pListView, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
	connect(m_pListView, SIGNAL(rightButtonPressed(QTreeWidgetItem *, QPoint)), this, SLOT(rightButtonPressed(QTreeWidgetItem *, QPoint)));

	new QShortcut(Qt::Key_Escape, this, SLOT(cancelClicked()));

	fillList();

	if(!parent())
	{
		if(KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry).y() < 5)
			KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry)
			    .setY(5);

		//setGeometry(KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry));
		resize(KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry).width(),
		    KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry).height());

		QRect rect = g_pMainWindow->screen()->availableGeometry();
		move(rect.x() + ((rect.width() - KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry).width()) / 2), rect.y() + ((rect.height() - KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry).height()) / 2));
	}
}

RegisteredUsersDialog::~RegisteredUsersDialog()
{
#ifndef COMPILE_ON_MAC
	if(!parent())
		KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry) = QRect(pos().x(), pos().y(), size().width(), size().height());
#else
	if(!parent())
		KVI_OPTION_RECT(KviOption_rectRegisteredUsersDialogGeometry) = geometry();
#endif

	g_pRegisteredUsersDialog = nullptr;

	delete g_pLocalRegisteredUserDataBase;
	g_pLocalRegisteredUserDataBase = nullptr;
}

void RegisteredUsersDialog::itemPressed(QTreeWidgetItem * it, int c)
{
	if(!it)
		return;
	RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)it;
	if(b->type() == RegisteredUsersDialogItemBase::User)
	{
		RegisteredUsersDialogItem * i = (RegisteredUsersDialogItem *)it;

		QRect r = m_pListView->visualItemRect(i);
		int daw = m_pListView->columnWidth(0);

		QPoint ppp = m_pListView->mapFromGlobal(QCursor::pos());

		if((c == 1) && (ppp.x() < (r.height() + 5 + daw)))
		{
			// notify list toggle
			if(i->user()->getProperty("notify").isEmpty())
			{
				// try to find the nicknames to be notified
				QString szMask;

				for(KviIrcMask * m = i->user()->maskList()->first(); m; m = i->user()->maskList()->next())
				{
					QString tmp = m->nick();
					if((tmp.indexOf('*') == -1) && (tmp.indexOf('?') == -1) && (!tmp.isEmpty()))
					{
						if(!szMask.isEmpty())
							szMask.append(' ');
						szMask.append(tmp);
					}
				}
				// if the nickname list is still empty, build a dummy nick to notify
				if(szMask.isEmpty())
				{
					szMask = i->user()->name();
					szMask.replace(" ", "");
					szMask.replace("'", "");
					szMask.replace("&", "");
					szMask.replace(",", "");
				}

				i->user()->setProperty("notify", szMask);
			}
			else
			{
				i->user()->setProperty("notify", QString("")); // kill that
			}

			m_pListView->repaint(r);
		}
	}
}

void RegisteredUsersDialog::itemDoubleClicked(QTreeWidgetItem * it, int)
{
	if(!it)
		return;
	RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)it;
	if(b->type() == RegisteredUsersDialogItemBase::User)
	{
		editItem((RegisteredUsersDialogItem *)it);
	}
	else
	{
		RegisteredUsersGroupItem * i = (RegisteredUsersGroupItem *)b;
		editGroup(i->group());
	}
}

void RegisteredUsersDialog::addGroupClicked()
{
	bool ok;
	QString text = QInputDialog::getText(this, "Add Group Name - KVIrc",
	    __tr2qs_ctx("Group name:", "register"), QLineEdit::Normal, QString(), &ok);

	if(ok && !text.isEmpty())
	{
		g_pLocalRegisteredUserDataBase->addGroup(text);
		fillList();
	}
}

void RegisteredUsersDialog::editGroup(KviRegisteredUserGroup * group)
{
	bool ok;

	QString text = QInputDialog::getText(this, "Change Group Name - KVIrc",
	    __tr2qs_ctx("Group name:", "register"), QLineEdit::Normal, group->name(), &ok);

	if(ok && !text.isEmpty())
	{
		QString szOldGroup = group->name();
		g_pLocalRegisteredUserDataBase->groupDict()->setAutoDelete(false);
		g_pLocalRegisteredUserDataBase->groupDict()->remove(szOldGroup);
		g_pLocalRegisteredUserDataBase->groupDict()->setAutoDelete(true);
		group->setName(text);
		g_pLocalRegisteredUserDataBase->groupDict()->insert(text, group);

		KviPointerHashTable<QString, KviRegisteredUser> * d = g_pLocalRegisteredUserDataBase->userDict();

		for(KviRegisteredUser * u = d->first(); u; u = d->next())
		{
			if(u->group() == szOldGroup)
				u->setGroup(text);
		}

		fillList();

		// select the last edited item
		for(int c = 0; c < m_pListView->topLevelItemCount(); c++)
		{
			QTreeWidgetItem * pGroup = m_pListView->topLevelItem(c);
			if(KviQString::equalCI(pGroup->text(0), text))
			{
				pGroup->setSelected(true);
				m_pListView->setCurrentItem(pGroup);
				break;
			}
		}
	}
}

void RegisteredUsersDialog::rightButtonPressed(QTreeWidgetItem * pItem, QPoint pnt)
{
	if(pItem)
	{
		RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)pItem;
		if(b->type() == RegisteredUsersDialogItemBase::User)
		{
			QMenu * groups = new QMenu;

			KviPointerHashTable<QString, KviRegisteredUserGroup> * pGroups = g_pLocalRegisteredUserDataBase->groupDict();
			for(KviPointerHashTableEntry<QString, KviRegisteredUserGroup> * g = pGroups->firstEntry(); g; g = pGroups->nextEntry())
			{
				groups->addAction(g->key())
				    ->setData(g->data()->name());
			}

			connect(groups, SIGNAL(triggered(QAction *)), this, SLOT(moveToGroupMenuClicked(QAction *)));

			QMenu * mainPopup = new QMenu;
			mainPopup->addAction(__tr2qs_ctx("Move to Group", "register"))->setMenu(groups);
			mainPopup->exec(pnt);
		}
	}
}

void RegisteredUsersDialog::moveToGroupMenuClicked(QAction * pAction)
{
	QString szGroup = pAction->data().toString();

	QList<QTreeWidgetItem *> list = m_pListView->selectedItems();
	for(int i = 0; i < list.count(); i++)
	{
		RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)(list.at(i));
		if(b->type() == RegisteredUsersDialogItemBase::User)
		{
			((RegisteredUsersDialogItem *)list.at(i))->user()->setGroup(szGroup);
		}
	}
	fillList();
}

void RegisteredUsersDialog::fillList()
{
	m_pListView->clear();
	KviPointerHashTable<QString, RegisteredUsersGroupItem> groupItems(5, false);
	groupItems.setAutoDelete(false);

	KviPointerHashTable<QString, KviRegisteredUserGroup> * pGroups = g_pLocalRegisteredUserDataBase->groupDict();
	for(KviRegisteredUserGroup * g = pGroups->first(); g; g = pGroups->next())
	{
		RegisteredUsersGroupItem * pCur = new RegisteredUsersGroupItem(m_pListView, g);
		groupItems.insert(g->name(), pCur);
		pCur->setExpanded(true);
	}

	KviPointerHashTable<QString, KviRegisteredUser> * d = g_pLocalRegisteredUserDataBase->userDict();
	for(KviRegisteredUser * u = d->first(); u; u = d->next())
	{
		if(u->group().isEmpty())
			u->setGroup(__tr2qs_ctx("Default", "register"));
		if(groupItems.find(u->group()))
		{
			(void)new RegisteredUsersDialogItem(groupItems.find(u->group()), u);
		}
		else if(groupItems.find(__tr2qs_ctx("Default", "register")))
		{
			(void)new RegisteredUsersDialogItem(groupItems.find(__tr2qs_ctx("Default", "register")), u);
		}
		else
		{ //should never be called
			KviRegisteredUserGroup * pGroup = g_pLocalRegisteredUserDataBase->addGroup(__tr2qs_ctx("Default", "register"));
			RegisteredUsersGroupItem * pCur = new RegisteredUsersGroupItem(m_pListView, pGroup);
			groupItems.insert(__tr2qs_ctx("Default", "register"), pCur);
			(void)new RegisteredUsersDialogItem(pCur, u);
		}
	}
	if(m_pListView->topLevelItem(0))
	{
		m_pListView->topLevelItem(0)->setSelected(true);
		m_pListView->setCurrentItem(m_pListView->topLevelItem(0));
	}
}

void RegisteredUsersDialog::closeEvent(QCloseEvent * e)
{
	m_pListView->clear();
	e->accept();
	okClicked();
	//delete this;
}

void RegisteredUsersDialog::okClicked()
{
	m_pListView->clear();
	g_pRegisteredUserDataBase->copyFrom(g_pLocalRegisteredUserDataBase);
	g_pApp->restartNotifyLists();
	delete this;
}

void RegisteredUsersDialog::cancelClicked()
{
	m_pListView->clear();
	delete this;
}

void RegisteredUsersDialog::addClicked()
{
	RegisteredUserEntryDialog * dlg = new RegisteredUserEntryDialog(this, nullptr);
	int ret = dlg->exec();
	delete dlg;

	if(!g_pRegisteredUsersDialog)
		return; // we have been deleted!

	if(ret == QDialog::Accepted)
	{
		fillList();
	}
}

void RegisteredUsersDialog::addWizardClicked()
{
	RegistrationWizard * w = new RegistrationWizard("", g_pLocalRegisteredUserDataBase, this, true);
	int ret = w->exec();
	delete w;
	if(!g_pRegisteredUsersDialog)
		return; // we have been deleted!
	if(ret == QDialog::Accepted)
	{
		fillList();
	}
}

void RegisteredUsersDialog::removeClicked()
{
	QList<QTreeWidgetItem *> list = m_pListView->selectedItems();
	for(int i = 0; i < list.count(); i++)
	{
		RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)(list.at(i));
		if(b->type() == RegisteredUsersDialogItemBase::User)
		{
			g_pLocalRegisteredUserDataBase->removeUser(((RegisteredUsersDialogItem *)list.at(i))->user()->name());
		}
		else
		{
			g_pLocalRegisteredUserDataBase->removeGroup(((RegisteredUsersGroupItem *)list.at(i))->group()->name());
		}
	}
	fillList();
}

void RegisteredUsersDialog::editClicked()
{
	RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)(m_pListView->currentItem());
	if(!b)
		return;
	if(b->type() == RegisteredUsersDialogItemBase::User)
	{
		RegisteredUsersDialogItem * i = (RegisteredUsersDialogItem *)b;
		editItem(i);
	}
	else
	{
		RegisteredUsersGroupItem * i = (RegisteredUsersGroupItem *)b;
		editGroup(i->group());
	}
}

void RegisteredUsersDialog::editItem(RegisteredUsersDialogItem * i)
{
	KviRegisteredUser * u = i->user();

	i->setUser(nullptr);

	QString szName = u->name();

	RegisteredUserEntryDialog * dlg = new RegisteredUserEntryDialog(this, u);
	int res = dlg->exec();
	delete dlg;

	if(!g_pRegisteredUsersDialog)
		return; // we have been deleted!

	if(res == QDialog::Accepted)
	{
		fillList();

		// select the last edited item
		for(int c = 0; c < m_pListView->topLevelItemCount(); c++)
		{
			QTreeWidgetItem * pGroup = m_pListView->topLevelItem(c);
			for(int d = 0; d < pGroup->childCount(); d++)
			{
				RegisteredUsersDialogItem * pUser = (RegisteredUsersDialogItem *)pGroup->child(d);
				if(KviQString::equalCI(pUser->user()->name(), szName))
				{
					pUser->setSelected(true);
					m_pListView->setCurrentItem(pUser);
					break;
				}
			}
		}

		return;
	}

	i->setUser(u);
	m_pListView->update();
}

void RegisteredUsersDialog::selectionChanged()
{
	bool bHaveSelected = !m_pListView->selectedItems().empty();

	m_pEditButton->setEnabled(bHaveSelected);
	m_pRemoveButton->setEnabled(bHaveSelected);
	m_pExportButton->setEnabled(bHaveSelected);
}

#define KVI_REGUSER_DB_FILE_MAGIC 0x5334DBDB
#define KVI_REGUSER_DB_FILE_VERSION 1

struct KviReguserDbFileHeader
{
	kvi_u32_t magic;
	kvi_u32_t version;
	kvi_u32_t nentries;
};

void RegisteredUsersDialog::exportClicked()
{
	unsigned int nEntries = 0;

	QList<QTreeWidgetItem *> list = m_pListView->selectedItems();

	//only "user" entries matters, discard groups from count
	for(int i = 0; i < list.count(); i++)
	{
		if(((RegisteredUsersDialogItemBase *)list.at(i))->type() == RegisteredUsersDialogItemBase::User)
			nEntries++;
	}

	if(nEntries < 1)
	{
		KviMessageBox::warning(__tr2qs_ctx("No entries selected.", "register"));
		return;
	}

	QString buffer;

	if(!KviFileDialog::askForSaveFileName(buffer, __tr2qs_ctx("Enter a Filename - KVIrc", "register"), nullptr, nullptr, true, true, true, this))
		return;

	if(!g_pRegisteredUsersDialog)
		return; // we have been deleted!

	KviFile f(buffer);
	if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate))
	{
		KviMessageBox::warning(__tr2qs_ctx("Can't open file %Q for writing.", "register"), &buffer);
		return;
	}

	KviReguserDbFileHeader hf;
	hf.magic = KVI_REGUSER_DB_FILE_MAGIC;
	hf.version = KVI_REGUSER_DB_FILE_VERSION;
	hf.nentries = nEntries;

	if(f.write((const char *)&hf, sizeof(KviReguserDbFileHeader)) != sizeof(KviReguserDbFileHeader))
		goto write_error;

	for(int i = 0; i < list.count(); i++)
	{
		RegisteredUsersDialogItemBase * pBase = (RegisteredUsersDialogItemBase *)list.at(i);
		if(pBase->type() != RegisteredUsersDialogItemBase::User)
			continue;

		KviRegisteredUser * u = ((RegisteredUsersDialogItem *)list.at(i))->user();
		if(u)
		{
			if(!f.save(u->name()))
				goto write_error;
			KviPointerHashTable<QString, QString> * pd = u->propertyDict();
			if(pd)
			{
				if(!f.save(pd->count()))
					goto write_error;
				for(KviPointerHashTableEntry<QString, QString> * pCur = pd->firstEntry(); pCur; pCur = pd->nextEntry())
				{
					QString key = pCur->key();
					if(!f.save(key))
						goto write_error;
					if(!f.save(*(pCur->data())))
						goto write_error;
				}
			}
			else
			{
				if(!f.save(0))
					goto write_error;
			}

			KviPointerList<KviIrcMask> * ml = u->maskList();
			if(ml)
			{
				if(!f.save(ml->count()))
					goto write_error;
				for(KviIrcMask * m = ml->first(); m; m = ml->next())
				{
					QString fullMask;
					m->mask(fullMask, KviIrcMask::NickUserHost);
					if(!f.save(fullMask))
						goto write_error;
				}
			}
			else
			{
				if(!f.save(0))
					goto write_error;
			}

			QString avatar;
			if(u->getProperty("avatar", avatar))
			{
				std::unique_ptr<KviAvatar> av{g_pIconManager->getAvatar(QString(), avatar)};
				if(av)
				{
					if(!av->pixmap()->isNull())
					{
						if(!f.save(1))
							goto write_error;
						QImageWriter io;
						io.setDevice(&f);
						io.setFormat("PNG");
						if(!io.write(av->pixmap()->toImage()))
							goto write_error;
					}
					else
					{
						if(!f.save(0))
							goto write_error;
					}
				}
				else
				{
					if(!f.save(0))
						goto write_error;
				}
			}
			else
			{
				if(!f.save(0))
					goto write_error;
			}
		}
	}

	goto succesfull_export;

write_error:
	KviMessageBox::warning(__tr2qs_ctx("Can't export the registered users database: write error.", "register"));
	f.close();
	return;

succesfull_export:

	f.close();
}

void RegisteredUsersDialog::selectAllClicked()
{
	m_pListView->selectAll();
}

void RegisteredUsersDialog::importClicked()
{
	//KviCString buffer;
	QString buffer;

	if(!KviFileDialog::askForOpenFileName(buffer, __tr2qs_ctx("Select a File - KVIrc", "register"), QString(), QString(), false, true, this))
		return;

	if(!g_pRegisteredUsersDialog)
		return; // we have been deleted!

	KviFile f(buffer);
	if(!f.open(QIODevice::ReadOnly))
	{
		KviMessageBox::warning(__tr2qs_ctx("Can't open file %Q for reading.", "register"), &buffer);
		return;
	}

	KviReguserDbFileHeader hf;
	unsigned int idx;

	if(f.read((char *)&hf, sizeof(KviReguserDbFileHeader)) != sizeof(KviReguserDbFileHeader))
		goto read_error;

	if(hf.magic != KVI_REGUSER_DB_FILE_MAGIC)
	{
		KviMessageBox::warning(__tr2qs_ctx("The file %Q doesn't appear to be a valid registered users database.", "register"), &buffer);
		f.close();
		return;
	}

	if(hf.version != KVI_REGUSER_DB_FILE_VERSION)
	{
		KviMessageBox::warning(__tr2qs_ctx("The file %Q contains an invalid registered users database version.", "register"), &buffer);
		f.close();
		return;
	}

	for(idx = 0; idx < hf.nentries; idx++)
	{
		QString szName;
		if(!f.load(szName))
			goto read_error;
		KviRegisteredUser * u = g_pLocalRegisteredUserDataBase->getUser(szName);
		if(!u)
			goto read_error;
		unsigned int count = 0;
		if(!f.load(count))
			goto read_error;
		for(unsigned int up = 0; up < count; up++)
		{
			QString szKey, szValue;
			if(!f.load(szKey))
				goto read_error;
			if(!f.load(szValue))
				goto read_error;
			u->setProperty(szKey, szValue);
		}
		if(!f.load(count))
			goto read_error;
		for(unsigned int um = 0; um < count; um++)
		{
			QString szMask;
			if(!f.load(szMask))
				goto read_error;
			if(!szMask.isEmpty())
			{
				KviIrcMask * m = new KviIrcMask(szMask);
				g_pLocalRegisteredUserDataBase->addMask(u, m);
			}
		}
		if(!f.load(count))
			goto read_error;
		if(count)
		{
			// there is an avatar
			QImage img;
			QImageReader io;
			io.setDevice(&f);
			io.setFormat("PNG");
			img = io.read();
			//			if(io.read())goto read_error;

			if(img.isNull())
				qDebug("Oops! Read a null image?");

			QString fName = u->name();
			KviFileUtils::adjustFilePath(fName);

			QString fPath;
			int rnm = 0;
			do
			{
				g_pApp->getLocalKvircDirectory(fPath, KviApplication::Avatars, fName);
				fPath.append(QString("%1.png").arg(rnm));
				rnm++;
			} while(KviFileUtils::fileExists(fPath));

			if(!img.save(fPath, "PNG"))
			{
				qDebug("Can't save image %s", fPath.toUtf8().data());
			}
			else
			{
				u->setProperty("avatar", fPath);
			}
		}
	}

	goto succesfull_import;

read_error:
	KviMessageBox::warning(__tr2qs_ctx("Can't import the registered users database: read error.", "register"));
	f.close();
	return;

succesfull_import:

	f.close();
	fillList();
}
