/*******************************************************************************
 * retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp                          *
 *                                                                             *
 * Copyright 2012-2013  by Robert Fernie      <retroshare.project@gmail.com>   *
 *                                                                             *
 * This program is free software: you can redistribute it and/or modify        *
 * it under the terms of the GNU Affero General Public License as              *
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.                         *
 *                                                                             *
 * You should have received a copy of the GNU Affero General Public License    *
 * along with this program. If not, see <https://www.gnu.org/licenses/>.       *
 *                                                                             *
 *******************************************************************************/

#include <QMenu>
#include <QMessageBox>
#include <QToolButton>

#include "GxsGroupFrameDialog.h"
#include "ui_GxsGroupFrameDialog.h"
#include "GxsMessageFrameWidget.h"

#include "gui/settings/rsharesettings.h"
#include "gui/RetroShareLink.h"
#include "gui/gxs/GxsGroupShareKey.h"
#include "gui/common/GroupTreeWidget.h"
#include "gui/common/RSTreeWidget.h"
#include "gui/notifyqt.h"
#include "gui/common/UIStateHelper.h"
#include "gui/common/UserNotify.h"
#include "util/qtthreadsutils.h"
#include "retroshare/rsgxsifacetypes.h"
#include "GxsCommentDialog.h"

//#define DEBUG_GROUPFRAMEDIALOG

/* Images for TreeWidget */
#define IMAGE_SUBSCRIBE      ":/images/edit_add24.png"
#define IMAGE_UNSUBSCRIBE    ":/icons/cancel.svg"
#define IMAGE_INFO           ":/images/info16.png"
#define IMAGE_COPYLINK       ":/images/copyrslink.png"
#define IMAGE_EDIT           ":/icons/png/pencil-edit-button.png"
#define IMAGE_TABNEW         ":/icons/newtab.svg"
#define IMAGE_DELETE         ":/images/delete.png"
#define IMAGE_RETRIEVE       ":/images/edit_add24.png"
#define IMAGE_COMMENT        ""

#define TOKEN_TYPE_GROUP_SUMMARY    1
//#define TOKEN_TYPE_SUBSCRIBE_CHANGE 2
//#define TOKEN_TYPE_CURRENTGROUP     3
#define TOKEN_TYPE_STATISTICS       4

#define MAX_COMMENT_TITLE 32

static const uint32_t DELAY_BETWEEN_GROUP_STATISTICS_UPDATE = 120; // do not update group statistics more often than once every 2 mins

/*
 * Transformation Notes:
 *   there are still a couple of things that the new groups differ from Old version.
 *   these will need to be addressed in the future.
 *     -> Child TS (for sorting) is not handled by GXS, this will probably have to be done in the GUI.
 *     -> Need to handle IDs properly.
 *     -> Much more to do.
 */

/** Constructor */
GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl,const QString& settings_name, QWidget *parent,bool allow_dist_sync)
: MainPage(parent),mSettingsName(settings_name)
{
	/* Invoke the Qt Designer generated object setup routine */
	ui = new Ui::GxsGroupFrameDialog();
	ui->setupUi(this);

	mShouldUpdateMessageSummaryList = true;
	mShouldUpdateGroupStatistics = false;
    mLastGroupStatisticsUpdateTs=0;
	mInitialized = false;
	mDistSyncAllowed = allow_dist_sync;
	mInFill = false;
	mCountChildMsgs = false;
	mYourGroups = NULL;
	mSubscribedGroups = NULL;
	mPopularGroups = NULL;
	mOtherGroups = NULL;

	/* Setup Queue */
	mInterface = ifaceImpl;

	/* Setup UI helper */
	mStateHelper = new UIStateHelper(this);

	mStateHelper->addWidget(TOKEN_TYPE_GROUP_SUMMARY, ui->loadingLabel, UISTATE_LOADING_VISIBLE);
	mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, true);

	connect(ui->groupTreeWidget, SIGNAL(treeCustomContextMenuRequested(QPoint)), this, SLOT(groupTreeCustomPopupMenu(QPoint)));
	connect(ui->groupTreeWidget, SIGNAL(treeCurrentItemChanged(QString)), this, SLOT(changedCurrentGroup(QString)));
	connect(ui->groupTreeWidget->treeWidget(), SIGNAL(signalMouseMiddleButtonClicked(QTreeWidgetItem*)), this, SLOT(groupTreeMiddleButtonClicked(QTreeWidgetItem*)));
	connect(ui->messageTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(messageTabCloseRequested(int)));
	connect(ui->messageTabWidget, SIGNAL(currentChanged(int)), this, SLOT(messageTabChanged(int)));

	connect(ui->todoPushButton, SIGNAL(clicked()), this, SLOT(todo()));

	ui->groupTreeWidget->setDistSearchVisible(allow_dist_sync) ;

    if(allow_dist_sync)
		connect(ui->groupTreeWidget, SIGNAL(distantSearchRequested(const QString&)), this, SLOT(searchNetwork(const QString&)));

	/* Set initial size the splitter */
	ui->splitter->setStretchFactor(0, 0);
	ui->splitter->setStretchFactor(1, 1);

	QList<int> sizes;
	sizes << 300 << width(); // Qt calculates the right sizes
	ui->splitter->setSizes(sizes);

	// load settings
	processSettings(true);

#ifndef UNFINISHED
	ui->todoPushButton->hide();
#endif
}

GxsGroupFrameDialog::~GxsGroupFrameDialog()
{
	// save settings
	processSettings(false);

	delete(ui);
}

void GxsGroupFrameDialog::getGroupList(std::map<RsGxsGroupId, RsGroupMetaData> &group_list)
{
	group_list = mCachedGroupMetas ;

	if(group_list.empty())
		updateGroupSummary();
    else
        std::cerr << "************** Using cached GroupMetaData" << std::endl;
}
void GxsGroupFrameDialog::initUi()
{
	registerHelpButton(ui->helpButton, getHelpString(),pageName()) ;

    ui->titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(icon(ICON_NAME)));
	ui->titleBarLabel->setText(text(TEXT_NAME));

	/* Initialize group tree */
	QToolButton *newGroupButton = new QToolButton(this);
    newGroupButton->setIcon(FilesDefs::getIconFromQtResourcePath(icon(ICON_NEW)));
	newGroupButton->setToolTip(text(TEXT_NEW));
	connect(newGroupButton, SIGNAL(clicked()), this, SLOT(newGroup()));
	ui->groupTreeWidget->addToolButton(newGroupButton);

	/* Create group tree */
    mYourGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_YOUR_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_YOUR_GROUP)), true);
    mSubscribedGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_SUBSCRIBED_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_SUBSCRIBED_GROUP)), true);
    mPopularGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_POPULAR_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_POPULAR_GROUP)), false);
    mOtherGroups = ui->groupTreeWidget->addCategoryItem(text(TEXT_OTHER_GROUP), FilesDefs::getIconFromQtResourcePath(icon(ICON_OTHER_GROUP)), false);

	if (text(TEXT_TODO).isEmpty()) {
		ui->todoPushButton->hide();
	}

	// load settings
	processSettings(true);

	if (groupFrameSettingsType() != GroupFrameSettings::Nothing) {
		connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged()));
		settingsChanged();
	}

	mInitialized = true;
}

void GxsGroupFrameDialog::showEvent(QShowEvent* /*event*/)
{
	if (!mInitialized )
	{
		/* Problem: virtual methods cannot be used in constructor */

		initUi();
	}

    uint32_t children =  mYourGroups->childCount() + mSubscribedGroups->childCount() + mPopularGroups->childCount() + mOtherGroups->childCount();

    bool empty = mCachedGroupMetas.empty() || children==0;

    updateDisplay( empty );
}

void GxsGroupFrameDialog::paintEvent(QPaintEvent *pe)
{
	if(mShouldUpdateMessageSummaryList)
	{
		if(!mGroupIdsSummaryToUpdate.empty())
			for(auto& group_id: mGroupIdsSummaryToUpdate)
				updateMessageSummaryListReal(group_id);
		else
			updateMessageSummaryListReal(RsGxsGroupId());

		mShouldUpdateMessageSummaryList = false;
		mGroupIdsSummaryToUpdate.clear();
	}

    rstime_t now = time(nullptr);

    if(mShouldUpdateGroupStatistics &&  now > DELAY_BETWEEN_GROUP_STATISTICS_UPDATE + mLastGroupStatisticsUpdateTs)
    {
        // This mechanism allows to gather multiple updateGroupStatistics events at once and not send too many of them at the same time.
        // it avoids re-loadign all the group everytime a friend sends new statistics.

        for(auto& groupId: mGroupStatisticsToUpdate)
			updateGroupStatisticsReal(groupId);

        mShouldUpdateGroupStatistics = false;
        mLastGroupStatisticsUpdateTs = time(nullptr);
        mGroupStatisticsToUpdate.clear();
    }

    MainPage::paintEvent(pe);
}

void GxsGroupFrameDialog::processSettings(bool load)
{
	if (mSettingsName.isEmpty()) {
		return;
	}

	Settings->beginGroup(mSettingsName);

	if (load) {
		// load settings

		// state of splitter
		ui->splitter->restoreState(Settings->value("Splitter").toByteArray());
	} else {
		// save settings

		// state of splitter
		Settings->setValue("Splitter", ui->splitter->saveState());
	}

	ui->groupTreeWidget->processSettings(load);

	Settings->endGroup();
}

bool GxsGroupFrameDialog::useTabs()
{
	GroupFrameSettings groupFrameSettings;

	return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings)  && groupFrameSettings.mOpenAllInNewTab;
}

void GxsGroupFrameDialog::settingsChanged()
{
	GroupFrameSettings groupFrameSettings;
	if (Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings)) {
		setSingleTab(!groupFrameSettings.mOpenAllInNewTab);
		setHideTabBarWithOneTab(groupFrameSettings.mHideTabBarWithOneTab);
	}
}

void GxsGroupFrameDialog::setSingleTab(bool singleTab)
{
	if (singleTab)
    {
        while(ui->messageTabWidget->count() > 1)
        {
            auto w = ui->messageTabWidget->widget(0) ;
            ui->messageTabWidget->removeTab(0);
            delete w;
        }
		ui->messageTabWidget->hideCloseButton(0);
	}
}

void GxsGroupFrameDialog::setHideTabBarWithOneTab(bool hideTabBarWithOneTab)
{
	ui->messageTabWidget->setHideTabBarWithOneTab(hideTabBarWithOneTab);
}

void GxsGroupFrameDialog::updateDisplay(bool complete)
{
    if(complete)    // || !getGrpIds().empty() || !getGrpIdsMeta().empty()) {
		updateGroupSummary(); /* Update group list */

//    updateSearchResults() ;
}

void GxsGroupFrameDialog::updateSearchResults()
{
    for(auto& it:mSearchGroupsItems)
        updateSearchResults(it.first);
}

void GxsGroupFrameDialog::updateSearchResults(const TurtleRequestId& sid)
{
	std::cerr << "updating search ID " << std::hex << sid << std::dec << std::endl;

	std::map<RsGxsGroupId,RsGxsGroupSearchResults> group_infos;

	getDistantSearchResults(sid,group_infos) ;

	std::cerr << "retrieved " << std::endl;

	auto it2 = mSearchGroupsItems.find(sid);

    if(it2 == mSearchGroupsItems.end())
    {
        std::cerr << "(EE) received a channel group turtle search result with ID " << sid << " but no item is known for this search" << std::endl;
        return;
    }

	QList<GroupItemInfo> group_items ;

	for(auto it3(group_infos.begin());it3!=group_infos.end();++it3)
	{
		std::cerr << "  adding group " << it3->first << " " << it3->second.mGroupId << " \"" << it3->second.mGroupName << "\"" << std::endl;
		for(auto& s:it3->second.mSearchContexts)
			std::cerr << "    Context string \"" << s << "\"" << std::endl;

		GroupItemInfo i;
		i.id             = QString(it3->second.mGroupId.toStdString().c_str());
		i.name           = QString::fromUtf8(it3->second.mGroupName.c_str());
		i.popularity     = 0; // could be set to the number of hits
		i.lastpost       = QDateTime::fromTime_t(it3->second.mLastMessageTs);
		i.subscribeFlags = 0; // irrelevant here
		i.publishKey     = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags);
		i.adminKey       = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags);
		i.max_visible_posts = it3->second.mNumberOfMessages;
		i.context_strings = it3->second.mSearchContexts;

		group_items.push_back(i);
	}

	ui->groupTreeWidget->fillGroupItems(it2->second, group_items);
}

void GxsGroupFrameDialog::todo()
{
	QMessageBox::information(this, "Todo", text(TEXT_TODO));
}

void GxsGroupFrameDialog::removeCurrentSearch()
{
    QAction *action = dynamic_cast<QAction*>(sender()) ;

    if(!action)
        return ;

    TurtleRequestId search_request_id = action->data().toUInt();

    auto it = mSearchGroupsItems.find(search_request_id) ;

    if(it == mSearchGroupsItems.end())
        return ;

    ui->groupTreeWidget->removeSearchItem(it->second) ;
    mSearchGroupsItems.erase(it);

    mKnownGroups.erase(search_request_id);

    clearDistantSearchResults(search_request_id);
}

void GxsGroupFrameDialog::removeAllSearches()
{
    for(auto it(mSearchGroupsItems.begin());it!=mSearchGroupsItems.end();++it)
    {
        QString group_id;
        TurtleRequestId search_request_id;

        if(ui->groupTreeWidget->isSearchRequestResultItem(it->second,group_id,search_request_id))
			clearDistantSearchResults(search_request_id);

		ui->groupTreeWidget->removeSearchItem(it->second) ;
    }
    mSearchGroupsItems.clear();
    mKnownGroups.clear();
}

// Same function than the one in rsgxsnetservice.cc, so that all times are automatically consistent

uint32_t GxsGroupFrameDialog::checkDelay(uint32_t time_in_secs)
{
    if(time_in_secs <    1 * 86400)
        return 0        ;
    if(time_in_secs <=  10 * 86400)
        return 5 * 86400;
    if(time_in_secs <=  20 * 86400)
        return 15 * 86400;
    if(time_in_secs <=  60 * 86400)
        return 30 * 86400;
    if(time_in_secs <= 120 * 86400)
        return 90 * 86400;
    if(time_in_secs <= 250 * 86400)
        return 180 * 86400;

   return 365 * 86400;
}

void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
{
	QMenu contextMnu(this);

	// First separately handle the case of search top level items
	TurtleRequestId search_request_id = 0 ;
	QString group_id_s;
	if(ui->groupTreeWidget->isSearchRequestItem(point,search_request_id))
	{
		contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DELETE), tr("Remove this search"), this, SLOT(removeCurrentSearch()))->setData(search_request_id);
		contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_DELETE), tr("Remove all searches"), this, SLOT(removeAllSearches()));
	}

	// Then check whether we have a searched item, or a normal group
	else if(ui->groupTreeWidget->isSearchRequestResult(point,group_id_s,search_request_id))
	{
		contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_RETRIEVE), tr("Request data"), this, SLOT(distantRequestGroupData()))->setData(group_id_s);
	}

	else // Group Id
	{
		QString id = ui->groupTreeWidget->itemIdAt(point);
		if (!id.isEmpty())
		{
			mGroupId = RsGxsGroupId(id.toStdString());
			int subscribeFlags = ui->groupTreeWidget->subscribeFlags(QString::fromStdString(mGroupId.toStdString()));

			bool isAdmin      = IS_GROUP_ADMIN(subscribeFlags);
			bool isPublisher  = IS_GROUP_PUBLISHER(subscribeFlags);
			bool isSubscribed = IS_GROUP_SUBSCRIBED(subscribeFlags);

			QAction *action;

			action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));

			if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here
				action->setEnabled(false);

			if (isSubscribed) {
				action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup()));
				action->setEnabled (!mGroupId.isNull() && IS_GROUP_SUBSCRIBED(subscribeFlags));
			} else {
				action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Subscribe"), this, SLOT(subscribeGroup()));
				action->setDisabled (mGroupId.isNull() || IS_GROUP_SUBSCRIBED(subscribeFlags));
			}

			contextMnu.addSeparator();

			contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(icon(ICON_NEW)), text(TEXT_NEW), this, SLOT(newGroup()));

			action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_INFO), tr("Show Details"), this, SLOT(showGroupDetails()));
			action->setEnabled (!mGroupId.isNull());

			action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT), tr("Edit Details"), this, SLOT(editGroupDetails()));
			action->setEnabled (!mGroupId.isNull() && isAdmin);

			uint32_t current_store_time = checkDelay(mInterface->getStoragePeriod(mGroupId))/86400 ;
			uint32_t current_sync_time  = checkDelay(mInterface->getSyncPeriod(mGroupId))/86400 ;

			std::cerr << "Got sync=" << current_sync_time << ". store=" << current_store_time << std::endl;
			QAction *actnn = NULL;

			QMenu *ctxMenu2 = contextMnu.addMenu(tr("Synchronise posts of last...")) ;
			actnn = ctxMenu2->addAction(tr(" 5 days"     ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(  5)) ; if(current_sync_time ==  5) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 2 weeks"    ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_sync_time == 15) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 1 month"    ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_sync_time == 30) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 3 months"   ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_sync_time == 90) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 6 months"   ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_sync_time ==180) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 1 year  "   ),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_sync_time ==365) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setSyncPostsDelay())) ; actnn->setData(QVariant(  0)) ; if(current_sync_time ==  0) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			ctxMenu2->setEnabled(isSubscribed);

			ctxMenu2 = contextMnu.addMenu(tr("Store posts for at most...")) ;
			actnn = ctxMenu2->addAction(tr(" 5 days"     ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(  5)) ; if(current_store_time ==  5) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 2 weeks"    ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 15)) ; if(current_store_time == 15) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 1 month"    ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 30)) ; if(current_store_time == 30) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 3 months"   ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant( 90)) ; if(current_store_time == 90) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 6 months"   ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(180)) ; if(current_store_time ==180) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" 1 year  "   ),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(365)) ; if(current_store_time ==365) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			actnn = ctxMenu2->addAction(tr(" Indefinitly"),this,SLOT(setStorePostsDelay())) ; actnn->setData(QVariant(  0)) ; if(current_store_time ==  0) { actnn->setEnabled(false);actnn->setIcon(FilesDefs::getIconFromQtResourcePath(":/images/start.png"));}
			ctxMenu2->setEnabled(isSubscribed);

			if (shareKeyType()) {
				action = contextMnu.addAction(QIcon(""), tr("Share publish permissions..."), this, SLOT(sharePublishKey()));
				action->setEnabled(!mGroupId.isNull() && isPublisher);
			}

			if (getLinkType() != RetroShareLink::TYPE_UNKNOWN) {
				action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyGroupLink()));
				action->setEnabled(!mGroupId.isNull());
			}

			contextMnu.addSeparator();

			action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark all as read"), this, SLOT(markMsgAsRead()));
			action->setEnabled (!mGroupId.isNull() && isSubscribed);

			action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark all as unread"), this, SLOT(markMsgAsUnread()));
			action->setEnabled (!mGroupId.isNull() && isSubscribed);

			/* Add special actions */
			QList<QAction*> actions;
			groupTreeCustomActions(mGroupId, subscribeFlags, actions);
			if (!actions.isEmpty()) {
				contextMnu.addSeparator();
				contextMnu.addActions(actions);
			}
		}
	}
	//Add Standard Menu
	ui->groupTreeWidget->treeWidget()->createStandardContextMenu(&contextMnu);

	contextMnu.exec(QCursor::pos());
}

void GxsGroupFrameDialog::setStorePostsDelay()
{
    QAction *action = dynamic_cast<QAction*>(sender()) ;

    if(!action || mGroupId.isNull())
    {
        std::cerr << "(EE) Cannot find action/group that called me! Group is " << mGroupId << ", action is " << (void*)action << "  " << __PRETTY_FUNCTION__ << std::endl;
        return;
    }

    uint32_t duration = action->data().toUInt() ;

    std::cerr << "Data is " << duration << std::endl;

 	mInterface->setStoragePeriod(mGroupId,duration * 86400) ;

    // If the sync is larger, we reduce it. No need to sync more than we store. The machinery below also takes care of this.
    //
    uint32_t sync_period = mInterface->getSyncPeriod(mGroupId);

    if(duration > 0)      // the >0 test is to discard the indefinitly test. Basically, if we store for less than indefinitly, the sync is reduced accordingly.
    {
        if(sync_period == 0 || sync_period > duration*86400)
        {
			mInterface->setSyncPeriod(mGroupId,duration * 86400) ;

            std::cerr << "(II) auto adjusting sync period to " << duration<< " days as well." << std::endl;
        }
    }
}


void GxsGroupFrameDialog::setSyncPostsDelay()
{
    QAction *action = dynamic_cast<QAction*>(sender()) ;

    if(!action || mGroupId.isNull())
    {
        std::cerr << "(EE) Cannot find action/group that called me! Group is " << mGroupId << ", action is " << (void*)action << "  " << __PRETTY_FUNCTION__ << std::endl;
        return;
    }

    uint32_t duration = action->data().toUInt() ;

    std::cerr << "Data is " << duration << std::endl;

 	mInterface->setSyncPeriod(mGroupId,duration * 86400) ;

    // If the store is smaller, we increase it accordingly. No need to sync more than we store. The machinery below also takes care of this.
    //
    uint32_t store_period = mInterface->getStoragePeriod(mGroupId);

    if(duration == 0)
 		mInterface->setStoragePeriod(mGroupId,duration * 86400) ;	// indefinite sync => indefinite storage
    else
    {
        if(store_period != 0 && store_period < duration*86400)
        {
 			mInterface->setStoragePeriod(mGroupId,duration * 86400) ;	// indefinite sync => indefinite storage
            std::cerr << "(II) auto adjusting storage period to " << duration<< " days as well." << std::endl;
        }
    }
}

void GxsGroupFrameDialog::restoreGroupKeys(void)
{
	QMessageBox::warning(this, "RetroShare", "ToDo");

#ifdef TOGXS
	mInterface->groupRestoreKeys(mGroupId);
#endif
}

void GxsGroupFrameDialog::newGroup()
{
	GxsGroupDialog *dialog = createNewGroupDialog();

	if (!dialog) {
		return;
	}

	dialog->exec();
	delete(dialog);
}

void GxsGroupFrameDialog::subscribeGroup()
{
	groupSubscribe(true);
}

void GxsGroupFrameDialog::unsubscribeGroup()
{
	groupSubscribe(false);
}

void GxsGroupFrameDialog::groupSubscribe(bool subscribe)
{
	if (mGroupId.isNull()) {
		return;
	}

	uint32_t token;
	mInterface->subscribeToGroup(token, mGroupId, subscribe);
}

void GxsGroupFrameDialog::showGroupDetails()
{
	if (mGroupId.isNull()) {
		return;
	}

	GxsGroupDialog *dialog = createGroupDialog(GxsGroupDialog::MODE_SHOW, mGroupId);
	if (!dialog) {
		return;
	}

	dialog->exec();
	delete(dialog);
}

void GxsGroupFrameDialog::editGroupDetails()
{
	if (mGroupId.isNull()) {
		return;
	}

	GxsGroupDialog *dialog = createGroupDialog(GxsGroupDialog::MODE_EDIT, mGroupId);
	if (!dialog) {
		return;
	}

	dialog->exec();
	delete(dialog);
}

void GxsGroupFrameDialog::copyGroupLink()
{
	if (mGroupId.isNull()) {
		return;
	}

	QString name;
	if(!getCurrentGroupName(name)) return;

	RetroShareLink link = RetroShareLink::createGxsGroupLink(getLinkType(), mGroupId, name);

	if (link.valid()) {
		QList<RetroShareLink> urls;
		urls.push_back(link);
		RSLinkClipboard::copyLinks(urls);
	}
}

bool GxsGroupFrameDialog::getCurrentGroupName(QString& name)
{
	return ui->groupTreeWidget->getGroupName(QString::fromStdString(mGroupId.toStdString()), name);
}

void GxsGroupFrameDialog::markMsgAsRead()
{
	GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
	if (msgWidget) {
		msgWidget->setAllMessagesRead(true);
	}
}

void GxsGroupFrameDialog::markMsgAsUnread()
{
	GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
	if (msgWidget) {
		msgWidget->setAllMessagesRead(false);
	}
}

void GxsGroupFrameDialog::sharePublishKey()
{
	if (mGroupId.isNull()) {
		return;
	}

//	QMessageBox::warning(this, "", "ToDo");

    GroupShareKey shareUi(this, mGroupId, shareKeyType());
    shareUi.exec();
}

void GxsGroupFrameDialog::loadComment(const RsGxsGroupId &grpId, const QVector<RsGxsMessageId>& msg_versions, const RsGxsMessageId &most_recent_msgId, const QString &title)
{
	RsGxsCommentService *commentService = getCommentService();
	if (!commentService) {
		/* No comment service available */
		return;
	}

	GxsCommentDialog *commentDialog = commentWidget(most_recent_msgId);

	if (!commentDialog) {
		QString comments = title;
		if (title.length() > MAX_COMMENT_TITLE)
		{
			comments.truncate(MAX_COMMENT_TITLE - 3);
			comments += "…";
		}

        commentDialog = new GxsCommentDialog(this,RsGxsId(), commentService);

		QWidget *commentHeader = createCommentHeaderWidget(grpId, most_recent_msgId);
		if (commentHeader) {
			commentDialog->setCommentHeader(commentHeader);
		}

        std::set<RsGxsMessageId> msgv;
        for(int i=0;i<msg_versions.size();++i)
            msgv.insert(msg_versions[i]);

		commentDialog->commentLoad(grpId, msgv,most_recent_msgId);

		int index = ui->messageTabWidget->addTab(commentDialog, comments);
        ui->messageTabWidget->setTabIcon(index, FilesDefs::getIconFromQtResourcePath(IMAGE_COMMENT));
	}

	ui->messageTabWidget->setCurrentWidget(commentDialog);
}

bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessageId& msgId)
{
	if (groupId.isNull()) {
		return false;
	}

	if (mStateHelper->isLoading(TOKEN_TYPE_GROUP_SUMMARY)) {
		mNavigatePendingGroupId = groupId;
		mNavigatePendingMsgId = msgId;

		/* No information if group is available */
		return true;
	}

	QString groupIdString = QString::fromStdString(groupId.toStdString());
	if (ui->groupTreeWidget->activateId(groupIdString, msgId.isNull()) == NULL) {
		return false;
	}

	changedCurrentGroup(groupIdString);

	/* search exisiting tab */
	GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
	if (!msgWidget) {
		return false;
	}

	if (msgId.isNull()) {
		return true;
	}

	return msgWidget->navigate(msgId);
}

GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId)
{
	int tabCount = ui->messageTabWidget->count();

	for (int index = 0; index < tabCount; ++index)
    {
		GxsMessageFrameWidget *childWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));

		if (childWidget && childWidget->groupId() == groupId)
			return childWidget;
	}

	return NULL;
}

GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId)
{
	GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId);

	if (!msgWidget)
		return NULL;

	int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true));
	ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon());

	connect(msgWidget, SIGNAL(groupChanged(QWidget*)), this, SLOT(messageTabInfoChanged(QWidget*)));
	connect(msgWidget, SIGNAL(waitingChanged(QWidget*)), this, SLOT(messageTabWaitingChanged(QWidget*)));
	connect(msgWidget, SIGNAL(loadComment(RsGxsGroupId,QVector<RsGxsMessageId>,RsGxsMessageId,QString)), this, SLOT(loadComment(RsGxsGroupId,QVector<RsGxsMessageId>,RsGxsMessageId,QString)));

	return msgWidget;
}

GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId)
{
	int tabCount = ui->messageTabWidget->count();
	for (int index = 0; index < tabCount; ++index) {
		GxsCommentDialog *childWidget = dynamic_cast<GxsCommentDialog*>(ui->messageTabWidget->widget(index));
		if (childWidget && childWidget->messageId() == msgId) {
			return childWidget;
		}
	}

	return NULL;
}

void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId)
{
	if (mInFill) {
		return;
	}

	if (groupId.isEmpty())
    {
        auto w = currentWidget();

        if(w)
			w->setGroupId(RsGxsGroupId());

		return;
	}

	mGroupId = RsGxsGroupId(groupId.toStdString());

	if (mGroupId.isNull())
		return;

	/* search exisiting tab */
	GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);

    // check that we have at least one tab

	if(msgWidget)
		ui->messageTabWidget->setCurrentWidget(msgWidget);
    else
    {
        if(useTabs() || ui->messageTabWidget->count()==0)
        {
			msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString()));
			ui->messageTabWidget->setCurrentWidget(msgWidget);
		}
        else
			currentWidget()->setGroupId(mGroupId);
	}
}

void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item)
{
	openGroupInNewTab(RsGxsGroupId(ui->groupTreeWidget->itemId(item).toStdString()));
}

void GxsGroupFrameDialog::openInNewTab()
{
	openGroupInNewTab(mGroupId);
}

void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId)
{
	if (groupId.isNull()) {
		return;
	}

	/* search exisiting tab */
	GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId);

	ui->messageTabWidget->setCurrentWidget(msgWidget);
}

void GxsGroupFrameDialog::messageTabCloseRequested(int index)
{
	if(ui->messageTabWidget->count() == 1) /* Don't close single tab */
		return;

	GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
	delete msgWidget ;
}

GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const
{
    return dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex()));
}

void GxsGroupFrameDialog::messageTabChanged(int index)
{
	GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));

	if (!msgWidget)
		return;

	ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false);
}

void GxsGroupFrameDialog::messageTabInfoChanged(QWidget *widget)
{
	int index = ui->messageTabWidget->indexOf(widget);
	if (index < 0) {
		return;
	}

	GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
	if (!msgWidget) {
		return;
	}

	ui->messageTabWidget->setTabText(index, msgWidget->groupName(true));
	ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon());
}

void GxsGroupFrameDialog::messageTabWaitingChanged(QWidget *widget)
{
	int index = ui->messageTabWidget->indexOf(widget);
	if (index < 0) {
		return;
	}

	GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
	if (!msgWidget) {
		return;
	}

	ui->groupTreeWidget->setWaiting(QString::fromStdString(msgWidget->groupId().toStdString()), msgWidget->isWaiting());
}

///***** INSERT GROUP LISTS *****/
void GxsGroupFrameDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *groupInfo, GroupItemInfo &groupItemInfo)
{
	groupItemInfo.id = QString::fromStdString(groupInfo->mMeta.mGroupId.toStdString());
	groupItemInfo.name = QString::fromUtf8(groupInfo->mMeta.mGroupName.c_str());
	groupItemInfo.popularity = groupInfo->mMeta.mPop;
	groupItemInfo.lastpost = QDateTime::fromTime_t(groupInfo->mMeta.mLastPost);
	groupItemInfo.subscribeFlags = groupInfo->mMeta.mSubscribeFlags;
	groupItemInfo.publishKey = IS_GROUP_PUBLISHER(groupInfo->mMeta.mSubscribeFlags) ;
	groupItemInfo.adminKey = IS_GROUP_ADMIN(groupInfo->mMeta.mSubscribeFlags) ;
	groupItemInfo.max_visible_posts = groupInfo->mMeta.mVisibleMsgCount ;

#if TOGXS
	if (groupInfo.mGroupFlags & RS_DISTRIB_AUTHEN_REQ) {
		groupItemInfo.name += " (" + tr("AUTHD") + ")";
        groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(IMAGE_GROUPAUTHD);
	}
	else
#endif
	{
        groupItemInfo.icon = FilesDefs::getIconFromQtResourcePath(icon(ICON_DEFAULT));
	}
}

void GxsGroupFrameDialog::insertGroupsData(const std::list<RsGxsGenericGroupData*>& groupList)
{
	if (!mInitialized) {
		return;
	}

	mInFill = true;

	QList<GroupItemInfo> adminList;
	QList<GroupItemInfo> subList;
	QList<GroupItemInfo> popList;
	QList<GroupItemInfo> otherList;
	std::multimap<uint32_t, GroupItemInfo> popMap;

	for (auto& g:groupList)
    {
		/* sort it into Publish (Own), Subscribed, Popular and Other */
		uint32_t flags = g->mMeta.mSubscribeFlags;

		GroupItemInfo groupItemInfo;
		groupInfoToGroupItemInfo(g, groupItemInfo);

		if (IS_GROUP_SUBSCRIBED(flags))
		{
			if (IS_GROUP_ADMIN(flags))
				adminList.push_back(groupItemInfo);
			else
				subList.push_back(groupItemInfo); /* subscribed group */
		}
		else
        {
			popMap.insert(std::make_pair(g->mMeta.mLastPost, groupItemInfo)); /* rate the others by time of last post */
        }
	}

	/* iterate backwards through popMap - take the top 5 or 10% of list */
	uint32_t popCount = 5;
	if (popCount < popMap.size() / 10)
		popCount = popMap.size() / 10;

	uint32_t i = 0;
	std::multimap<uint32_t, GroupItemInfo>::reverse_iterator rit;

	for (rit = popMap.rbegin(); rit != popMap.rend(); ++rit,++i)
		if(i < popCount)
			popList.append(rit->second);
		else
			otherList.append(rit->second);

	/* now we can add them in as a tree! */
	ui->groupTreeWidget->fillGroupItems(mYourGroups, adminList);
	mYourGroups->setText(GTW_COLUMN_POPULARITY, QString::number(mYourGroups->childCount()));
	ui->groupTreeWidget->fillGroupItems(mSubscribedGroups, subList);
	mSubscribedGroups->setText(GTW_COLUMN_POPULARITY, QString::number(mSubscribedGroups->childCount()));
	ui->groupTreeWidget->fillGroupItems(mPopularGroups, popList);
	mPopularGroups->setText(GTW_COLUMN_POPULARITY, QString::number(mPopularGroups->childCount()));
	ui->groupTreeWidget->fillGroupItems(mOtherGroups, otherList);
	mOtherGroups->setText(GTW_COLUMN_POPULARITY, QString::number(mOtherGroups->childCount()));

	mInFill = false;

	/* Re-fill group */
	if (!ui->groupTreeWidget->activateId(QString::fromStdString(mGroupId.toStdString()), true))
		mGroupId.clear();

	updateMessageSummaryList(RsGxsGroupId());
}

void GxsGroupFrameDialog::updateMessageSummaryList(RsGxsGroupId groupId)
{
    // groupId.isNull() means that we need to update all groups so we clear up the list of groups to update.

    if(!groupId.isNull())
        mGroupIdsSummaryToUpdate.insert(groupId);
    else
        mGroupIdsSummaryToUpdate.clear();

    mShouldUpdateMessageSummaryList = true;
}

void GxsGroupFrameDialog::updateMessageSummaryListReal(RsGxsGroupId groupId)
{
	if (!mInitialized) {
		return;
	}

	if (groupId.isNull())
	{
		QTreeWidgetItem *items[2] = { mYourGroups, mSubscribedGroups };
		for (int item = 0; item < 2; ++item) {
			int child;
			int childCount = items[item]->childCount();
			for (child = 0; child < childCount; ++child) {
				QTreeWidgetItem *childItem = items[item]->child(child);
				QString childId = ui->groupTreeWidget->itemId(childItem);
				if (childId.isEmpty())
					continue;

				updateGroupStatistics(RsGxsGroupId(childId.toLatin1().constData()));
			}
		}
	}
	else
		updateGroupStatistics(groupId);
}

/*********************** **** **** **** ***********************/
/** Request / Response of Data ********************************/
/*********************** **** **** **** ***********************/

void GxsGroupFrameDialog::updateGroupSummary()
{
	if (!mInitialized) return;//UI not yet initialized

 	RsThread::async([this]()
	{
		auto groupInfo = new std::list<RsGxsGenericGroupData*>() ;

		if(!getGroupData(*groupInfo))
		{
			std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info." << std::endl;
            delete groupInfo;
			return;
		}
        if(groupInfo->empty())
        {
			std::cerr << __PRETTY_FUNCTION__ << " no group info collected." << std::endl;
            delete groupInfo;
            return;
        }

		RsQThreadUtils::postToObject( [this,groupInfo]()
		{
			/* Here it goes any code you want to be executed on the Qt Gui
			 * thread, for example to update the data model with new information
			 * after a blocking call to RetroShare API complete, note that
			 * Qt::QueuedConnection is important!
			 */

            // Here we save the focus, and restore it afterwards: there's no need to grab the focus here and
            // if we do, it may harm the navitation in forums, channels, boards, etc.

            auto w = QApplication::focusWidget();

            insertGroupsData(*groupInfo);
			updateSearchResults();

			mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false);

			if (!mNavigatePendingGroupId.isNull()) {
				/* Navigate pending */
				navigate(mNavigatePendingGroupId, mNavigatePendingMsgId);

				mNavigatePendingGroupId.clear();
				mNavigatePendingMsgId.clear();
			}

			// update the local cache in order to avoid re-asking the data when the UI wants it (this happens on ::show() for instance)

			mCachedGroupMetas.clear();

			// now delete the data that is not used anymore

			for(auto& g:*groupInfo)
			{
				mCachedGroupMetas[g->mMeta.mGroupId] = g->mMeta;
				delete g;
			}

            delete groupInfo;

            // Restore the focus.

            if(w)
                w->setFocus();

        }, this );
	});
}

/*********************** **** **** **** ***********************/
/*********************** **** **** **** ***********************/

void GxsGroupFrameDialog::updateGroupStatistics(const RsGxsGroupId &groupId)
{
    mGroupStatisticsToUpdate.insert(groupId);
    mShouldUpdateGroupStatistics = true;
}

void GxsGroupFrameDialog::updateGroupStatisticsReal(const RsGxsGroupId &groupId)
{
    RsThread::async([this,groupId]()
	{
		GxsGroupStatistic stats;

        if(! getGroupStatistics(groupId, stats))
		{
			std::cerr << __PRETTY_FUNCTION__ << " failed to collect group statistics for group " << groupId << std::endl;
			return;
		}

		RsQThreadUtils::postToObject( [this,stats, groupId]()
		{
			/* Here it goes any code you want to be executed on the Qt Gui
			 * thread, for example to update the data model with new information
			 * after a blocking call to RetroShare API complete, note that
			 * Qt::QueuedConnection is important!
			 */

            QTreeWidgetItem *item = ui->groupTreeWidget->getItemFromId(QString::fromStdString(stats.mGrpId.toStdString()));

            if (item)
                ui->groupTreeWidget->setUnreadCount(item, mCountChildMsgs ? (stats.mNumThreadMsgsUnread + stats.mNumChildMsgsUnread) : stats.mNumThreadMsgsUnread);

            mCachedGroupStats[groupId] = stats;

			getUserNotify()->updateIcon();

        }, this );
	});
}

void GxsGroupFrameDialog::getServiceStatistics(GxsServiceStatistic& stats) const
{
    if(!mCachedGroupStats.empty())
    {
        stats = GxsServiceStatistic(); // clears everything

        for(auto& it:  mCachedGroupStats)
        {
            const GxsGroupStatistic& s(it.second);

            stats.mNumMsgs             += s.mNumMsgs;
            stats.mNumGrps             += 1;
            stats.mSizeOfMsgs          += s.mTotalSizeOfMsgs;
            stats.mNumThreadMsgsNew    += s.mNumThreadMsgsNew;
            stats.mNumThreadMsgsUnread += s.mNumThreadMsgsUnread;
            stats.mNumChildMsgsNew     += s.mNumChildMsgsNew ;
            stats.mNumChildMsgsUnread  += s.mNumChildMsgsUnread ;
        }

        // Also save the service statistics in conf file, so that we can display it right away at start.

        Settings->beginGroup(mSettingsName);
        Settings->setValue("NumMsgs",            stats.mNumMsgs            );
        Settings->setValue("NumGrps",            stats.mNumGrps            );
        Settings->setValue("SizeOfMessages",     stats.mSizeOfMsgs         );
        Settings->setValue("NumThreadMsgsNew",   stats.mNumThreadMsgsNew   );
        Settings->setValue("NumThreadMsgsUnread",stats.mNumThreadMsgsUnread);
        Settings->setValue("NumChildMsgsNew",    stats.mNumChildMsgsNew    );
        Settings->setValue("NumChildMsgsUnread", stats.mNumChildMsgsUnread );
        Settings->endGroup();
    }
    else	// Get statistics from settings if no cache is already present: allows to display at start.
    {
        Settings->beginGroup(mSettingsName);

        stats.mNumMsgs             = Settings->value("NumMsgs",QVariant(0)).toInt();
        stats.mNumGrps             = Settings->value("NumGrps",QVariant(0)).toInt();
        stats.mSizeOfMsgs          = Settings->value("SizeOfMessages",QVariant(0)).toInt();
        stats.mNumThreadMsgsNew    = Settings->value("NumThreadMsgsNew",QVariant(0)).toInt();
        stats.mNumThreadMsgsUnread = Settings->value("NumThreadMsgsUnread",QVariant(0)).toInt();
        stats.mNumChildMsgsNew     = Settings->value("NumChildMsgsNew",QVariant(0)).toInt();
        stats.mNumChildMsgsUnread  = Settings->value("NumChildMsgsUnread",QVariant(0)).toInt();

        Settings->endGroup();
    }
}

TurtleRequestId GxsGroupFrameDialog::distantSearch(const QString& search_string)   // this should be overloaded in the child class
{
    std::cerr << "Searching for \"" << search_string.toStdString() << "\". Function is not overloaded, so nothing will happen." << std::endl;
    return 0;
}

void GxsGroupFrameDialog::searchNetwork(const QString& search_string)
{
    if(search_string.isNull())
        return ;

    uint32_t request_id = distantSearch(search_string);

    if(request_id == 0)
        return ;

    mSearchGroupsItems[request_id] = ui->groupTreeWidget->addSearchItem(tr("Search for")+ " \"" + search_string + "\"",(uint32_t)request_id,FilesDefs::getIconFromQtResourcePath(icon(ICON_SEARCH)));
}

void GxsGroupFrameDialog::distantRequestGroupData()
{
    QAction *action = dynamic_cast<QAction*>(sender()) ;

    if(!action)
        return ;

    RsGxsGroupId group_id(action->data().toString().toStdString());

    if(group_id.isNull())
    {
        std::cerr << "Cannot retrieve group! Group id is null!" << std::endl;
    }

	std::cerr << "Explicit request for group " << group_id << std::endl;
    checkRequestGroup(group_id) ;
}

