/* This file is part of the KDE project
  Copyright (C) 2007, 2012 Dag Andersen <danders@get2net>

  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.
*/

#include "kptpertcpmmodel.h"

#include "kptglobal.h"
#include "kptcommonstrings.h"
#include "kptproject.h"
#include "kpttask.h"
#include "kptnode.h"
#include "kptschedule.h"
#include "kptdebug.h"

#include <KLocalizedString>

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


namespace KPlato
{

class Project;
class Node;
class Task;

typedef QList<Node*> NodeList;

// TODO: find some better values
static const quintptr ListItemId = static_cast<quintptr>(-1);
static const quintptr ProjectItemId  = static_cast<quintptr>(-2);

CriticalPathItemModel::CriticalPathItemModel( QObject *parent )
    : ItemModelBase( parent ),
    m_manager( 0 )
{
/*    connect( this, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()) );
    connect( this, SIGNAL(modelReset()), SLOT(slotReset()) );*/
}

CriticalPathItemModel::~CriticalPathItemModel()
{
}

void CriticalPathItemModel::slotNodeToBeInserted( Node *, int )
{
    //debugPlan<<node->name();
}

void CriticalPathItemModel::slotNodeInserted( Node * /*node*/ )
{
    //debugPlan<<node->getParent->name()<<"-->"<<node->name();
}

void CriticalPathItemModel::slotNodeToBeRemoved( Node *node )
{
    Q_UNUSED(node);
    //debugPlan<<node->name();
/*    if ( m_path.contains( node ) ) {
    }*/
}

void CriticalPathItemModel::slotNodeRemoved( Node *node )
{
    Q_UNUSED(node);
    //debugPlan<<node->name();
}

void CriticalPathItemModel::setProject( Project *project )
{
    beginResetModel();
    if ( m_project ) {
        disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted()));
        disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
        disconnect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
        disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
        disconnect( m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotLayoutToBeChanged()) );

        disconnect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
        disconnect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
        disconnect( m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotLayoutChanged()) );
    }
    m_project = project;
    m_nodemodel.setProject( project );
    if ( project ) {
        connect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted()));
        connect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
        connect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
        connect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
        connect( m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotLayoutToBeChanged()) );

        connect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
        connect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
        connect( m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotLayoutChanged()) );
    }
    endResetModel();
}

void CriticalPathItemModel::setManager( ScheduleManager *sm )
{
    beginResetModel();
    debugPlan<<this;
    m_manager = sm;
    m_nodemodel.setManager( sm );
    if ( m_project == 0 || m_manager == 0 ) {
        m_path.clear();
    } else {
        m_path = m_project->criticalPath( m_manager->scheduleId(), 0 );
    }
    debugPlan<<m_path;
    endResetModel();
}

QModelIndex CriticalPathItemModel::parent( const QModelIndex & ) const
{
    return QModelIndex();
}

QModelIndex CriticalPathItemModel::index( int row, int column, const QModelIndex &parent ) const
{
    if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
        return QModelIndex();
    }
    if ( parent.isValid() ) {
        return QModelIndex();
    }
    Node *n = m_path.value( row );
    QModelIndex i = createIndex(row, column, n );
    return i;
}

Duration::Unit CriticalPathItemModel::presentationUnit( const Duration &dur ) const
{
    if ( dur.toDouble( Duration::Unit_d ) < 1.0 ) {
        return Duration::Unit_h;
    }
    return Duration::Unit_d;
}

QVariant CriticalPathItemModel::name( int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return i18n( "Path" );
        case Qt::ToolTipRole:
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant CriticalPathItemModel::duration( int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
        case Qt::ToolTipRole: {
            Duration v = m_project->duration( m_manager->scheduleId() );
            return QVariant(QLocale().toString( v.toDouble( presentationUnit( v ) ), 'f', 1 ) + Duration::unitToString( presentationUnit( v ) ));
        }
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant CriticalPathItemModel::variance( int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
        case Qt::ToolTipRole: {
            double v = 0.0;
            foreach ( Node *n, m_path ) {
                long id = m_manager->scheduleId();
                v += n->variance( id, presentationUnit( m_project->duration( id ) ) );
            }
            return QLocale().toString( v, 'f', 1 );
            break;
        }
        case Qt::EditRole: {
            double v = 0.0;
            foreach ( Node *n, m_path ) {
                v += n->variance( m_manager->scheduleId() );
            }
            return v;
        }
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant CriticalPathItemModel::notUsed( int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return "";
        default:
            return QVariant();
    }
    return QVariant();
}

QVariant CriticalPathItemModel::data( const QModelIndex &index, int role ) const
{
    QVariant result;
    if ( ! index.isValid() ) {
        return result;
    }
    if ( role == Qt::TextAlignmentRole ) {
        return alignment( index.column() );
    }
    Node *n = node( index );
    if ( n == 0 ) {
        switch ( index.column() ) {
            case NodeModel::NodeName: result = name( role ); break;
            case NodeModel::NodeDuration: result = duration( role ); break;
            case NodeModel::NodeVarianceDuration: result = variance( role ); break;
            default:
                result = notUsed( role ); break;
        }
    } else  {
        result = m_nodemodel.data( n, index.column(), role );
    }
    if ( result.isValid() ) {
        if ( role == Qt::DisplayRole && result.type() == QVariant::String && result.toString().isEmpty()) {
            // HACK to show focus in empty cells
            result = ' ';
        }
        return result;
    }
    return result;
}

QVariant CriticalPathItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
    if ( orientation == Qt::Horizontal ) {
        if ( role == Qt::DisplayRole ) {
            return m_nodemodel.headerData( section, role );
        } else if ( role == Qt::TextAlignmentRole ) {
            return alignment( section );
        }
    }
    if ( role == Qt::ToolTipRole ) {
        return m_nodemodel.headerData( section, role );
    } else if ( role == Qt::WhatsThisRole ) {
        return m_nodemodel.headerData( section, role );
    }
    return ItemModelBase::headerData(section, orientation, role);
}

QVariant CriticalPathItemModel::alignment( int column ) const
{
    return m_nodemodel.headerData( column, Qt::TextAlignmentRole );
}

int CriticalPathItemModel::columnCount( const QModelIndex & ) const
{
    return m_nodemodel.propertyCount();
}

int CriticalPathItemModel::rowCount( const QModelIndex &parent ) const
{
    if ( parent.isValid() ) {
        return 0;
    }
    if ( m_manager && m_manager->expected() && m_manager->expected()->criticalPathList() ) {
        return m_path.count() + 1;
    }
    return 0;
}

Node *CriticalPathItemModel::node( const QModelIndex &index ) const
{
    if ( ! index.isValid() ) {
        return 0;
    }
    return m_path.value( index.row() );
}

void CriticalPathItemModel::slotNodeChanged( Node *node )
{
    debugPlan;
    if ( node == 0 || node->type() == Node::Type_Project || ! m_path.contains( node ) ) {
        return;
    }
    int row = m_path.indexOf( node );
    emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount() - 1, node ) );
}


//-----------------------------
PertResultItemModel::PertResultItemModel( QObject *parent )
    : ItemModelBase( parent ),
    m_manager( 0 )
{
/*    connect( this, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()) );
    connect( this, SIGNAL(modelReset()), SLOT(slotReset()) );*/
}

PertResultItemModel::~PertResultItemModel()
{
}

void PertResultItemModel::slotAboutToBeReset()
{
    debugPlan;
    clear();
}

void PertResultItemModel::slotReset()
{
    debugPlan;
    refresh();
}

void PertResultItemModel::slotNodeToBeInserted( Node *, int )
{
    //debugPlan<<node->name();
    clear();
}

void PertResultItemModel::slotNodeInserted( Node * /*node*/ )
{
    //debugPlan<<node->getParent->name()<<"-->"<<node->name();
    refresh();
}

void PertResultItemModel::slotNodeToBeRemoved( Node * /*node*/ )
{
    //debugPlan<<node->name();
    clear();
}

void PertResultItemModel::slotNodeRemoved( Node * /*node*/ )
{
    //debugPlan<<node->name();
    refresh();
}

void PertResultItemModel::setProject( Project *project )
{
    clear();
    if ( m_project ) {
        disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted()));
        disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
        disconnect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
        disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
        disconnect( m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotLayoutToBeChanged()) );

        disconnect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
        disconnect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
        disconnect( m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotLayoutChanged()) );
    }
    m_project = project;
    m_nodemodel.setProject( project );
    if ( project ) {
        connect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted()));
        connect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) );
        connect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) );
        connect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) );
        connect( m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotLayoutToBeChanged()) );

        connect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) );
        connect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) );
        connect( m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotLayoutChanged()) );
    }
    refresh();
}

void PertResultItemModel::setManager( ScheduleManager *sm )
{
    m_manager = sm;
    m_nodemodel.setManager( sm );
    refresh();
}

void PertResultItemModel::clear()
{
    debugPlan<<this;
    foreach ( NodeList *l, m_top ) {
        int c = l->count();
        if ( c > 0 ) {
            // FIXME: gives error msg:
            // Can't select indexes from different model or with different parents
            QModelIndex i = index( l );
            debugPlan<<i<<": "<<c;
//            beginRemoveRows( i, 0, c-1 );
//            endRemoveRows();
        }
    }
    m_critical.clear();
    m_noncritical.clear();
    if ( ! m_top.isEmpty() ) {
        beginRemoveRows( QModelIndex(), 0, m_top.count() - 1 );
        m_top.clear();
        m_topNames.clear();
        endRemoveRows();
    }
}

void PertResultItemModel::refresh()
{
    clear();
    if ( m_project == 0 ) {
        return;
    }
    long id = m_manager == 0 ? -1 : m_manager->scheduleId();
    debugPlan<<id;
    if ( id == -1 ) {
        return;
    }
    m_topNames << i18n( "Project" );
    m_top << &m_dummyList; // dummy
    const QList< NodeList > *lst = m_project->criticalPathList( id );
    if ( lst ) {
        for ( int i = 0; i < lst->count(); ++i ) {
            m_topNames << i18n( "Critical Path" );
            m_top.append( const_cast<NodeList*>( &( lst->at( i ) ) ) );
            debugPlan<<m_topNames.last()<<lst->at( i );
        }
        if ( lst->isEmpty() ) debugPlan<<"No critical path";
    }
    foreach( Node* n, m_project->allNodes() ) {
        if ( n->type() != Node::Type_Task && n->type() != Node::Type_Milestone ) {
            continue;
        }
        Task *t = static_cast<Task*>( n );
        if ( t->inCriticalPath( id ) ) {
            continue;
        } else if ( t->isCritical( id ) ) {
            m_critical.append( t );
        } else {
            m_noncritical.append( t );
        }
    }
    if ( ! m_critical.isEmpty() ) {
        m_topNames << i18n( "Critical" );
        m_top.append(&m_critical );
    }
    if ( ! m_noncritical.isEmpty() ) {
        m_topNames << i18n( "Non-critical" );
        m_top.append(&m_noncritical );
    }
    if ( ! m_top.isEmpty() ) {
        debugPlan<<m_top;
        beginInsertRows( QModelIndex(), 0, m_top.count() -1 );
        endInsertRows();
        foreach ( NodeList *l, m_top ) {
            int c = l->count();
            if ( c > 0 ) {
                beginInsertRows( index( l ), 0, c-1 );
                endInsertRows();
            }
        }
    }
}

Qt::ItemFlags PertResultItemModel::flags( const QModelIndex &index ) const
{
    Qt::ItemFlags flags = QAbstractItemModel::flags( index );
    flags &= ~( Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled );
    return flags;
}


QModelIndex PertResultItemModel::parent( const QModelIndex &index ) const
{
    if ( !index.isValid() ) {
        return QModelIndex();
    }
    //debugPlan<<index.internalPointer()<<": "<<index.row()<<", "<<index.column();
    int row = index.internalId();
    if ( row < 0 ) {
        return QModelIndex(); // top level has no parent
    }
    if ( m_top.value( row ) == 0 ) {
        return QModelIndex();
    }
    return createIndex( row, 0, ListItemId );
}

QModelIndex PertResultItemModel::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 == 0 ) {
            QModelIndex idx = createIndex(row, column, ProjectItemId ); // project
            return idx;
        }
        if ( row >= m_top.count() ) {
            return QModelIndex(); // shouldn't happend
        }
        QModelIndex idx = createIndex(row, column, ListItemId );
        //debugPlan<<parent<<", "<<idx;
        return idx;
    }
    if ( parent.row() == 0 ) {
        return QModelIndex();
    }
    NodeList *l = m_top.value( parent.row() );
    if ( l == 0 ) {
        return QModelIndex();
    }
    QModelIndex i = createIndex(row, column, parent.row() );
    return i;
}

// QModelIndex PertResultItemModel::index( const Node *node ) const
// {
//     if ( m_project == 0 || node == 0 ) {
//         return QModelIndex();
//     }
//     foreach( NodeList *l, m_top ) {
//         int row = l->indexOf( const_cast<Node*>( node ) );
//         if ( row != -1 ) {
//             return createIndex( row, 0, const_cast<Node*>( node ) );
//         }
//     }
//     return QModelIndex();
// }

QModelIndex PertResultItemModel::index( const NodeList *lst ) const
{
    if ( m_project == 0 || lst == 0 ) {
        return QModelIndex();
    }
    NodeList *l = const_cast<NodeList*>( lst );
    int row = m_top.indexOf( l );
    if ( row <= 0 ) {
        return QModelIndex();
    }
    return createIndex( row, 0, ListItemId );
}

QVariant PertResultItemModel::name( int row, int role ) const
{

    switch ( role ) {
        case Qt::DisplayRole:
        case Qt::EditRole:
        case Qt::ToolTipRole:
            return m_topNames.value( row );
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::name( const Node *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
        case Qt::EditRole:
        case Qt::ToolTipRole:
            return node->name();
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::earlyStart( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->earlyStart( m_manager->scheduleId() );
        case Qt::ToolTipRole:
            return QLocale().toString( node->earlyStart( m_manager->scheduleId() ).date(), QLocale::ShortFormat );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::earlyFinish( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->earlyFinish( m_manager->scheduleId() );
        case Qt::ToolTipRole:
            return QLocale().toString( node->earlyFinish( m_manager->scheduleId() ).date(), QLocale::ShortFormat );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::lateStart( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->lateStart( m_manager->scheduleId() );
        case Qt::ToolTipRole:
            return QLocale().toString( node->lateStart( m_manager->scheduleId() ).date(), QLocale::ShortFormat );
        case Qt::EditRole:
            break;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::lateFinish( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->lateFinish( m_manager->scheduleId() );
        case Qt::ToolTipRole:
            return QLocale().toString( node->lateFinish( m_manager->scheduleId() ).date(), QLocale::ShortFormat );
        case Qt::EditRole:
            break;
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::positiveFloat( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->positiveFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nHourFraction );
        case Qt::ToolTipRole:
            return node->positiveFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nDayTime );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::freeFloat( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->freeFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nHourFraction );
        case Qt::ToolTipRole:
            return node->freeFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nDayTime );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::negativeFloat( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->negativeFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nHourFraction );
        case Qt::ToolTipRole:
            return node->negativeFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nDayTime );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::startFloat( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->startFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nHourFraction );
        case Qt::ToolTipRole:
            return node->startFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nDayTime );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::finishFloat( const Task *node, int role ) const
{
    switch ( role ) {
        case Qt::DisplayRole:
            return node->finishFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nHourFraction );
        case Qt::ToolTipRole:
            return node->finishFloat( m_manager->scheduleId() ).toString( Duration::Format_i18nDayTime );
        case Qt::EditRole:
        case Qt::StatusTipRole:
        case Qt::WhatsThisRole:
            return QVariant();
    }
    return QVariant();
}

QVariant PertResultItemModel::data( const QModelIndex &index, int role ) const
{
    QVariant result;
    if ( ! index.isValid() ) {
        return result;
    }
    if ( role == Qt::TextAlignmentRole ) {
        return alignment( index.column() );
    }
    Node *n = node( index );
    if ( n == 0 ) {
        switch ( index.column() ) {
            case 0: return name( index.row(), role );
            default: break;
        }
        return QVariant();
    }
    if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) {
        result = m_nodemodel.data( n, index.column(), role );
    }
    if ( n->type() == Node::Type_Project ) {
        //Project *p = static_cast<Project*>( n );
        switch ( index.column() ) {
            case NodeModel::NodeName: result = name( NodeModel::NodeName, role ); break;
            default:
                //debugPlan<<"data: invalid display value column "<<index.column();
                return QVariant();
        }
    }
    if ( result.isValid() ) {
        if ( role == Qt::DisplayRole && result.type() == QVariant::String && result.toString().isEmpty()) {
            // HACK to show focus in empty cells
            result = ' ';
        }
        return result;
    }
    return QVariant();
}

QVariant PertResultItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
    if ( orientation == Qt::Horizontal ) {
        if ( role == Qt::DisplayRole ) {
            return m_nodemodel.headerData( section, role );
        } else if ( role == Qt::TextAlignmentRole ) {
            return alignment( section );
        }
    }
    if ( role == Qt::ToolTipRole ) {
        return m_nodemodel.headerData( section, role );
    } else if ( role == Qt::WhatsThisRole ) {
        return m_nodemodel.headerData( section, role );
    }
    return ItemModelBase::headerData(section, orientation, role);
}

QVariant PertResultItemModel::alignment( int column ) const
{
    return m_nodemodel.headerData( column, Qt::TextAlignmentRole );
}

QAbstractItemDelegate *PertResultItemModel::createDelegate( int column, QWidget * /*parent*/ ) const
{
    switch ( column ) {
        default: return 0;
    }
    return 0;
}

int PertResultItemModel::columnCount( const QModelIndex & ) const
{
    return m_nodemodel.propertyCount();
}

int PertResultItemModel::rowCount( const QModelIndex &parent ) const
{
    if ( ! parent.isValid() ) {
        //debugPlan<<"top="<<m_top.count();
        return m_top.count();
    }
    NodeList *l = list( parent );
    if ( l ) {
        //debugPlan<<"list "<<parent.row()<<": "<<l->count();
        return l->count();
    }
    //debugPlan<<"node "<<parent.row();
    return 0; // nodes don't have children
}

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


QStringList PertResultItemModel::mimeTypes() const
{
    return QStringList();
}

QMimeData *PertResultItemModel::mimeData( const QModelIndexList & ) const
{
    QMimeData *m = new QMimeData();
    return m;
}

bool PertResultItemModel::dropAllowed( Node *, const QMimeData * )
{
    return false;
}

bool PertResultItemModel::dropMimeData( const QMimeData *, Qt::DropAction , int , int , const QModelIndex & )
{
    return false;
}

NodeList *PertResultItemModel::list( const QModelIndex &index ) const
{
    if ( index.isValid() && index.internalId() == ListItemId ) {
        //debugPlan<<index<<"is list: "<<m_top.value( index.row() );
        return m_top.value( index.row() );
    }
    //debugPlan<<index<<"is not list";
    return 0;
}

Node *PertResultItemModel::node( const QModelIndex &index ) const
{
    if ( ! index.isValid() ) {
        return 0;
    }
    if ( index.internalId() == ProjectItemId ) {
        return m_project;
    }
    if ( index.internalId() == 0 ) {
        return 0;
    }
    NodeList *l = m_top.value( index.internalId() );
    if ( l ) {
        return l->value( index.row() );
    }
    return 0;
}

void PertResultItemModel::slotNodeChanged( Node *)
{
    debugPlan;
    refresh();
/*    if ( node == 0 || node->type() == Node::Type_Project ) {
        return;
    }
    int row = node->getParent()->findChildNode( node );
    emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount() - 1, node ) );*/
}


} // namespace KPlato
