/***************************************************************************
 *	Copyright (C) 2010 by cazou88											*
 *	cazou88@users.sourceforge.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.,											*
 *	59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.				*
 ***************************************************************************/

#include <algorithm>           // for sort
#include <cstddef>            // for NULL
#include <klocalizedstring.h>  // for i18n
#include <qdebug.h>            // for QDebug
#include <qglobal.h>           // for foreach, qSwap, qDebug, Q_UNUSED
#include <qicon.h>             // for QIcon
#include <qstringliteral.h>    // for QStaticStringData, QStringLiteral

#include "common.h"            // for DEBUG_LINE_INFO, PACKAGE_AVAILABLE
#include "global.h"            // for formatTime
#include "packagelistitem.h"   // for PackageListItem
#include "queuelistitem.h"     // for QueueListItem
#include "queuelistmodel.h"
#include "queuelistview.h"     // for QueueListView

class QObject;

QueueListModel::QueueListModel(QObject *parent)
 : QAbstractItemModel(parent)
{
}

QueueListModel::~QueueListModel()
{
	while (!m_packages.isEmpty())
		delete m_packages.takeLast();
}

auto QueueListModel::packages() -> QList<QueueListItem*>
{
	return m_packages;
}

void QueueListModel::setPackages(QList<QueueListItem*>& packages)
{
	DEBUG_LINE_INFO;
	// Remove all rows
	if (rowCount() > 0)
	{
		beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
		//Fix bug #3163827 order of queue items
		//I think this bug has to do with deleting the parent before deleting the child items,
		//so if I get the order right it should go away, currently that means changing the order randomly
		while (!m_packages.isEmpty())
			delete m_packages.takeFirst();//.takeLast();
		endRemoveRows();
	}

	if (packages.count() <= 0)
		return;

	// Set packages
	//NOTE: This only puts top-level packages in the QList
	QList<QueueListItem*> tmp;
	for(QueueListItem* item : packages)
	{
		if (item->parentItem() == nullptr)
			tmp << item;
	}
	beginInsertRows(QModelIndex(), 0, tmp.count());
	m_packages = tmp;
	endInsertRows();
}

// Implementation of pure virtual methods to be implemented.
auto QueueListModel::columnCount(const QModelIndex& parent) const -> int
{
	Q_UNUSED(parent)
	if (parent.column() > 0)
		return 0;
	return 6;
}

auto QueueListModel::data(const QModelIndex& index, int role) const -> QVariant
{
	if (!index.isValid())
		return QVariant();

	auto *p = static_cast<QueueListItem*>(index.internalPointer());
	switch(index.column())
	{
	case 0:
		if (role == Qt::DisplayRole)
			return QVariant(QStringLiteral("%1 (%2)").arg(p->name(), p->category()));
		if (role == Qt::DecorationRole && p->status() & PACKAGE_AVAILABLE)
			return QVariant(QIcon::fromTheme(QStringLiteral("kuroo_package")));
		if (role == Qt::DecorationRole)
			return QVariant(QIcon::fromTheme(QStringLiteral("kuroo_stable")));

		break;
	case 1:
		if (role == Qt::DecorationRole && p->isInWorld())
			return QVariant(QIcon::fromTheme(QStringLiteral("kuroo_world")));

		break;
	case 2:
		if (role == Qt::DisplayRole)
			return QVariant(p->version());

		break;
	case 3:
		if (role == Qt::DisplayRole)
		{
			if (p->duration() < 0)
				return QVariant(i18n("na"));
			return QVariant(formatTime(p->duration()));
		}

		break;
	case 4:
		if (role == Qt::DisplayRole)
			return QVariant(p->size());

		break;
	case 5:
		if (role == Qt::DisplayRole)
			return QVariant(QString());

		break;
	default:
		qDebug() << "Error: invalid column!";
		break;
	}

	return QVariant();
}

auto QueueListModel::index(int row, int column, const QModelIndex& parent) const -> QModelIndex
{
	if (row < 0)
		return QModelIndex();

	if (!parent.isValid() )
	{
		if (row < m_packages.count()) {
			return createIndex(row, column, m_packages.at(row));
		}
		return QModelIndex();
	
	}

	auto *item = static_cast<QueueListItem*>(parent.internalPointer());
	if (item && row < item->children().count())
	{
		item = item->children().at(row);
		return createIndex(row, column, item);
	}
	return QModelIndex();
}

auto QueueListModel::hasChildren(const QModelIndex& parent) const -> bool
{
	if (parent.isValid())
	{
		//if the parent is valid then check it's children count
		auto *item = static_cast<QueueListItem*>(parent.internalPointer());
		if (item)
			return (item->children().count() > 0);
		return false;
	}
	//otherwise it's a root, so return true
	return true;
}

auto QueueListModel::parent(const QModelIndex& index) const -> QModelIndex
{
	if (!index.isValid())
	{
		return QModelIndex();
	}
	auto *item = static_cast<QueueListItem*>(index.internalPointer());
	if (item == nullptr || item->parentItem() == nullptr)
		return QModelIndex();

	return createIndex(item->parentItem()->packageIndex(), 0, item->parentItem());
}

auto QueueListModel::rowCount(const QModelIndex& parent) const -> int
{
	if (parent.column() > 0)
		return 0;

	if (!parent.isValid())
		return m_packages.count();

	auto *item = static_cast<QueueListItem*>(parent.internalPointer());
	return item->children().count();
}

auto QueueListModel::headerData(int section, Qt::Orientation orientation, int role) const -> QVariant
{
	if (orientation != Qt::Horizontal)
		return QVariant();

	switch (section)
	{
	case 0:
		if (role == Qt::DisplayRole)
			return i18n("Package");
		break;
	case 1:
		if (role == Qt::DecorationRole)
			return QIcon::fromTheme(QStringLiteral("kuroo_world_column"));
		break;
	case 2:
		if (role == Qt::DisplayRole)
			return i18n("Version");
		break;
	case 3:
		if (role == Qt::DisplayRole)
			return QStringLiteral("Duration");
		break;
	case 4:
		if (role == Qt::DisplayRole)
			return QStringLiteral("Size");
		break;
	case 5:
		if (role == Qt::DisplayRole)
			return QStringLiteral("Progress");
		break;
	default:
		qDebug() << "Error: invalid column!";
		break;
	}

	return QVariant();
}

void QueueListModel::updateItem(QueueListItem *item, QueueListView *listView)
{
	QModelIndexList mList = persistentIndexList();
	for(const QModelIndex index : std::as_const(mList))
	{
		if (index.isValid() && index.internalPointer() == item && index.column() == 5)
		{
			listView->update(index);
			return;
		}
	}

}

auto QueueListModel::packageLessThan(PackageListItem *p1, PackageListItem *p2) -> bool
{
	return p1->name() < p2->name();
}

auto QueueListModel::packageMoreThan(PackageListItem *p1, PackageListItem *p2) -> bool
{
	return p1->name() > p2->name();
}

void QueueListModel::sort(int column, Qt::SortOrder order)
{
	if (column != 0)
		return;

	sortTree(m_packages, order);
}

void QueueListModel::sortTree(QList<QueueListItem*> items, Qt::SortOrder order)
{
	//WARN: Hoping that this doesn't change items as it iterates over them
	for(QueueListItem *item : items)
	{
		sortTree(item->children(), order);
	}

	if (order == Qt::AscendingOrder)
		std::sort(items.begin(), items.end(), packageLessThan);
	else
		std::sort(items.begin(), items.end(), packageMoreThan);

}
