/*
 * Copyright (C) 2014-2024 CZ.NIC
 *
 * 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 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include "src/datovka_shared/utility/date_time.h"
#include "src/gui/icon_container.h"
#include "src/models/helper.h"
#include "src/models/message_found_model.h"

static IconContainer inconContainer; /* Local icon container. */

MsgFoundModel::MsgFoundModel(QObject *parent)
    : QAbstractTableModel(parent),
    m_entries(),
    m_customColumnHeaders()
{
}

int MsgFoundModel::rowCount(const QModelIndex &parent) const
{
	if (parent.isValid()) {
		return 0;
	} else {
		return m_entries.size();
	}
}

int MsgFoundModel::columnCount(const QModelIndex &parent) const
{
	if (parent.isValid()) {
		return 0;
	} else {
		return MAX_COLNUM + m_customColumnHeaders.size();
	}
}

QVariant MsgFoundModel::data(const QModelIndex &index, int role) const
{
	const int row = index.row();
	const int col = index.column();
	if (Q_UNLIKELY((row < 0) || (row >= rowCount()))) {
		Q_ASSERT(0);
		return QVariant();
	}
	if (Q_UNLIKELY((col < 0) || (col >= columnCount()))) {
		Q_ASSERT(0);
		return QVariant();
	}

	switch (role) {
	case Qt::DisplayRole:
		switch (col) {
		case COL_ACCOUNT_ID:
			return m_entries.at(row).identif.acntIdDb.username();
			break;
		case COL_MESSAGE_ID:
			return m_entries.at(row).identif.msgId.dmId();
			break;
		case COL_ANNOTATION:
			return m_entries.at(row).dmAnnotation;
			break;
		case COL_SENDER:
			return m_entries.at(row).dmSender;
			break;
		case COL_RECIPIENT:
			return m_entries.at(row).dmRecipient;
			break;
		case COL_DELIVERY_TIME:
			{
				const QDateTime &deliveryTime =
				    m_entries.at(row).identif.msgId.deliveryTime();
				return deliveryTime.isValid() ?
				    deliveryTime.toString(Utility::dateTimeDisplayFormat) :
				    QString();
			}
			break;
		case COL_ACCEPT_TIME:
			{
				const QDateTime &acceptanceTime =
				    m_entries.at(row).dmAcceptanceTime;
				return acceptanceTime.isValid() ?
				    acceptanceTime.toString(Utility::dateTimeDisplayFormat) :
				    QString();
			}
			break;
		case COL_ATTACH_DOWNLOADED:
			/* Hide text. */
			return QVariant();
			break;
		default:
			{
				const int customColIdx = col - MAX_COLNUM;
				if (customColIdx < m_customColumnHeaders.size()) {
					return m_entries.at(row).customValues.value(col, QString());
				}
			}
			Q_ASSERT(0);
			return QVariant();
			break;
		}
		break;

	case Qt::DecorationRole:
		if (col == COL_ATTACH_DOWNLOADED) {
			if (m_entries.at(row).identif.downloaded) {
				return inconContainer.icon(
				    IconContainer::ICON_ATTACHMENT);
			}
		}
		return QVariant();
		break;

	case Qt::ToolTipRole:
		if (col == COL_MESSAGE_ID) {
			return m_entries.at(row).msgDetail;
		}
		return QVariant();
		break;

	case Qt::AccessibleTextRole:
		switch (col) {
		case COL_ACCOUNT_ID:
			return tr("Data-box username") + QLatin1String(" ") +
			    data(index).toString();
			break;
		case COL_MESSAGE_ID:
		case COL_ANNOTATION:
		case COL_SENDER:
		case COL_RECIPIENT:
		case COL_DELIVERY_TIME:
		case COL_ACCEPT_TIME:
			return headerData(index.column(), Qt::Horizontal).toString() +
			    QLatin1String(" ") + data(index).toString();
			break;
		case COL_ATTACH_DOWNLOADED:
			if (m_entries.at(row).identif.downloaded) {
				return tr("attachments downloaded");
			} else {
				return tr("attachments not downloaded");
			}
			break;
		default:
			{
				const int customColIdx = col - MAX_COLNUM;
				if (customColIdx < m_customColumnHeaders.size()) {
					return headerData(index.column(), Qt::Horizontal).toString() +
					    QLatin1String(" ") + data(index).toString();
				}
			}
			Q_ASSERT(0);
			return QVariant();
			break;
		};
		break;

	case ROLE_PROXYSORT:
		switch (col) {
		case COL_ACCOUNT_ID:
			return ModelHelper::sortRank(
			    m_entries.at(row).identif.acntIdDb.username(),
			    m_entries.at(row).identif.msgId.dmId());
			break;
		case COL_MESSAGE_ID:
			return m_entries.at(row).identif.msgId.dmId();
			break;
		case COL_ANNOTATION:
			return m_entries.at(row).dmAnnotation;
			break;
		case COL_SENDER:
			return m_entries.at(row).dmSender;
			break;
		case COL_RECIPIENT:
			return m_entries.at(row).dmRecipient;
			break;
		case COL_DELIVERY_TIME:
			return ModelHelper::sortRank(
			    m_entries.at(row).identif.msgId.deliveryTime(),
			    m_entries.at(row).identif.msgId.dmId());
			break;
		case COL_ACCEPT_TIME:
			return ModelHelper::sortRank(
			    m_entries.at(row).dmAcceptanceTime,
			    m_entries.at(row).identif.msgId.dmId());
			break;
		case COL_ATTACH_DOWNLOADED:
			return ModelHelper::sortRank(
			    m_entries.at(row).identif.downloaded ? 1 : 0,
			    m_entries.at(row).identif.msgId.dmId());
			break;
		default:
			{
				const int customColIdx = col - MAX_COLNUM;
				if (customColIdx < m_customColumnHeaders.size()) {
					return ModelHelper::sortRank(
					    m_entries.at(row).customValues.value(col, QString()),
					    m_entries.at(row).identif.msgId.dmId());
				}
			}
			Q_ASSERT(0);
			return QVariant();
			break;
		}
		break;

	default:
		return QVariant();
		break;
	}
}

QVariant MsgFoundModel::headerData(int section, Qt::Orientation orientation,
    int role) const
{
	Q_UNUSED(orientation);

	switch (role) {
	case Qt::DisplayRole:
		switch (section) {
		case COL_ACCOUNT_ID:
			return tr("Data Box");
			break;
		case COL_MESSAGE_ID:
			return tr("Message ID");
			break;
		case COL_ANNOTATION:
			return tr("Subject");
			break;
		case COL_SENDER:
			return tr("Sender");
			break;
		case COL_RECIPIENT:
			return tr("Recipient");
			break;
		case COL_DELIVERY_TIME:
			return tr("Delivery Time");
			break;
		case COL_ACCEPT_TIME:
			return tr("Acceptance Time");
			break;
		case COL_ATTACH_DOWNLOADED:
			return QVariant();
			break;
		default:
			{
				const int customColIdx = section - MAX_COLNUM;
				if (customColIdx < m_customColumnHeaders.size()) {
					return m_customColumnHeaders.at(customColIdx);
				}
			}
			Q_ASSERT(0);
			return QVariant();
			break;
		}
		break;

	case Qt::DecorationRole:
		if (section == COL_ATTACH_DOWNLOADED) {
			return inconContainer.icon(IconContainer::ICON_ATTACHMENT);
		}
		return QVariant();
		break;

	case Qt::ToolTipRole:
		if (section == COL_ATTACH_DOWNLOADED) {
			return tr("Attachments downloaded");
		}
		return QVariant();
		break;

	default:
		return QVariant();
		break;
	}
}

void MsgFoundModel::appendData(const AcntIdDb &acntIdDb,
    const QList<MessageDb::SoughtMsg> &entryList)
{
	if (Q_UNLIKELY(entryList.isEmpty())) {
		return;
	}

	beginInsertRows(QModelIndex(), rowCount(),
	    rowCount() + entryList.size() - 1);

	for (const MessageDb::SoughtMsg &entry : entryList) {
		Entry e(acntIdDb, entry.mId, entry.isVodz, entry.type,
		    entry.isDownloaded, entry.dmAnnotation, entry.dmSender,
		    entry.dmRecipient, entry.dmAcceptanceTime, QString());
		m_entries.append(e);
	}

	endInsertRows();
}

void MsgFoundModel::setMessageDetail(const AcntId &acntId, const MsgId &msgId,
    const QString &detail)
{
	int foundRow = -1;
	for (int row = 0; row < rowCount(); ++row) {
		const MsgIdentification &identif = m_entries.at(row).identif;
		if ((((AcntId)identif.acntIdDb) == acntId) &&
		    (identif.msgId.dmId() == msgId.dmId())) {
			foundRow = row;
			break;
		}
	}

	if (foundRow < 0) {
		return;
	}

	m_entries[foundRow].msgDetail = detail;

	emit dataChanged(index(foundRow, COL_MESSAGE_ID),
	    index(foundRow, COL_MESSAGE_ID), QVector<int>() << Qt::ToolTipRole);
}

void MsgFoundModel::removeAllRows(void)
{
	beginResetModel();
	m_entries.clear();

	endResetModel();
}

void MsgFoundModel::remove(const AcntId &acntId, const MsgId &msgId)
{
	int foundRow = findRow(acntId, msgId.dmId());
	if (foundRow < 0) {
		return;
	}

	beginRemoveRows(QModelIndex(), foundRow, foundRow);

	m_entries.removeAt(foundRow);

	endRemoveRows();
}

QList<MsgFoundModel::MsgIdentification> MsgFoundModel::listIdentifications(
    qint64 dmId) const
{
	QList<MsgIdentification> identifierList;

	for (const Entry &entry : m_entries) {
		if (entry.identif.msgId.dmId() == dmId) {
			identifierList.append(entry.identif);
		}
	}

	return identifierList;
}

QList<MsgFoundModel::MsgIdentification> MsgFoundModel::listDownloaded(
    bool downloaded) const
{
	QList<MsgIdentification> identifierList;

	for (const Entry &entry : m_entries) {
		if (entry.identif.downloaded == downloaded) {
			identifierList.append(entry.identif);
		}
	}

	return identifierList;
}

bool MsgFoundModel::overrideDownloaded(const AcntId &acntId, const MsgId &msgId,
   bool forceDownloaded)
{
	int foundRow = findRow(acntId, msgId.dmId());
	if (foundRow < 0) {
		return false;
	}

	m_entries[foundRow].identif.downloaded = forceDownloaded;

	const QModelIndex changedIdx = index(foundRow, COL_ATTACH_DOWNLOADED);
	emit dataChanged(changedIdx, changedIdx);

	return true;
}

const MsgFoundModel::MsgIdentification &MsgFoundModel::msgIdentification(
    int row) const
{
	static const MsgIdentification invalidIdentif;

	if (Q_UNLIKELY(row > rowCount())) {
		Q_ASSERT(0);
		return invalidIdentif;
	}

	return m_entries.at(row).identif;
}

int MsgFoundModel::addCustomColumn(const QString &columnName)
{
	beginInsertColumns(QModelIndex(), columnCount(),
	    columnCount() + 1 - 1);

	m_customColumnHeaders.append(columnName);

	endInsertColumns();

	return columnCount() - 1;
}

bool MsgFoundModel::overrideCustomColumnVal(const AcntId &acntId,
    const MsgId &msgId, int col, const QString &forceVal)
{
	if (Q_UNLIKELY(((col < MAX_COLNUM) || (col >= columnCount())))) {
		return false;
	}

	const int foundRow = findRow(acntId, msgId.dmId());
	if (Q_UNLIKELY(foundRow < 0)) {
		return false;
	}

	m_entries[foundRow].customValues[col] = forceVal;

	const QModelIndex changedIdx = index(foundRow, col);
	emit dataChanged(changedIdx, changedIdx);

	return true;
}

QString MsgFoundModel::getCustomColumnVal(int row, int col) const
{
	if (Q_UNLIKELY(((col < MAX_COLNUM) || (col >= columnCount())))) {
		return QString();
	}
	if (Q_UNLIKELY(((row < 0) || (row >= rowCount())))) {
		return QString();
	}

	return m_entries[row].customValues[col];
}

int MsgFoundModel::findRow(const AcntId &acntId, qint64 dmId) const
{
	int foundRow = -1;
	for (int row = 0; row < rowCount(); ++row) {
		const MsgIdentification &identif = m_entries.at(row).identif;
		if ((((AcntId)identif.acntIdDb) == acntId) &&
		    (identif.msgId.dmId() == dmId)) {
			foundRow = row;
			break;
		}
	}
	return foundRow;
}
