/* This file is part of the KDE project
  Copyright (C) 2007 Dag Andersen <danders@get2net.dk>
  Copyright (C) 2011, 2012 Dag Andersen <danders@get2net.dk>
  Copyright (C) 2016 Dag Andersen <danders@get2net.dk>
  
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/

// clazy:excludeall=qstring-arg
#include "kptresourcemodel.h"

#include "kptlocale.h"
#include "kptcommonstrings.h"
#include "kptcommand.h"
#include "kptitemmodelbase.h"
#include "kptcalendar.h"
#include "kptduration.h"
#include "kptnode.h"
#include "kptproject.h"
#include "kpttask.h"
#include "kptresource.h"
#include "kptdatetime.h"
#include "kptdebug.h"

#include <KoIcon.h>

#include <QMimeData>
#include <QMimeDatabase>
#include <QStringList>
#include <QLocale>

#include <KIO/TransferJob>
#include <KIO/StatJob>

#ifdef PLAN_KCONTACTS_FOUND
#include <KContacts/Addressee>
#include <KContacts/VCardConverter>
#endif


namespace KPlato
{

//--------------------------------------

ResourceModel::ResourceModel(QObject *parent)
    : QObject(parent),
    m_project(0)
{
}

ResourceModel::~ResourceModel()
{
}

const QMetaEnum ResourceModel::columnMap() const
{
    return metaObject()->enumerator(metaObject()->indexOfEnumerator("Properties"));
}

void ResourceModel::setProject(Project *project)
{
    m_project = project;
}

int ResourceModel::propertyCount() const
{
    return columnMap().keyCount();
}

QVariant ResourceModel::name(const Resource *res, int role) const
{
    //debugPlan<<res->name()<<","<<role;
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
            return res->name();
        case Qt::ToolTipRole:
            if (res->isShared()) {
                return xi18nc("@info:tooltip", "%1 is a <emphasis>Shared</emphasis> resource and can thus be shared with other projects", res->name());
            }
            if (res->autoAllocate()) {
                return xi18nc("@info:tooltip", "%1:<nl/>This resource will be automatically allocated to new tasks", res->name());
            }
            return res->name();
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
        case Qt::DecorationRole:
            if (res->isBaselined()) {
                return koIcon("view-time-schedule-baselined");
             }
             break;
        case Qt::CheckStateRole:
            return res->autoAllocate() ? Qt::Checked : Qt::Unchecked;
        default:
            break;
    }
    return QVariant();
}

QVariant ResourceModel::name(const  ResourceGroup *res, int role) const
{
    //debugPlan<<res->name()<<","<<role;
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
            return res->name();
        case Qt::ToolTipRole:
            if (!res->isShared()) {
                return res->name();
            }
            return xi18nc("@info:tooltip", "%1 is a <emphasis>Shared</emphasis> resource group and can thus be shared with other projects", res->name());
            break;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::scope(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return res->isShared() ? i18n("Shared") : i18n("Local");
        case Qt::ToolTipRole:
            if (!res->isShared()) {
                return xi18nc("@info:tooltip", "%1 is a <emphasis>Local</emphasis> resource and can only be used in this project", res->name());
            }
            return xi18nc("@info:tooltip", "%1 is a <emphasis>Shared</emphasis> resource and can thus be shared with other projects", res->name());
        case Qt::EditRole:
            return res->isShared() ? "Shared" : "Local";
        case Role::EnumList:
            return QStringList() << i18n("Local") << i18n("Shared");
        case Role::EnumListValue:
            return res->isShared() ? 1 : 0;
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::scope(const ResourceGroup *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return res->isShared() ? i18n("Shared") : i18n("Local");
        case Qt::ToolTipRole:
            if (!res->isShared()) {
                return xi18nc("@info:tooltip", "%1 is a <emphasis>Local</emphasis> resource group and can only be used in this project", res->name());
            }
            return xi18nc("@info:tooltip", "%1 is a <emphasis>Shared</emphasis> resource group and can thus be shared with other projects", res->name());
        case Qt::EditRole:
            return res->isShared() ? "Shared" : "Local";
        case Role::EnumList:
            return QStringList() << i18n("Local") << i18n("Shared");
        case Role::EnumListValue:
            return res->isShared() ? 1 : 0;
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::type(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::ToolTipRole:
            return res->typeToString(true);
        case Qt::EditRole:
            return res->typeToString(false);
        case Role::EnumList:
            return res->typeToStringList(true);
        case Role::EnumListValue:
            return (int)res->type();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::type(const ResourceGroup *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::ToolTipRole:
            return res->typeToString(true);
        case Qt::EditRole:
            return res->typeToString(false);
        case Role::EnumList:
            return res->typeToStringList(true);
        case Role::EnumListValue:
            return (int)res->type();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}


QVariant ResourceModel::initials(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
        case Qt::ToolTipRole:
            return res->initials();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::email(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
        case Qt::ToolTipRole:
            return res->email();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::calendar(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole: {
            if (res->type() == Resource::Type_Team) {
                return " ";
            }
            QString s = i18n("None");
            Calendar *cal = res->calendar(true); // don't check for default calendar
            if (cal) {
                s = cal->name();
            } else if (res->type() == Resource::Type_Work) {
                // Do we get a default calendar
                cal = res->calendar();
                if (cal) {
                    s = i18nc("Default (calendar name)", "Default (%1)", cal->name());
                }
            }
            return s;
        }
        case Qt::ToolTipRole: {
            if (res->type() == Resource::Type_Team) {
                return xi18nc("@info:tooltip", "A team resource does not have a calendar");
            }
            QString s = xi18nc("@info:tooltip", "No calendar");
            Calendar *cal = res->calendar(true); // don't check for default calendar
            if (cal) {
                s = cal->name();
            } else if (res->type() == Resource::Type_Work) {
                // Do we get a default calendar
                cal = res->calendar();
                if (cal) {
                    s = xi18nc("@info:tooltip 1=calendar name", "Using default calendar: %1", cal->name());
                }
            }
            return s;
        }
        case Role::EnumList: {
            Calendar *cal = m_project->defaultCalendar();
            QString s = i18n("None");
            if (cal &&  res->type() == Resource::Type_Work) {
                s = i18nc("Default (calendar name)", "Default (%1)", cal->name());
            }
            return QStringList() << s << m_project->calendarNames();
        }
        case Qt::EditRole:
        case Role::EnumListValue: {
            Calendar *cal = res->calendar(true); // don't check for default calendar
            return cal == 0 ? 0 : m_project->calendarNames().indexOf(cal->name()) + 1;
        }
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::units(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
            return res->units();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::ToolTipRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::availableFrom(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return QLocale().toString(res->availableFrom(), QLocale::ShortFormat);
        case Qt::EditRole:
            return res->availableFrom();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::ToolTipRole: {
            if (res->availableFrom().isValid()) {
                return xi18nc("infor:tooltip", "Available from: %1", QLocale().toString(res->availableFrom(), QLocale::LongFormat));
            }
            return xi18nc("infor:tooltip", "Available from project target start time: %1", QLocale().toString(m_project->constraintStartTime(), QLocale::LongFormat));
        }
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::availableUntil(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return QLocale().toString(res->availableUntil(), QLocale::ShortFormat);
        case Qt::EditRole:
            return res->availableUntil();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::ToolTipRole: {
            if (res->availableUntil().isValid()) {
                return xi18nc("infor:tooltip", "Available until: %1", QLocale().toString(res->availableUntil(), QLocale::LongFormat));
            }
            return xi18nc("infor:tooltip", "Available until project target finish time: %1", QLocale().toString(m_project->constraintEndTime(), QLocale::LongFormat));
        }
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::normalRate(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return m_project->locale()->formatMoney(res->normalRate());
        case Qt::EditRole:
            return res->normalRate();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::ToolTipRole:
            return i18n("Cost per hour, normal time: %1", m_project->locale()->formatMoney(res->normalRate()));
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::overtimeRate(const Resource *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return m_project->locale()->formatMoney(res->overtimeRate());
        case Qt::EditRole:
            return res->overtimeRate();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::ToolTipRole:
            return i18n("Cost per hour, overtime: %1", m_project->locale()->formatMoney(res->overtimeRate()));
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::account(const Resource *resource, int role) const
{
    switch (role) {
        case Qt::DisplayRole: {
            Account *a = resource->account();
            return a == 0 ? i18n("None") : a->name();
        }
        case Qt::ToolTipRole: {
            Account *a = resource->account();
            return i18n("Account: %1", (a == 0 ? i18n("None") : a->name()));
        }
        case Role::EnumListValue:
        case Qt::EditRole: {
            Account *a = resource->account();
            return a == 0 ? 0 : (m_project->accounts().costElements().indexOf(a->name()) + 1);
        }
        case Role::EnumList: {
            QStringList lst;
            lst << i18n("None");
            lst += m_project->accounts().costElements();
            return lst;
        }
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceModel::data(const Resource *resource, int property, int role) const
{
    if (role == Role::ObjectType) {
        return OT_Resource;
    }
    QVariant result;
    if (resource == 0) {
        return result;
    }
    switch (property) {
        case ResourceName: result = name(resource, role); break;
        case ResourceScope: result = scope(resource, role); break;
        case ResourceType: result = type(resource, role); break;
        case ResourceInitials: result = initials(resource, role); break;
        case ResourceEmail: result = email(resource, role); break;
        case ResourceCalendar: result = calendar(resource, role); break;
        case ResourceLimit: result = units(resource, role); break;
        case ResourceAvailableFrom: result = availableFrom(resource, role); break;
        case ResourceAvailableUntil: result = availableUntil(resource, role); break;
        case ResourceNormalRate: result = normalRate(resource, role); break;
        case ResourceOvertimeRate: result = overtimeRate(resource, role); break;
        case ResourceAccount: result = account(resource, role); break;
        default:
            debugPlan<<"data: invalid display value: property="<<property;
            break;
    }
    return result;
}

QVariant ResourceModel::data(const ResourceGroup *group, int property, int role) const
{
    if (role == Role::ObjectType) {
        return OT_ResourceGroup;
    }
    QVariant result;
    if (group == 0) {
        return result;
    }
    switch (property) {
        case ResourceModel::ResourceName: result = name(group, role); break;
        case ResourceModel::ResourceScope: result = scope(group, role); break;
        case ResourceModel::ResourceType: result = type(group, role); break;
        default:
            if (role == Qt::DisplayRole) {
                if (property < propertyCount()) {
                    result = QString();
                } else {
                    debugPlan<<"data: invalid display value column"<<property;
                    return QVariant();
                }
            }
            break;
    }
    return result;
}


QVariant ResourceModel::headerData(int section, int role)
{
    if (role == Qt::DisplayRole) {
        switch (section) {
            case ResourceName: return i18n("Name");
            case ResourceScope: return i18n("Scope");
            case ResourceType: return i18n("Type");
            case ResourceInitials: return i18n("Initials");
            case ResourceEmail: return i18n("Email");
            case ResourceCalendar: return i18n("Calendar");
            case ResourceLimit: return i18n("Limit (%)");
            case ResourceAvailableFrom: return i18n("Available From");
            case ResourceAvailableUntil: return i18n("Available Until");
            case ResourceNormalRate: return i18n("Normal Rate");
            case ResourceOvertimeRate: return i18n("Overtime Rate");
            case ResourceAccount: return i18n("Account");
            default: return QVariant();
        }
    } else if (role == Qt::TextAlignmentRole) {
        switch (section) {
            case ResourceName:
            case ResourceScope:
            case ResourceType:
            case ResourceInitials:
            case ResourceEmail:
            case ResourceCalendar:
                return QVariant();
            case ResourceLimit:
                return (int)(Qt::AlignRight|Qt::AlignVCenter);
            case ResourceAvailableFrom:
            case ResourceAvailableUntil:
                return QVariant();
            case ResourceNormalRate:
            case ResourceOvertimeRate:
                return (int)(Qt::AlignRight|Qt::AlignVCenter);
            case ResourceAccount:
                return QVariant();
            default:
                return QVariant();
        }
    } else if (role == Qt::ToolTipRole) {
        switch (section) {
            case ResourceName: return ToolTip::resourceName();
            case ResourceScope: return ToolTip::resourceScope();
            case ResourceType: return ToolTip::resourceType();
            case ResourceInitials: return ToolTip::resourceInitials();
            case ResourceEmail: return ToolTip::resourceEMail();
            case ResourceCalendar: return ToolTip::resourceCalendar();
            case ResourceLimit: return ToolTip::resourceUnits();
            case ResourceAvailableFrom: return ToolTip::resourceAvailableFrom();
            case ResourceAvailableUntil: return ToolTip::resourceAvailableUntil();
            case ResourceNormalRate: return ToolTip::resourceNormalRate();
            case ResourceOvertimeRate: return ToolTip::resourceOvertimeRate();
            case ResourceAccount: return ToolTip::resourceAccount();
            default: return QVariant();
        }
    }
    return QVariant();
}

//--------------------------------------

ResourceItemModel::ResourceItemModel(QObject *parent)
    : ItemModelBase(parent),
    m_group(0),
    m_resource(0)
{
}

ResourceItemModel::~ResourceItemModel()
{
}

void ResourceItemModel::slotResourceToBeInserted(const ResourceGroup *group, int row)
{
    //debugPlan<<group->name()<<","<<row;
    Q_ASSERT(m_group == 0);
    m_group = const_cast<ResourceGroup*>(group);
    beginInsertRows(index(group), row, row);
}

void ResourceItemModel::slotResourceInserted(const Resource *resource)
{
    //debugPlan<<resource->name();
    Q_ASSERT(resource->parentGroup() == m_group);
#ifdef NDEBUG
    Q_UNUSED(resource)
#endif
    endInsertRows();
    m_group = 0;
    emit layoutChanged(); //HACK to make the right view react! Bug in qt?
}

void ResourceItemModel::slotResourceToBeRemoved(const Resource *resource)
{
    //debugPlan<<resource->name();
    Q_ASSERT(m_resource == 0);
#ifdef NDEBUG
    Q_UNUSED(resource)
#endif
    m_resource = const_cast<Resource*>(resource);
    int row = index(resource).row();
    beginRemoveRows(index(resource->parentGroup()), row, row);
}

void ResourceItemModel::slotResourceRemoved(const Resource *resource)
{
    //debugPlan<<resource->name();
    Q_ASSERT(resource == m_resource);
#ifdef NDEBUG
    Q_UNUSED(resource)
#endif
    endRemoveRows();
    m_resource = 0;
}

void ResourceItemModel::slotResourceGroupToBeInserted(const ResourceGroup *group, int row)
{
    //debugPlan<<group->name();
    Q_ASSERT(m_group == 0);
    m_group = const_cast<ResourceGroup*>(group);
    beginInsertRows(QModelIndex(), row, row);
}

void ResourceItemModel::slotResourceGroupInserted(const ResourceGroup *group)
{
    //debugPlan<<group->name();
    Q_ASSERT(group == m_group);
#ifdef NDEBUG
    Q_UNUSED(group)
#endif
    endInsertRows();
    m_group = 0;
}

void ResourceItemModel::slotResourceGroupToBeRemoved(const ResourceGroup *group)
{
    //debugPlan<<group->name();
    Q_ASSERT(m_group == 0);
    m_group = const_cast<ResourceGroup*>(group);
    int row = index(group).row();
    beginRemoveRows(QModelIndex(), row, row);
}

void ResourceItemModel::slotResourceGroupRemoved(const ResourceGroup *group)
{
    //debugPlan<<group->name();
    Q_ASSERT(group == m_group);
#ifdef NDEBUG
    Q_UNUSED(group)
#endif

    endRemoveRows();
    m_group = 0;
}

void ResourceItemModel::setProject(Project *project)
{
    beginResetModel();
    if (m_project) {
        disconnect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted);
        disconnect(m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged);
        disconnect(m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged);
        disconnect(m_project, &Project::resourceGroupChanged, this, &ResourceItemModel::slotResourceGroupChanged);

        disconnect(m_project, &Project::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeInserted);

        disconnect(m_project, &Project::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved);

        disconnect(m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeInserted);

        disconnect(m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved);

        disconnect(m_project, &Project::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupInserted);

        disconnect(m_project, &Project::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved);

        disconnect(m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceInserted);

        disconnect(m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved);

        disconnect(m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged);
    }
    m_project = project;
    if (m_project) {
        connect(m_project, &Project::aboutToBeDeleted, this, &ResourceItemModel::projectDeleted);
        connect(m_project, &Project::localeChanged, this, &ResourceItemModel::slotLayoutChanged);
        connect(m_project, &Project::resourceChanged, this, &ResourceItemModel::slotResourceChanged);
        connect(m_project, &Project::resourceGroupChanged, this, &ResourceItemModel::slotResourceGroupChanged);

        connect(m_project, &Project::resourceGroupToBeAdded, this, &ResourceItemModel::slotResourceGroupToBeInserted);

        connect(m_project, &Project::resourceGroupToBeRemoved, this, &ResourceItemModel::slotResourceGroupToBeRemoved);

        connect(m_project, &Project::resourceToBeAdded, this, &ResourceItemModel::slotResourceToBeInserted);

        connect(m_project, &Project::resourceToBeRemoved, this, &ResourceItemModel::slotResourceToBeRemoved);

        connect(m_project, &Project::resourceGroupAdded, this, &ResourceItemModel::slotResourceGroupInserted);

        connect(m_project, &Project::resourceGroupRemoved, this, &ResourceItemModel::slotResourceGroupRemoved);

        connect(m_project, &Project::resourceAdded, this, &ResourceItemModel::slotResourceInserted);

        connect(m_project, &Project::resourceRemoved, this, &ResourceItemModel::slotResourceRemoved);

        connect(m_project, &Project::defaultCalendarChanged, this, &ResourceItemModel::slotCalendarChanged);
    }
    m_model.setProject(m_project);
    endResetModel();
}

Qt::ItemFlags ResourceItemModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = ItemModelBase::flags(index);
    if (!m_readWrite) {
        //debugPlan<<"read only"<<flags;
        if (index.isValid()) {
            flags |= Qt::ItemIsDragEnabled;
        }
        return flags &= ~Qt::ItemIsEditable;
    }
    if (!index.isValid()) {
        //debugPlan<<"invalid"<<flags;
        return flags;
    }
    Resource *r = qobject_cast<Resource*>(object (index));
    if (r != 0) {
        flags |= Qt::ItemIsDragEnabled;
        if (r->isShared()) {
            flags &= ~Qt::ItemIsEditable;
            if (index.column() == ResourceModel::ResourceName) {
                flags |= Qt::ItemIsUserCheckable;
            }
            return flags;
        }
        switch (index.column()) {
            case ResourceModel::ResourceName:
                flags |= Qt::ItemIsEditable | Qt::ItemIsUserCheckable;
                break;
            case ResourceModel::ResourceScope:
                flags &= ~Qt::ItemIsEditable;
                break;
            case ResourceModel::ResourceType:
                if (! r->isBaselined()) {
                    flags |= Qt::ItemIsEditable;
                }
                break;
            case ResourceModel::ResourceAccount:
                if (! r->isBaselined()) {
                    flags |= Qt::ItemIsEditable;
                }
                break;
            case ResourceModel::ResourceNormalRate:
                if (! r->isBaselined()) {
                    flags |= Qt::ItemIsEditable;
                }
                break;
            case ResourceModel::ResourceOvertimeRate:
                if (! r->isBaselined()) {
                    flags |= Qt::ItemIsEditable;
                }
                break;
            default:
                flags |= Qt::ItemIsEditable;
        }
        //debugPlan<<"resource"<<flags;
    } else {
        ResourceGroup *g = qobject_cast<ResourceGroup*>(object(index));
        if (g) {
            flags |= Qt::ItemIsDragEnabled;
            if (g->isShared()) {
                flags &= ~Qt::ItemIsEditable;
                return flags;
            }
            flags |= Qt::ItemIsDropEnabled;
            switch (index.column()) {
                case ResourceModel::ResourceName: flags |= Qt::ItemIsEditable; break;
                case ResourceModel::ResourceType: flags |= Qt::ItemIsEditable; break;
                default: flags &= ~Qt::ItemIsEditable;
            }
            //debugPlan<<"group"<<flags;
        }
    }
    return flags;
}


QModelIndex ResourceItemModel::parent(const QModelIndex &index) const
{
    if (!index.isValid() || m_project == 0) {
        return QModelIndex();
    }
    //debugPlan<<index.internalPointer()<<":"<<index.row()<<","<<index.column();

    Resource *r = resource(index);
    if (r && r->parentGroup()) {
        // only resources have parent
        int row = m_project->indexOf(r->parentGroup());
        return createIndex(row, 0, r->parentGroup());
    }

    return QModelIndex();
}

QModelIndex ResourceItemModel::index(int row, int column, const QModelIndex &parent) const
{
    if (m_project == 0 || column < 0 || column >= columnCount() || row < 0) {
        return QModelIndex();
    }
    if (! parent.isValid()) {
        if (row < m_project->numResourceGroups()) {
            return createIndex(row, column, m_project->resourceGroupAt(row));
        }
        return QModelIndex();
    }
    ResourceGroup *g = group(parent);
    if (g) {
        if (row < g->numResources()) {
            return createIndex(row, column, g->resourceAt(row));
        }
        return QModelIndex();
    }
    return QModelIndex();
}

QModelIndex ResourceItemModel::index(const Resource *resource, int column) const
{
    if (m_project == 0 || resource == 0) {
        return QModelIndex();
    }
    Resource *r = const_cast<Resource*>(resource);
    int row = -1;
    ResourceGroup *par = r->parentGroup();
    if (par) {
        row = par->indexOf(r);
        return createIndex(row, column, r);
    }
    return QModelIndex();
}

QModelIndex ResourceItemModel::index(const ResourceGroup *group, int column) const
{
    if (m_project == 0 || group == 0) {
        return QModelIndex();
    }
    ResourceGroup *g = const_cast<ResourceGroup*>(group);
    int row = m_project->indexOf(g);
    return createIndex(row, column, g);

}

int ResourceItemModel::columnCount(const QModelIndex &/*parent*/) const
{
    return m_model.propertyCount();
}

int ResourceItemModel::rowCount(const QModelIndex &parent) const
{
    if (m_project == 0) {
        return 0;
    }
    if (! parent.isValid()) {
        return m_project->numResourceGroups();
    }
    ResourceGroup *g = group(parent);
    if (g) {
        return g->numResources();
    }
    return 0;
}

QVariant ResourceItemModel::name(const  ResourceGroup *res, int role) const
{
    //debugPlan<<res->name()<<","<<role;
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
        case Qt::ToolTipRole:
            return res->name();
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

bool ResourceItemModel::setName(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toString() == res->name()) {
                return false;
            }
            emit executeCommand(new ModifyResourceNameCmd(res, value.toString(), kundo2_i18n("Modify resource name")));
            return true;
        case Qt::CheckStateRole:
            emit executeCommand(new ModifyResourceAutoAllocateCmd(res, value.toBool(), kundo2_i18n("Modify resource auto allocate")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setName(ResourceGroup *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toString() == res->name()) {
                return false;
            }
            emit executeCommand(new ModifyResourceGroupNameCmd(res, value.toString(), kundo2_i18n("Modify resourcegroup name")));
            return true;
    }
    return false;
}

QVariant ResourceItemModel::type(const ResourceGroup *res, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
        case Qt::ToolTipRole:
            return res->typeToString(true);
        case Qt::EditRole:
            return res->typeToString(false);
        case Role::EnumList:
            return res->typeToStringList(true);
        case Role::EnumListValue:
            return (int)res->type();
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

bool ResourceItemModel::setType(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole: {
            Resource::Type v;
            QStringList lst = res->typeToStringList(false);
            if (lst.contains(value.toString())) {
                v = static_cast<Resource::Type>(lst.indexOf(value.toString()));
            } else {
                v = static_cast<Resource::Type>(value.toInt());
            }
            if (v == res->type()) {
                return false;
            }
            emit executeCommand(new ModifyResourceTypeCmd(res, v, kundo2_i18n("Modify resource type")));
            return true;
        }
    }
    return false;
}

bool ResourceItemModel::setType(ResourceGroup *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole: {
            ResourceGroup::Type v;
            QStringList lst = res->typeToStringList(false);
            if (lst.contains(value.toString())) {
                v = static_cast<ResourceGroup::Type>(lst.indexOf(value.toString()));
            } else {
                v = static_cast<ResourceGroup::Type>(value.toInt());
            }
            if (v == res->type()) {
                return false;
            }
            emit executeCommand(new ModifyResourceGroupTypeCmd(res, v, kundo2_i18n("Modify resourcegroup type")));
            return true;
        }
    }
    return false;
}

bool ResourceItemModel::setInitials(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toString() == res->initials()) {
                return false;
            }
            emit executeCommand(new ModifyResourceInitialsCmd(res, value.toString(), kundo2_i18n("Modify resource initials")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setEmail(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toString() == res->email()) {
                return false;
            }
            emit executeCommand(new ModifyResourceEmailCmd(res, value.toString(), kundo2_i18n("Modify resource email")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setCalendar(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
        {
            Calendar *c = 0;
            if (value.toInt() > 0) {
                QStringList lst = m_model.calendar(res, Role::EnumList).toStringList();
                if (value.toInt() < lst.count()) {
                    c = m_project->calendarByName(lst.at(value.toInt()));
                }
            }
            if (c == res->calendar(true)) {
                return false;
            }
            emit executeCommand(new ModifyResourceCalendarCmd(res, c, kundo2_i18n("Modify resource calendar")));
            return true;
        }
    }
    return false;
}


bool ResourceItemModel::setUnits(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toInt() == res->units()) {
                return false;
            }
            emit executeCommand(new ModifyResourceUnitsCmd(res, value.toInt(), kundo2_i18n("Modify resource available units")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setAvailableFrom(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toDateTime() == res->availableFrom()) {
                return false;
            }
            emit executeCommand(new ModifyResourceAvailableFromCmd(res, value.toDateTime(), kundo2_i18n("Modify resource available from")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setAvailableUntil(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toDateTime() == res->availableUntil()) {
                return false;
            }
            emit executeCommand(new ModifyResourceAvailableUntilCmd(res, value.toDateTime(), kundo2_i18n("Modify resource available until")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setNormalRate(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toDouble() == res->normalRate()) {
                return false;
            }
            emit executeCommand(new ModifyResourceNormalRateCmd(res, value.toDouble(), kundo2_i18n("Modify resource normal rate")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setOvertimeRate(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole:
            if (value.toDouble() == res->overtimeRate()) {
                return false;
            }
            emit executeCommand(new ModifyResourceOvertimeRateCmd(res, value.toDouble(), kundo2_i18n("Modify resource overtime rate")));
            return true;
    }
    return false;
}

bool ResourceItemModel::setAccount(Resource *res, const QVariant &value, int role)
{
    switch (role) {
        case Qt::EditRole: {
            Account *a = 0;
            if (value.type() == QVariant::Int) {
                QStringList lst = m_model.account(res, Role::EnumList).toStringList();
                if (value.toInt() >= lst.count()) {
                    return false;
                }
                a = m_project->accounts().findAccount(lst.at(value.toInt()));
            } else if (value.type() == QVariant::String) {
                a = m_project->accounts().findAccount(value.toString());
            }
            Account *old = res->account();
            if (old != a) {
                emit executeCommand(new ResourceModifyAccountCmd(*res, old, a, kundo2_i18n("Modify resource account")));
                return true;
            }
        }
        default:
            break;
    }
    return false;
}

QVariant ResourceItemModel::notUsed(const ResourceGroup *, int role) const
{
    switch (role) {
        case Qt::DisplayRole:
            return QString(" ");
        case Qt::TextAlignmentRole:
            return Qt::AlignCenter;
        case Qt::EditRole:
        case Qt::ToolTipRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant ResourceItemModel::data(const QModelIndex &index, int role) const
{
    QVariant result;
    QObject *obj = object(index);
    if (obj == 0) {
        return QVariant();
    }
    if (role == Qt::TextAlignmentRole) {
        // use same alignment as in header (headers always horizontal)
        return headerData(index.column(), Qt::Horizontal, role);
    }
    Resource *r = qobject_cast<Resource*>(obj);
    if (r) {
        result = m_model.data(r, index.column(), role);
    } else {
        ResourceGroup *g = qobject_cast<ResourceGroup*>(obj);
        if (g) {
            result = m_model.data(g, index.column(), role);
        }
    }
    return result;
}

bool ResourceItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (! index.isValid()) {
        return ItemModelBase::setData(index, value, role);
    }
    if (role != Qt::EditRole && role != Qt::CheckStateRole) {
        return false;
    }
    if ((flags(index) & (Qt::ItemIsEditable | Qt::ItemIsUserCheckable)) == 0) {
        return false;
    }
    QObject *obj = object(index);
    Resource *r = qobject_cast<Resource*>(obj);
    if (r) {
        switch (index.column()) {
            case ResourceModel::ResourceName: return setName(r, value, role);
            case ResourceModel::ResourceScope: return false; // Not editable
            case ResourceModel::ResourceType: return setType(r, value, role);
            case ResourceModel::ResourceInitials: return setInitials(r, value, role);
            case ResourceModel::ResourceEmail: return setEmail(r, value, role);
            case ResourceModel::ResourceCalendar: return setCalendar(r, value, role);
            case ResourceModel::ResourceLimit: return setUnits(r, value, role);
            case ResourceModel::ResourceAvailableFrom: return setAvailableFrom(r, value, role);
            case ResourceModel::ResourceAvailableUntil: return setAvailableUntil(r, value, role);
            case ResourceModel::ResourceNormalRate: return setNormalRate(r, value, role);
            case ResourceModel::ResourceOvertimeRate: return setOvertimeRate(r, value, role);
            case ResourceModel::ResourceAccount: return setAccount(r, value, role);
            default:
                qWarning("data: invalid display value column %d", index.column());
                return false;
        }
    } else {
        ResourceGroup *g = qobject_cast<ResourceGroup*>(obj);
        if (g) {
            switch (index.column()) {
                case ResourceModel::ResourceName: return setName(g, value, role);
                case ResourceModel::ResourceScope: return false; // Not editable
                case ResourceModel::ResourceType: return setType(g, value, role);
                default:
                    qWarning("data: invalid display value column %d", index.column());
                    return false;
            }
        }
    }
    return false;
}

QVariant ResourceItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal) {
        if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole) {
            return m_model.headerData(section, role);
        }
    }
    if (role == Qt::ToolTipRole) {
        return m_model.headerData(section, role);
    }
    return ItemModelBase::headerData(section, orientation, role);
}

QAbstractItemDelegate *ResourceItemModel::createDelegate(int col, QWidget *parent) const
{
    switch (col) {
        case ResourceModel::ResourceType: return new EnumDelegate(parent);
        case ResourceModel::ResourceCalendar: return new EnumDelegate(parent);
        case ResourceModel::ResourceAvailableFrom: return new DateTimeCalendarDelegate(parent);
        case ResourceModel::ResourceAvailableUntil: return new DateTimeCalendarDelegate(parent);
        case ResourceModel::ResourceAccount: return new EnumDelegate(parent);
        default: break;
    }
    return 0;
}

QObject *ResourceItemModel::object(const QModelIndex &index) const
{
    QObject *o = 0;
    if (index.isValid()) {
        Q_ASSERT(m_project);
        //debugPlan<<(void*)index.internalPointer()<<m_project->resourceGroups()<<m_project->resourceList();
        Q_ASSERT(m_project->resourceGroups().contains(static_cast<ResourceGroup*>(index.internalPointer())) || m_project->resourceList().contains(static_cast<Resource*>(index.internalPointer())));
        o = static_cast<QObject*>(index.internalPointer());
        Q_ASSERT(o);
    }
    return o;
}

ResourceGroup *ResourceItemModel::group(const QModelIndex &index) const
{
    return qobject_cast<ResourceGroup*>(object(index));
}

Resource *ResourceItemModel::resource(const QModelIndex &index) const
{
    return qobject_cast<Resource*>(object(index));
}

void ResourceItemModel::slotCalendarChanged(Calendar*)
{
    foreach (Resource *r, m_project->resourceList()) {
        if (r->calendar(true) == 0) {
            slotResourceChanged(r);
        }
    }
}

void ResourceItemModel::slotResourceChanged(Resource *res)
{
    ResourceGroup *g = res->parentGroup();
    if (g) {
        int row = g->indexOf(res);
        emit dataChanged(createIndex(row, 0, res), createIndex(row, columnCount() - 1, res));
        return;
    }
}

void ResourceItemModel::slotResourceGroupChanged(ResourceGroup *res)
{
    Project *p = res->project();
    if (p) {
        int row = p->resourceGroups().indexOf(res);
        emit dataChanged(createIndex(row, 0, res), createIndex(row, columnCount() - 1, res));
    }
}

Qt::DropActions ResourceItemModel::supportedDropActions() const
{
    return Qt::MoveAction | Qt::CopyAction;
}

bool ResourceItemModel::dropAllowed(const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data)
{
    if (data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) {
        QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        int i = 0;
        foreach (Resource *r, resourceList(stream)) {
            if (r->isShared()) {
                return false;
            }
        }
    }

    //debugPlan<<index<<data;
    // TODO: if internal, don't allow dropping on my own parent
    switch (dropIndicatorPosition) {
        case ItemModelBase::OnItem:
            return qobject_cast<ResourceGroup*>(object(index)); // Allow only on group
        default:
            break;
    }
    return false;
}

QStringList ResourceItemModel::mimeTypes() const
{
    return ItemModelBase::mimeTypes()
#ifdef PLAN_KDEPIMLIBS_FOUND
            << "text/x-vcard"
            << "text/directory"
            << "text/uri-list"
#endif
            << "application/x-vnd.kde.plan.resourceitemmodel.internal";
}

void ResourceItemModel::slotDataArrived(KIO::Job *job, const QByteArray &data)
{
    if (m_dropDataMap.contains(job)) {
        m_dropDataMap[ job ].data += data;
    }
}

void ResourceItemModel::slotJobFinished(KJob *job)
{
    if (job->error() || ! m_dropDataMap.contains(job)) {
        debugPlan<<(job->error() ? "Job error":"Error: no such job");
    } else if (QMimeDatabase().mimeTypeForData(m_dropDataMap[ job ].data).inherits(QStringLiteral("text/x-vcard"))) {
        ResourceGroup *g = 0;
        if (m_dropDataMap[ job ].parent.isValid()) {
            g = qobject_cast<ResourceGroup*>(object(m_dropDataMap[ job ].parent));
        } else {
            g = qobject_cast<ResourceGroup*>(object(index(m_dropDataMap[ job ].row, m_dropDataMap[ job ].column, m_dropDataMap[ job ].parent)));
        }
        if (g == 0) {
            debugPlan<<"No group"<<m_dropDataMap[ job ].row<<m_dropDataMap[ job ].column<<m_dropDataMap[ job ].parent;
        } else {
            createResources(g, m_dropDataMap[ job ].data);
        }
    }
    if (m_dropDataMap.contains(job)) {
        m_dropDataMap.remove(job);
    }
    disconnect(job, &KJob::result, this, &ResourceItemModel::slotJobFinished);
}

bool ResourceItemModel::createResources(ResourceGroup *group, const QByteArray &data)
{
#ifdef PLAN_KCONTACTS_FOUND
    KContacts::VCardConverter vc;
    KContacts::Addressee::List lst = vc.parseVCards(data);
    MacroCommand *m = new MacroCommand(kundo2_i18np("Add resource from address book", "Add %1 resources from address book", lst.count()));
    // Note: windows needs this type of iteration
    for (int a = 0; a < lst.count(); ++a) {
        Resource *r = new Resource();
        QString uid = lst[a].uid();
        if (! m_project->findResource(uid)) {
            r->setId(uid);
        }
        r->setName(lst[a].formattedName());
        r->setEmail(lst[a].preferredEmail());
        m->addCommand(new AddResourceCmd(group, r));
    }
    if (m->isEmpty()) {
        delete m;
        return false;
    }
    emit executeCommand(m);
    return true;
#else
    Q_UNUSED(group);
    Q_UNUSED(data);
    return false;
#endif
}

bool ResourceItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    debugPlan<<row<<column<<parent;
    if (action == Qt::IgnoreAction) {
        return true;
    }
    if (column > 0) {
        return false;
    }
    ResourceGroup *g = 0;
    if (parent.isValid()) {
        g = qobject_cast<ResourceGroup*>(object(parent));
    } else {
        g = qobject_cast<ResourceGroup*>(object(index(row, column, parent)));
    }
    if (g == 0) {
        debugPlan<<"No group"<<row<<column<<parent;
        return false;
    }
    debugPlan<<data->formats()<<g->name();
    if (data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal")) {
        debugPlan<<action<<Qt::MoveAction;
        if (action == Qt::MoveAction) {
            MacroCommand *m = 0;
            QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal");
            QDataStream stream(&encodedData, QIODevice::ReadOnly);
            int i = 0;
            foreach (Resource *r, resourceList(stream)) {
                if (r->parentGroup() == g) {
                    continue;
                }
                if (m == 0) m = new MacroCommand(KUndo2MagicString());
                m->addCommand(new MoveResourceCmd(g, r));
                ++i;
            }
            if (m) {
                KUndo2MagicString msg = kundo2_i18np("Move resource", "Move %1 resources", i);
                MacroCommand *c = new MacroCommand(msg);
                c->addCommand(m);
                emit executeCommand(c);
            }
            return true;
        }
        if (action == Qt::CopyAction) {
            MacroCommand *m = 0;
            QByteArray encodedData = data->data("application/x-vnd.kde.plan.resourceitemmodel.internal");
            QDataStream stream(&encodedData, QIODevice::ReadOnly);
            int i = 0;
            foreach (Resource *r, resourceList(stream)) {
                Resource *nr = new Resource(r);
                if (m == 0) m = new MacroCommand(KUndo2MagicString());
                m->addCommand(new AddResourceCmd(g, nr));
                ++i;
            }
            if (m) {
                KUndo2MagicString msg = kundo2_i18np("Copy resource", "Copy %1 resources", i);
                MacroCommand *c = new MacroCommand(msg);
                c->addCommand(m);
                emit executeCommand(c);
            }
            return true;
        }
        return true;
    }
    if (data->hasFormat("text/x-vcard") || data->hasFormat("text/directory")) {
        if (action != Qt::CopyAction) {
            return false;
        }
        QString f = data->hasFormat("text/x-vcard") ? "text/x-vcard" : "text/directory";
        return createResources(g, data->data(f));
    }
    if (data->hasFormat("text/uri-list")) {
        const QList<QUrl> urls = data->urls();
        if (urls.isEmpty()) {
            return false;
        }
        bool result = false;
        foreach (const QUrl &url, urls) {
            if (url.scheme() != "akonadi") {
                debugPlan<<url<<"is not 'akonadi'";
                continue;
            }
            // TODO: KIO::get will find out about this as well, no?
            KIO::StatJob* statJob = KIO::stat(url);
            statJob->setSide(KIO::StatJob::SourceSide);

            const bool isUrlReadable = statJob->exec();
            if (! isUrlReadable) {
                debugPlan<<url<<"does not exist";
                continue;
            }

            KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo);
            bool res = connect(job, &KIO::TransferJob::data, this, &ResourceItemModel::slotDataArrived);
            Q_ASSERT(res);
	    Q_UNUSED(res);
            res = connect(job, &KJob::result, this, &ResourceItemModel::slotJobFinished);
            Q_ASSERT(res);

            m_dropDataMap[ job ].action = action;
            m_dropDataMap[ job ].row = row;
            m_dropDataMap[ job ].column = column;
            m_dropDataMap[ job ].parent = parent;

            job->start();
            result = true;
        }
        return result;
    }
    return false;
}

QList<Resource*> ResourceItemModel::resourceList(QDataStream &stream)
{
    QList<Resource*> lst;
    while (!stream.atEnd()) {
        QString id;
        stream >> id;
        Resource *r = m_project->findResource(id);
        if (r) {
            lst << r;
        }
    }
    debugPlan<<lst;
    return lst;
}

QMimeData *ResourceItemModel::mimeData(const QModelIndexList & indexes) const
{
    QMimeData *m = ItemModelBase::mimeData(indexes);
    QByteArray encodedData;
    QDataStream stream(&encodedData, QIODevice::WriteOnly);
    QList<int> rows;
    foreach (const QModelIndex &index, indexes) {
        if (index.isValid() && !rows.contains(index.row())) {
            //debugPlan<<index.row();
            Resource *r = ::qobject_cast<Resource*>(object(index));
            if (r) {
                rows << index.row();
                stream << r->id();
            } else if (::qobject_cast<ResourceGroup*>(object(index))) {
                rows.clear();
                break;
            }
        }
    }
    if (!rows.isEmpty()) {
        m->setData("application/x-vnd.kde.plan.resourceitemmodel.internal", encodedData);
    }
    return m;
}

QModelIndex ResourceItemModel::insertGroup(ResourceGroup *g)
{
    //debugPlan;
    emit executeCommand(new AddResourceGroupCmd(m_project, g, kundo2_i18n("Add resource group")));
    int row = m_project->resourceGroups().indexOf(g);
    if (row != -1) {
        return createIndex(row, 0, g);
    }
    return QModelIndex();
}

QModelIndex ResourceItemModel::insertResource(ResourceGroup *g, Resource *r, Resource * /*after*/)
{
    //debugPlan;
    emit executeCommand(new AddResourceCmd(g, r, kundo2_i18n("Add resource")));
    int row = g->indexOf(r);
    if (row != -1) {
        return createIndex(row, 0, r);
    }
    return QModelIndex();
}

int ResourceItemModel::sortRole(int column) const
{
    switch (column) {
        case ResourceModel::ResourceAvailableFrom:
        case ResourceModel::ResourceAvailableUntil:
            return Qt::EditRole;
        default:
            break;
    }
    return Qt::DisplayRole;
}


//-------------------
ResourceItemSFModel::ResourceItemSFModel(QObject *parent)
    : QSortFilterProxyModel(parent)
{
    setDynamicSortFilter(true);
    setSourceModel(new ResourceItemModel(this));
}

void ResourceItemSFModel::setProject(Project *project)
{
    static_cast<ResourceItemModel*>(sourceModel())->setProject(project);
}

Resource *ResourceItemSFModel::resource(const QModelIndex &idx) const
{
    return static_cast<ResourceItemModel*>(sourceModel())->resource(mapToSource(idx));
}

QModelIndex ResourceItemSFModel::index(Resource *r) const
{
    return mapFromSource(static_cast<ResourceItemModel*>(sourceModel())->index(r));
}

Qt::ItemFlags ResourceItemSFModel::flags(const QModelIndex & index) const
{
    Qt::ItemFlags f = QSortFilterProxyModel::flags(index);
    if (index.isValid() && ! parent(index).isValid()) {
        // group, not selectable
        f &= ~Qt::ItemIsSelectable;
    }
    return f;
}

void ResourceItemSFModel::addFilteredResource(const Resource *r)
{
    if (! m_filteredResources.contains(r)) {
        m_filteredResources << r;
    }
}

bool ResourceItemSFModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{
    //TODO make this general filter
    ResourceItemModel *m = static_cast<ResourceItemModel*>(sourceModel());
    if (m->index(source_row, ResourceModel::ResourceType, source_parent).data(Role::EnumListValue).toInt() == ResourceGroup::Type_Work) {
        return false;
    }
    QModelIndex idx = m->index(source_row, 0, source_parent);
    return ! m_filteredResources.contains(m->resource(idx));
}

//-----------------------
AllocatedResourceItemModel::AllocatedResourceItemModel(QObject *parent)
    : QSortFilterProxyModel(parent),
    m_task(0)
{
    setDynamicSortFilter(true);
    setSourceModel(new ResourceItemModel(this));
}

int AllocatedResourceItemModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 2;
}

Project *AllocatedResourceItemModel::project() const
{
    return static_cast<ResourceItemModel*>(sourceModel())->project();
}

void AllocatedResourceItemModel::setProject(Project *project)
{
    debugPlan<<this->project()<<"="<<project;
    Project *p =this->project();
    if (p) {
        disconnect(p, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged);
    }
    static_cast<ResourceItemModel*>(sourceModel())->setProject(project);
    if (project) {
        connect(project, &Project::nodeChanged, this, &AllocatedResourceItemModel::slotNodeChanged);
    }
    debugPlan<<rowCount()<<":"<<sourceModel()->rowCount();
}

void AllocatedResourceItemModel::reset()
{
    beginResetModel();
    endResetModel();
    emit expandAll();
    emit resizeColumnToContents(0);
}

void AllocatedResourceItemModel::slotNodeChanged(Node *n)
{
    debugPlan<<(n==m_task)<<n<<n->name();
    if (n != m_task) {
        return;
    }
    reset();
}

Task *AllocatedResourceItemModel::task() const
{
    return m_task;
}

void AllocatedResourceItemModel::setTask(Task *task)
{
    debugPlan<<m_task<<"="<<task<<(task?task->name():"");
    m_task = task;
    reset();
    debugPlan<<rowCount()<<":"<<sourceModel()->rowCount();
}

QObject* AllocatedResourceItemModel::object(const QModelIndex& idx) const
{
    return static_cast<ResourceItemModel*>(sourceModel())->object(mapToSource(idx));
}

Resource *AllocatedResourceItemModel::resource(const QModelIndex &idx) const
{
    return qobject_cast<Resource*>(object(idx));
}

QModelIndex AllocatedResourceItemModel::index(Resource *r) const
{
    return mapFromSource(static_cast<ResourceItemModel*>(sourceModel())->index(r));
}

Qt::ItemFlags AllocatedResourceItemModel::flags(const QModelIndex & index) const
{
    Qt::ItemFlags f = QSortFilterProxyModel::flags(index);
    f &= ~Qt::ItemIsUserCheckable;
    return f;
}


QVariant AllocatedResourceItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (section == 1) {
        if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
            return xi18nc("@title:column", "Allocation");
        }
        return QVariant();
    }
    return QSortFilterProxyModel::headerData(section, orientation, role);
}

QVariant AllocatedResourceItemModel::allocation(const Resource *res, int role) const
{
    ResourceRequest *rr = m_task->requests().find(res);
    ResourceGroupRequest *gr = m_task->requests().find(res->parentGroup());
    if (rr == 0 || gr == 0) {
        return QVariant();
    }
    switch (role) {
        case Qt::DisplayRole: {
        case Qt::EditRole:
            // xgettext: no-c-format
            return i18nc("<value>%", "%1%",rr->units());
        }
        case Qt::ToolTipRole: {
            if (rr->units() == 0) {
                return xi18nc("@info:tooltip", "Not allocated");
            }
            return xi18nc("@info:tooltip", "%1 allocated out of %2 available", gr->count(), res->parentGroup()->numResources());
        }
        default:
            break;
    }
    return QVariant();
}

QVariant AllocatedResourceItemModel::allocation(const ResourceGroup *res, int role) const
{
    ResourceGroupRequest *gr = m_task->requests().find(res);
    if (gr == 0) {
        return QVariant();
    }
    switch (role) {
        case Qt::DisplayRole:
        case Qt::EditRole:
            return QString("%1 (%2)").arg(gr->units()).arg(gr->count());
        case Qt::ToolTipRole: {
            QString s1 = i18ncp("@info:tooltip",
                                 "%1 resource requested for dynamic allocation",
                                 "%1 resources requested for dynamic allocation",
                                 gr->units());
            QString s2 = i18ncp("@info:tooltip",
                                 "%1 resource allocated",
                                 "%1 resources allocated",
                                 gr->count());

            return xi18nc("@info:tooltip", "%1<nl/>%2", s1, s2);
        }
        case Qt::WhatsThisRole: {
            return xi18nc("@info:whatsthis",
                          "<title>Group allocations</title>"
                          "<para>You can allocate a number of resources from a group and let"
                          " the scheduler select from the available resources at the time of scheduling.</para>"
                          " These dynamically allocated resources will be in addition to any resource you have allocated specifically.");
        }
        case Role::Minimum: {
            return 0;
        }
        case Role::Maximum: {
            return res->numResources() - gr->units();
        }
        default:
            break;
    }
    return QVariant();
}

QVariant AllocatedResourceItemModel::data(const QModelIndex& idx, int role) const
{
    if (m_task == 0 || role == Qt::CheckStateRole || role == Qt::DecorationRole) {
        return QVariant();
    }
    if (idx.column() == 1) {
        switch (role) {
            case Qt::TextAlignmentRole:
                return Qt::AlignLeft;
            default: {
                QObject *o = object(idx);
                Resource *r = qobject_cast<Resource*>(o);
                if (r) {
                    return allocation(r, role);
                }
                ResourceGroup *g = qobject_cast<ResourceGroup*>(o);
                if (g) {
                    return allocation(g, role);
                }
                break;
            }
            return QVariant();
        }
    }
    return QSortFilterProxyModel::data(idx, role);
}

bool AllocatedResourceItemModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{
    if (m_task == 0) {
        return false;
    }
    QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
    if (! idx.isValid()) {
        return false;
    }
    bool result = false;
    const ResourceRequestCollection &req = m_task->requests();
    if (source_parent.isValid()) {
        const Resource *r = static_cast<ResourceItemModel*>(sourceModel())->resource(idx);
        result = (bool) req.find(r);
    } else {
        const ResourceGroup *g = static_cast<ResourceItemModel*>(sourceModel())->group(idx);
        ResourceGroupRequest *gr = req.find(g);
        result = (bool) gr && (gr->units() > 0 || gr->count() > 0);
    }
    debugPlan<<result<<":"<<source_parent<<idx;
    return result;
}

} // namespace KPlato
