/***************************************************************************
 *   Copyright (C) 2010 by Volker Lanz <vl@fidra.de>                       *
 *                                                                         *
 *   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            *
 ***************************************************************************/

#include "gui/editprofilecategoriespage.h"

#include "core/profile.h"
#include "core/category.h"
#include "core/activity.h"

#include "veneer/messagebox.h"
#include "veneer/iconloader.h"

#include <QTreeWidgetItem>
#include <QMessageBox>

static const int RoleCategoryModified = Qt::UserRole + 1;

class CategoryTreeWidgetItem : public QTreeWidgetItem
{
	public:
		CategoryTreeWidgetItem(Category* c) : QTreeWidgetItem(), category(c) {}
		Category* category;
};


EditProfileCategoriesPage::EditProfileCategoriesPage(QWidget* parent) :
	QWidget(parent),
	m_Profile(NULL)
{
	setupUi(this);
	addCategoryButton->setIcon(BarIcon("list-add"));
	removeCategoryButton->setIcon(BarIcon("list-remove"));
}

QTreeWidgetItem* EditProfileCategoriesPage::createCategoryTreeNode(Category& c)
{
	QTreeWidgetItem* item = new CategoryTreeWidgetItem(&c);

	item->setText(0, c.name());
	item->setIcon(0, SmallIcon("folder"));
	item->setData(0, RoleCategoryModified, false);
	item->setFlags(item->flags() | Qt::ItemIsEditable);

	foreach(Category* sub, c.subCategories())
		if (!sub->isRemoved())
			item->addChild(createCategoryTreeNode(*sub));

	item->sortChildren(0, Qt::AscendingOrder);

	return item;
}

void EditProfileCategoriesPage::setupPage(Profile* profile)
{
	m_Profile = profile;

	foreach(Category* c, m_Profile->categories())
		if (!c->isRemoved())
		{
			QTreeWidgetItem* top = createCategoryTreeNode(*c);
			categoriesTree->addTopLevelItem(top);
			top->setExpanded(true);
		}
}

void EditProfileCategoriesPage::setupConnections()
{
	connect(addCategoryButton, SIGNAL(clicked(bool)), SLOT(onAddCategoryButton(bool)));
	connect(removeCategoryButton, SIGNAL(clicked(bool)), SLOT(onRemoveCategoryButton(bool)));
	connect(categoriesTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(onCategoryItemChanged(QTreeWidgetItem*, int)));
	connect(categoriesTree, SIGNAL(itemSelectionChanged()), SLOT(onCategorySelectionChanged()));
}

bool EditProfileCategoriesPage::accept()
{
	removeDeletedCategories(m_Profile->categories());

	for (int i = 0; i < categoriesTree->topLevelItemCount(); i++)
		updateAndAddCategories(categoriesTree->topLevelItem(i));

	return true;
}

/** small helper: find all categories removed by removing one category
  * and all its children
  */
static void removedCategories(const Category* top, QSet<const Category*>& removed)
{
	removed.insert(top);

	foreach(const Category* sub, top->subCategories())
		removedCategories(sub, removed);
}

/** find all activities referring to a deleted category and set
  * their category to the next available
  * parent category
  */
void EditProfileCategoriesPage::reCategorizeActivities(Category* c)
{
	Q_ASSERT(c);

	QSet<const Category*> removed;
	removedCategories(c, removed);

	foreach(Activity* activity, m_Profile->activities())
		if (removed.find(activity->category()) != removed.end())
		{
			Category* newCategory = c->parent() != NULL ? c->parent() : m_Profile->rootCategory();
			activity->setCategory(newCategory);
		}
}

/** go through the set of deleted category ids and set the categories under
  * the profile to removed.
  */
void EditProfileCategoriesPage::removeDeletedCategories(Categories& cats)
{
	if (m_DeletedCategories.size() == 0)
		return;

	foreach(Category* cat, cats)
	{
		if (m_DeletedCategories.find(cat) != m_DeletedCategories.end())
		{
			cat->setRemoved(true);
			reCategorizeActivities(cat);
		}

		removeDeletedCategories(cat->subCategories());
	}
}

/** go through the QTreeWidgetItems in the category tree and find the new and the
  * modified ones. add the new ones where they belong. modify the existing ones that
  * have been changed.
  */
void EditProfileCategoriesPage::updateAndAddCategories(QTreeWidgetItem* item, Category* parent)
{
	Categories* cats = parent ? &parent->subCategories() : &m_Profile->categories();
	Category* cat = static_cast<CategoryTreeWidgetItem*>(item)->category;

	if (cat == NULL)
	{
		cats->append(new Category(m_Profile, parent, item->text(0)));
		cat = cats->last();
	}
	else if (item->data(0, RoleCategoryModified).toBool() == true)
	{
		Q_ASSERT(cat);

		if (cat != NULL)
			cat->setName(item->text(0));
	}

	for (int i = 0; i < item->childCount(); i++)
		updateAndAddCategories(item->child(i), cat);
}

void EditProfileCategoriesPage::onAddCategoryButton(bool)
{
	QTreeWidgetItem* item = new CategoryTreeWidgetItem(NULL);

	item->setText(0, tr("New Category"));
	item->setIcon(0, BarIcon("folder"));
	item->setData(0, RoleCategoryModified, true);
	item->setFlags(item->flags() | Qt::ItemIsEditable);

	if (categoriesTree->selectedItems().size() == 1)
	{
		categoriesTree->selectedItems()[0]->setExpanded(true);
		categoriesTree->selectedItems()[0]->addChild(item);
	}
	else
		categoriesTree->insertTopLevelItem(0, item);

	item->setExpanded(true);

	categoriesTree->editItem(item);

	emit modified();
}

void EditProfileCategoriesPage::onRemoveCategoryButton(bool)
{
	if (categoriesTree->selectedItems().size() == 0)
		return;

	QTreeWidgetItem* item = categoriesTree->selectedItems()[0];
	if (Veneer::messageBoxQuestionYesNo(this,
			tr("Are you sure you want to delete the selected category \"%1\" and all its subcategories?<br><br>Activities in this category will be re-assigned to the next available parent category.").arg(item->text(0)),
			tr("Really Delete Category?"),
			tr("Delete Category"),
			tr("Cancel"),
			"deleteCategoryWarning") == QMessageBox::No)
		return;

	foreach (QTreeWidgetItem* item, categoriesTree->selectedItems())
	{
		Category* c = static_cast<CategoryTreeWidgetItem*>(item)->category;
		// remember the ids of the categories we deleted so we can actually set
		// the category DbItem objects to status removed once the user accepts th
		// dialog.
		if (c)
			m_DeletedCategories.insert(c);

		delete item;
	}

	emit modified();
}

void EditProfileCategoriesPage::onCategoryItemChanged(QTreeWidgetItem* item, int)
{
	item->setData(0, RoleCategoryModified, true);

	emit modified();
}

void EditProfileCategoriesPage::onCategorySelectionChanged()
{
	QTreeWidgetItem* item = NULL;

	if (categoriesTree->selectedItems().size() > 0)
		item = categoriesTree->selectedItems()[0];

	addCategoryButton->setEnabled(item != NULL);
	removeCategoryButton->setEnabled(item != NULL && item->parent() != NULL);
}

