/*
    Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>

    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 "dynamictreemodel.h"

#include <QHash>
#include <QList>
#include <QTimer>
#include <QMimeData>

#include <QDebug>

#include <QStringList>

// If DUMPTREE is defined, ModelInsertCommand dumps the tree of what it is inserting.
// #define DUMPTREE
#ifdef DUMPTREE
#include <iostream>
#endif

DynamicTreeModel::DynamicTreeModel(QObject *parent)
  : QAbstractItemModel(parent),
    nextId(1)
{
}

QModelIndex DynamicTreeModel::index(int row, int column, const QModelIndex &parent) const
{
//   if (column != 0)
//     return QModelIndex();

  if ( column < 0 || row < 0 )
    return QModelIndex();

  QList<QList<qint64> > childIdColumns = m_childItems.value(parent.internalId());

  const qint64 grandParent = findParentId(parent.internalId());
  if (grandParent >= 0) {
    QList<QList<qint64> > parentTable = m_childItems.value(grandParent);
    Q_ASSERT(parent.column() < parentTable.size());
    QList<qint64> parentSiblings = parentTable.at(parent.column());
    Q_ASSERT(parent.row() < parentSiblings.size());
  }

  if (childIdColumns.size() == 0)
    return QModelIndex();

  if (column >= childIdColumns.size())
    return QModelIndex();

  QList<qint64> rowIds = childIdColumns.at(column);

  if ( row >= rowIds.size())
    return QModelIndex();

  qint64 id = rowIds.at(row);

  return createIndex(row, column, reinterpret_cast<void *>(id));

}

qint64 DynamicTreeModel::findParentId(qint64 searchId) const
{
  if (searchId <= 0)
    return -1;

  QHashIterator<qint64, QList<QList<qint64> > > i(m_childItems);
  while (i.hasNext())
  {
    i.next();
    QListIterator<QList<qint64> > j(i.value());
    while (j.hasNext())
    {
      QList<qint64> l = j.next();
      if (l.contains(searchId))
      {
        return i.key();
      }
    }
  }
  return -1;
}

QModelIndex DynamicTreeModel::parent(const QModelIndex &index) const
{
  if (!index.isValid())
    return QModelIndex();

  qint64 searchId = index.internalId();
  qint64 parentId = findParentId(searchId);
  // Will never happen for valid index, but what the hey...
  if (parentId <= 0)
    return QModelIndex();

  qint64 grandParentId = findParentId(parentId);
  if (grandParentId < 0)
    grandParentId = 0;

  int column = 0;
  QList<qint64> childList = m_childItems.value(grandParentId).at(column);

  int row = childList.indexOf(parentId);

  return createIndex(row, column, reinterpret_cast<void *>(parentId));

}

int DynamicTreeModel::rowCount(const QModelIndex &index ) const
{
  QList<QList<qint64> > cols = m_childItems.value(index.internalId());

  if (cols.size() == 0 )
    return 0;

  if (index.column() > 0)
    return 0;

  return cols.at(0).size();
}

int DynamicTreeModel::columnCount(const QModelIndex &index ) const
{
//   Q_UNUSED(index);
  return m_childItems.value(index.internalId()).size();
}

QVariant DynamicTreeModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid())
    return QVariant();

  if ( DynamicTreeModelId == role )
    return index.internalId();

  if (Qt::DisplayRole == role || Qt::EditRole == role)
  {
    return m_items.value(index.internalId());
  }
  return QVariant();
}


bool DynamicTreeModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
  if (role == Qt::EditRole)
  {
    m_items[index.internalId()] = value.toString();
    dataChanged(index, index);
    return true;
  }

  return QAbstractItemModel::setData(index, value, role);
}

void DynamicTreeModel::clear()
{
  beginResetModel();
  m_items.clear();
  m_childItems.clear();
  nextId = 1;
  endResetModel();
}


bool DynamicTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int _column, const QModelIndex& parent)
{
  Q_UNUSED(action);
  Q_UNUSED(_column);
  QByteArray encoded = data->data(mimeTypes().first());

  QHash<QModelIndex, QList<int> > movedItems;
  bool ok;
  qint64 id;
  int _row;
  static const int column = 0;
  QHash<qint64, QList<QList<qint64> > >::const_iterator it;
  foreach(const QByteArray &ba, encoded.split('\0'))
  {
    id = ba.toInt(&ok);
    if (!ok)
      qDebug() << ba;
    Q_ASSERT(ok);

    _row = -1;
    for (it = m_childItems.constBegin(); it != m_childItems.constEnd(); ++it)
    {
      _row = it.value().first().indexOf(id);
      if (_row < 0)
        continue;
      movedItems[createIndex(_row, column, reinterpret_cast<void *>(id)).parent()].append(_row);
      break;
    }
    Q_ASSERT(_row >= 0);
    if (_row < 0)
      return false;
  }

  const int destRow = row < 0 ? 0 : row;
  const QList<int> destPath = indexToPath(parent);

  QList<int> srcPath;
  QModelIndex srcParent;
  QHash<QModelIndex, QList<int> >::iterator src_parent_it = movedItems.begin();
  int startRow = 0;
  int endRow = 0;
  int nextMovedRow = 0;

  QList<int> rowsMoved;
  QList<int>::iterator src_row_it;
  QList<int>::iterator rows_moved_end;
  QList<ModelMoveCommand *> moveCommands;

  for ( ; src_parent_it != movedItems.end(); ++src_parent_it)
  {
    srcParent = src_parent_it.key();
    srcPath = indexToPath(srcParent);

    rowsMoved = src_parent_it.value();
    qSort(rowsMoved);
    src_row_it = rowsMoved.begin();
    rows_moved_end = rowsMoved.end();
    startRow = *src_row_it;
    endRow = startRow;
    ++src_row_it;

    if (src_row_it == rows_moved_end)
    {
      moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));
      continue;
    }

    for ( ; src_row_it != rows_moved_end; ++src_row_it)
    {
      nextMovedRow = *src_row_it;

      if (nextMovedRow == endRow + 1)
      {
        ++endRow;
      } else {
        Q_ASSERT(nextMovedRow > endRow + 1);
        moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));
        startRow = nextMovedRow;
        endRow = nextMovedRow;

        if ((src_row_it + 1) == rows_moved_end)
          moveCommands.prepend(getMoveCommand(srcPath, startRow, endRow));

      }
    }
  }

  QPersistentModelIndex destParent = parent;
  QPersistentModelIndex destRowIndex = index(destRow, column, parent);

  ModelMoveCommand *firstCommand = moveCommands.takeFirst();
  firstCommand->setDestAncestors(indexToPath(parent));
  firstCommand->setDestRow(destRow);
  firstCommand->doCommand();

  if (!destRowIndex.isValid())
    destRowIndex = index(destRow, column, parent);

  int offset = firstCommand->endRow() - firstCommand->startRow() + 1;
  foreach(ModelMoveCommand *moveCommand, moveCommands)
  {
    moveCommand->setDestAncestors(indexToPath(destParent));
    moveCommand->setDestRow(destRowIndex.row() + offset);
    moveCommand->doCommand();
    offset = moveCommand->endRow() - moveCommand->startRow() + 1;
  }

  return false;
}

ModelMoveCommand* DynamicTreeModel::getMoveCommand(const QList<int> &srcPath, int startRow, int endRow)
{
  ModelMoveCommand *moveCommand = new ModelMoveCommand(this, this);
  moveCommand->setAncestorRowNumbers(srcPath);
  moveCommand->setStartRow(startRow);
  moveCommand->setEndRow(endRow);
  return moveCommand;
}

Qt::ItemFlags DynamicTreeModel::flags(const QModelIndex& index) const
{
  Qt::ItemFlags flags = QAbstractItemModel::flags(index);
  if (index.isValid())
    return flags | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable;
  return flags;
}

Qt::DropActions DynamicTreeModel::supportedDropActions() const
{
  return Qt::MoveAction;
}

QStringList DynamicTreeModel::mimeTypes() const
{
  QStringList types;
  types << QLatin1String("application/x-dynamictreemodel-itemlist");
  return types;
}

QMimeData* DynamicTreeModel::mimeData(const QModelIndexList& indexes) const
{
  QMimeData *data = new QMimeData();
  QByteArray itemData;
  QModelIndexList::const_iterator it = indexes.begin();
  const QModelIndexList::const_iterator end = indexes.end();
  while(it != end)
  {
    itemData.append(QByteArray::number(it->internalId()));
    ++it;
    if (it != end)
      itemData.append('\0');
  }
  data->setData(mimeTypes().first(), itemData);
  return data;
}

QList<int> DynamicTreeModel::indexToPath(const QModelIndex &_idx) const
{
  QList<int> list;
  QModelIndex idx = _idx;
  while (idx.isValid())
  {
    list.prepend(idx.row());
    idx = idx.parent();
  }
  return list;
}

QModelIndexList DynamicTreeModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const
{
  if (role != DynamicTreeModelId)
    return QAbstractItemModel::match(start, role, value, hits, flags);

  qint64 id = value.toLongLong();

  QHash<qint64, QList<QList<qint64> > >::const_iterator it = m_childItems.constBegin();
  const QHash<qint64, QList<QList<qint64> > >::const_iterator end = m_childItems.constEnd();

  QList<QList<qint64> > items;
  QList<QList<qint64> >::const_iterator itemIt;
  QList<QList<qint64> >::const_iterator itemEnd;
  int foundIndexRow;
  for ( ; it != end; ++it)
  {
    items = it.value();
    itemEnd = items.constEnd();
    for (itemIt = items.constBegin(); itemIt != itemEnd; ++itemIt)
    {
      foundIndexRow = itemIt->indexOf(id);
      if (foundIndexRow != -1)
      {
        static const int column = 0;
        return QModelIndexList() << createIndex(foundIndexRow, column, reinterpret_cast<void *>(id));
      }
    }
  }
  return QModelIndexList();
}

ModelChangeCommand::ModelChangeCommand( DynamicTreeModel *model, QObject *parent )
    : QObject(parent), m_model(model), m_startRow(-1), m_endRow(-1), m_numCols(1)
{

}

QModelIndex ModelChangeCommand::findIndex(const QList<int> &rows) const
{
  const int col = 0;
  QModelIndex parent = QModelIndex();
  QListIterator<int> i(rows);
  while (i.hasNext())
  {
    parent = m_model->index(i.next(), col, parent);
    Q_ASSERT(parent.isValid());
  }
  return parent;
}

ModelInsertCommand::ModelInsertCommand(DynamicTreeModel *model, QObject *parent )
    : ModelChangeCommand(model, parent)
{

}

QList<ModelInsertCommand::Token> ModelInsertCommand::tokenize(const QString& treeString) const
{
  QStringList parts = treeString.split("-");

  QList<Token> tokens;

  const QStringList::const_iterator begin = parts.constBegin();
  const QStringList::const_iterator end = parts.constEnd();

  QStringList::const_iterator it = begin;
  ++it;
  for (; it != end; ++it)
  {
    Token token;
    if (it->trimmed().isEmpty())
    {
      token.type = Token::Branch;
    } else {
      token.type = Token::Leaf;
      token.content = *it;
    }
    tokens.append(token);
  }
  return tokens;
}

void ModelInsertCommand::interpret(const QString& treeString)
{
  m_treeString = treeString;

  QList<int> depths = getDepths(m_treeString);

  int size = 0;
  qCount(depths, 0, size);
  Q_ASSERT(size != 0);

  m_endRow = m_startRow + size - 1;
}

QList<int> ModelInsertCommand::getDepths(const QString& treeString) const
{
  int depth = 0;
  QList<int> depths;

#ifdef DUMPTREE
  int id = 1;
#endif

  QList<Token> tokens = tokenize(treeString);
  while(!tokens.isEmpty())
  {
    Token token = tokens.takeFirst();

    if (token.type == Token::Branch)
    {
      ++depth;
      continue;
    }
    Q_ASSERT(token.type == Token::Leaf);

    depths.append(depth);
#ifdef DUMPTREE
    std::cout << "\"";
    for (int i = 0; i <= depth; ++i)
      std::cout << " -";
    std::cout << " " << id++ << "\"" << std::endl;
#endif
    depth = 0;
  }

  return depths;
}

void ModelInsertCommand::doCommand()
{
  QModelIndex parent = findIndex(m_rowNumbers);

  if (!m_treeString.isEmpty())
  {
    QList<int> depths = getDepths(m_treeString);

    int size = 0;
    qCount(depths, 0, size);
    Q_ASSERT(size != 0);
    m_endRow = m_startRow + size - 1;
  }
  m_model->beginInsertRows(parent, m_startRow, m_endRow);
  if (!m_treeString.isEmpty())
  {
    doInsertTree(parent);
  } else {
    qint64 parentId = parent.internalId();

    for (int row = m_startRow; row <= m_endRow; row++)
    {
      for(int col = 0; col < m_numCols; col++ )
      {
        if (m_model->m_childItems[parentId].size() <= col)
        {
          m_model->m_childItems[parentId].append(QList<qint64>());
        }
        qint64 id = m_model->newId();
        QString name = QString::number(id);

        m_model->m_items.insert(id, name);
        m_model->m_childItems[parentId][col].insert(row, id);

      }
    }
  }
  m_model->endInsertRows();
}

void ModelInsertCommand::doInsertTree(const QModelIndex &fragmentParent)
{
  QList<int> depths = getDepths(m_treeString);

  qint64 fragmentParentIdentifier = fragmentParent.internalId();

  QList<int>::const_iterator it = depths.constBegin();
  const QList<int>::const_iterator end = depths.constEnd();

  QList<qint64> recentParents;
  recentParents.append(fragmentParentIdentifier);

  qint64 lastId = 0;
  qint64 id;
  QString name;
  int depth = 0;
  int row = m_startRow;
  Q_ASSERT(*it == depth);

  QList<int> rows;
  rows.append(row);

  for( ; it != end; ++it)
  {
    if (*it > depth)
    {
      Q_ASSERT(*it == depth + 1);
      fragmentParentIdentifier = lastId;
      if (recentParents.size() == *it)
        recentParents.append(fragmentParentIdentifier);
      else
        recentParents[*it] = fragmentParentIdentifier;

      ++depth;

    } else if ( *it < depth )
    {
      fragmentParentIdentifier = recentParents.at(*it);
      depth = (*it);
    }

    if (rows.size() == depth)
      rows.append(0);

    id = m_model->newId();
    lastId = id;
    for (int column = 0; column < m_numCols; ++column) {
        if (m_model->m_childItems[fragmentParentIdentifier].size() <= column)
        {
            m_model->m_childItems[fragmentParentIdentifier].append(QList<qint64>());
        }
        m_model->m_items.insert(id, QString::number(id));
        m_model->m_childItems[fragmentParentIdentifier][column].insert(rows[depth], id);
        if (column != m_numCols - 1)
            id = m_model->newId();
    }
    rows[depth]++;
  }
}


ModelInsertAndRemoveQueuedCommand::ModelInsertAndRemoveQueuedCommand(DynamicTreeModel* model, QObject* parent)
    : ModelChangeCommand(model, parent)
{
  qRegisterMetaType<QModelIndex>("QModelIndex");
}

void ModelInsertAndRemoveQueuedCommand::queuedBeginInsertRows(const QModelIndex& parent, int start, int end)
{
  m_model->beginInsertRows(parent, start, end);
}

void ModelInsertAndRemoveQueuedCommand::queuedEndInsertRows()
{
  m_model->endInsertRows();
}

void ModelInsertAndRemoveQueuedCommand::queuedBeginRemoveRows(const QModelIndex& parent, int start, int end)
{
  m_model->beginRemoveRows(parent, start, end);
}

void ModelInsertAndRemoveQueuedCommand::queuedEndRemoveRows()
{
  m_model->endRemoveRows();
}

void ModelInsertAndRemoveQueuedCommand::purgeItem(qint64 parent)
{
  QList<QList<qint64> > childItemRows = m_model->m_childItems.value(parent);

  if (childItemRows.size() > 0)
  {
    for (int col = 0; col < m_numCols; col++)
    {
      QList<qint64> childItems = childItemRows[col];
      foreach(qint64 item, childItems)
      {
        purgeItem(item);
        m_model->m_childItems[parent][col].removeOne(item);
      }
    }
  }
  m_model->m_items.remove(parent);
}

void ModelInsertAndRemoveQueuedCommand::doCommand()
{
  QModelIndex parent = findIndex(m_rowNumbers);

  connect (this, SIGNAL(beginInsertRows(const QModelIndex &, int, int)), SLOT(queuedBeginInsertRows(const QModelIndex &, int, int)), Qt::QueuedConnection);
  connect (this, SIGNAL(endInsertRows()), SLOT(queuedEndInsertRows()), Qt::QueuedConnection);
  connect (this, SIGNAL(beginRemoveRows(const QModelIndex &, int, int)), SLOT(queuedBeginRemoveRows(const QModelIndex &, int, int)), Qt::QueuedConnection);
  connect (this, SIGNAL(endRemoveRows()), SLOT(queuedEndRemoveRows()), Qt::QueuedConnection);

  emit beginInsertRows(parent, m_startRow, m_endRow);
//   m_model->beginInsertRows(parent, m_startRow, m_endRow);
  qint64 parentId = parent.internalId();
  for (int row = m_startRow; row <= m_endRow; row++)
  {
    for(int col = 0; col < m_numCols; col++ )
    {
      if (m_model->m_childItems[parentId].size() <= col)
      {
        m_model->m_childItems[parentId].append(QList<qint64>());
      }
      qint64 id = m_model->newId();
      QString name = QString::number(id);

      m_model->m_items.insert(id, name);
      m_model->m_childItems[parentId][col].insert(row, id);

    }
  }

  emit endInsertRows();
//   m_model->endInsertRows();

  emit beginRemoveRows(parent, m_startRow, m_endRow);
//   m_model->beginRemoveRows(parent, m_startRow, m_endRow);
  for(int col = 0; col < m_numCols; col++ )
  {
    QList<qint64> childItems = m_model->m_childItems.value(parentId).value(col);
    for (int row = m_startRow; row <= m_endRow; row++)
    {
      qint64 item = childItems[row];
      purgeItem(item);
      m_model->m_childItems[parentId][col].removeOne(item);
    }
  }
  emit endRemoveRows();
//   m_model->endRemoveRows();

}

ModelRemoveCommand::ModelRemoveCommand(DynamicTreeModel *model, QObject *parent )
    : ModelChangeCommand(model, parent)
{

}

void ModelRemoveCommand::doCommand()
{
  QModelIndex parent = findIndex(m_rowNumbers);
  m_model->beginRemoveRows(parent, m_startRow, m_endRow);
  qint64 parentId = parent.internalId();
  for(int col = 0; col < m_numCols; col++ )
  {
    QList<qint64> childItems = m_model->m_childItems.value(parentId).value(col);
    for (int row = m_startRow; row <= m_endRow; row++)
    {
      qint64 item = childItems[row];
      purgeItem(item);
      m_model->m_childItems[parentId][col].removeOne(item);
    }
  }
  m_model->endRemoveRows();
}

void ModelRemoveCommand::purgeItem(qint64 parent)
{
  QList<QList<qint64> > childItemRows = m_model->m_childItems.value(parent);

  if (childItemRows.size() > 0)
  {
    for (int col = 0; col < m_numCols; col++)
    {
      QList<qint64> childItems = childItemRows[col];
      foreach(qint64 item, childItems)
      {
        purgeItem(item);
        m_model->m_childItems[parent][col].removeOne(item);
      }
    }
  }
  m_model->m_items.remove(parent);
}


ModelDataChangeCommand::ModelDataChangeCommand(DynamicTreeModel *model, QObject *parent)
  : ModelChangeCommand(model, parent), m_startColumn(0)
{

}

void ModelDataChangeCommand::doCommand()
{
  QModelIndex parent = findIndex(m_rowNumbers);
  QModelIndex topLeft = m_model->index(m_startRow, m_startColumn, parent);
  QModelIndex bottomRight = m_model->index(m_endRow, m_numCols - 1, parent);

  QList<QList<qint64> > childItems = m_model->m_childItems[parent.internalId()];


  for (int col = m_startColumn; col < m_startColumn + m_numCols; col++)
  {
    for (int row = m_startRow; row <= m_endRow; row++ )
    {
      QString name = QString::number( m_model->newId() );
      m_model->m_items[childItems[col][row]] = name;
    }
  }
  m_model->dataChanged(topLeft, bottomRight);
}


ModelMoveCommand::ModelMoveCommand(DynamicTreeModel *model, QObject *parent)
: ModelChangeCommand(model, parent)
{

}

bool ModelMoveCommand::emitPreSignal(const QModelIndex &srcParent, int srcStart, int srcEnd, const QModelIndex &destParent, int destRow)
{
  return m_model->beginMoveRows(srcParent, srcStart, srcEnd, destParent, destRow);
}

void ModelMoveCommand::doCommand()
{
  QModelIndex srcParent = findIndex(m_rowNumbers);
  QModelIndex destParent = findIndex(m_destRowNumbers);

  if (!emitPreSignal(srcParent, m_startRow, m_endRow, destParent, m_destRow))
  {
    return;
  }

  for (int column = 0; column < m_numCols; ++column)
  {
    QList<qint64> l = m_model->m_childItems.value(srcParent.internalId())[column].mid(m_startRow, m_endRow - m_startRow + 1 );

    for (int i = m_startRow; i <= m_endRow ; i++)
    {
      m_model->m_childItems[srcParent.internalId()][column].removeAt(m_startRow);
    }
    int d;
    if (m_destRow < m_startRow)
      d = m_destRow;
    else
    {
      if (srcParent == destParent)
        d = m_destRow - (m_endRow - m_startRow + 1);
      else
        d = m_destRow - (m_endRow - m_startRow);
    }

    foreach(const qint64 id, l)
    {

      if (!m_model->m_childItems.contains(destParent.internalId()))
      {
        m_model->m_childItems[destParent.internalId()].append(QList<qint64>());
      }

      m_model->m_childItems[destParent.internalId()][column].insert(d++, id);
    }
  }

  emitPostSignal();
}

void ModelMoveCommand::emitPostSignal()
{
  m_model->endMoveRows();
}


ModelMoveLayoutChangeCommand::ModelMoveLayoutChangeCommand(DynamicTreeModel* model, QObject* parent): ModelMoveCommand(model, parent)
{

}

ModelMoveLayoutChangeCommand::~ModelMoveLayoutChangeCommand()
{

}

bool ModelMoveLayoutChangeCommand::emitPreSignal(const QModelIndex& srcParent, int srcStart, int srcEnd, const QModelIndex& destParent, int destRow)
{
  m_model->layoutAboutToBeChanged();

  const int column = 0;

  for (int row = srcStart; row <= srcEnd; ++row)
  {
    m_beforeMoveList << m_model->index(row, column, srcParent);
  }

  if (srcParent != destParent)
  {
    for (int row = srcEnd + 1; row < m_model->rowCount(srcParent); ++row)
    {
      m_beforeMoveList << m_model->index(row, column, srcParent);
    }
    for (int row = destRow; row < m_model->rowCount(destParent); ++row)
    {
      m_beforeMoveList << m_model->index(row, column, destParent);
    }
  } else {
    if (destRow < srcStart)
    {
      for (int row = destRow; row < srcStart; ++row)
      {
        m_beforeMoveList << m_model->index(row, column, srcParent);
      }
    } else {
      for (int row = srcStart + (srcEnd - srcStart + 1); row < destRow; ++row)
      {
        m_beforeMoveList << m_model->index(row, column, srcParent);
      }
    }
  }
  // We assume that the move was legal here.
  return true;
}

void ModelMoveLayoutChangeCommand::emitPostSignal()
{
  int srcStart = m_startRow;
  int srcEnd = m_endRow;
  int destRow = m_destRow;

  // Moving indexes may affect the m_rowNumbers and m_destRowNumbers.
  // Instead of adjusting them programmatically, the test writer must specify them if they change.

  const QList<int> sourceRowNumbers = m_endOfMoveSourceAncestors.isEmpty() ? m_rowNumbers : m_endOfMoveSourceAncestors;
  QModelIndex srcParent = findIndex(sourceRowNumbers);

  const QList<int> destRowNumbers = m_endOfMoveDestAncestors.isEmpty() ? m_destRowNumbers : m_endOfMoveDestAncestors;
  QModelIndex destParent = findIndex(destRowNumbers);

  const int column = 0;

  QModelIndexList afterMoveList;

  if (srcParent != destParent)
  {
    for (int row = destRow; row <= (destRow + (srcEnd - srcStart)); ++row)
    {
      afterMoveList << m_model->index(row, column, destParent);
    }
    for (int row = srcStart; row < m_model->rowCount(srcParent); ++row)
    {
      afterMoveList << m_model->index(row, column, srcParent);
    }
    for (int row = destRow + (srcEnd - srcStart + 1); row < m_model->rowCount(destParent); ++row)
    {
      afterMoveList << m_model->index(row, column, destParent);
    }
  } else {
    if (destRow < srcStart)
    {
      for (int row = srcStart; row <= srcEnd; ++row)
      {
        afterMoveList << m_model->index(destRow + (srcStart - row), column, destParent);
      }
    } else {
      for (int row = srcStart; row <= srcEnd; ++row)
      {
        afterMoveList << m_model->index(destRow + (srcStart - row - 1), column, destParent);
      }
    }
    if (destRow < srcStart)
    {
      for (int row = destRow + 1; row <= srcStart; ++row)
      {
        afterMoveList << m_model->index(row, column, srcParent);
      }
    } else {
      for (int row = srcStart + (srcEnd - srcStart + 1); row < (srcStart + (destRow - srcEnd)); ++row)
      {
        afterMoveList << m_model->index(row - (srcEnd - srcStart + 1), column, srcParent);
      }
    }
  }

  m_model->changePersistentIndexList(m_beforeMoveList, afterMoveList);
  m_beforeMoveList.clear();
  m_model->layoutChanged();


}

ModelResetCommand::ModelResetCommand(DynamicTreeModel* model, QObject* parent)
  : ModelChangeCommand(model, parent)
{

}

ModelResetCommand::~ModelResetCommand()
{

}

void ModelResetCommand::setInitialTree(const QString& treeString)
{
  m_treeString = treeString;
}

void ModelResetCommand::doCommand()
{
  m_model->beginResetModel();
  bool blocked = m_model->blockSignals(true);
  m_model->clear();
  if (!m_treeString.isEmpty())
  {
    ModelInsertCommand *ins = new ModelInsertCommand(m_model);
    ins->setStartRow(0);
    ins->interpret(m_treeString);
    ins->doCommand();
  }
  m_model->blockSignals(blocked);
  m_model->endResetModel();
}

ModelLayoutChangeCommand::ModelLayoutChangeCommand(DynamicTreeModel* model, QObject* parent)
  : ModelChangeCommand(model, parent)
{

}

ModelLayoutChangeCommand::~ModelLayoutChangeCommand()
{

}

void ModelLayoutChangeCommand::setInitialTree(const QString& treeString)
{
  m_treeString = treeString;
}

void ModelLayoutChangeCommand::setPersistentChanges(const QList< ModelLayoutChangeCommand::PersistentChange >& changes)
{
  m_changes = changes;
}

void ModelLayoutChangeCommand::doCommand()
{
  m_model->layoutAboutToBeChanged();
  QModelIndexList oldList;

  foreach(const PersistentChange &change, m_changes)
  {
    const IndexFinder oldFinder(m_model, change.oldPath);
    oldList << oldFinder.getIndex();
  }

  bool blocked = m_model->blockSignals(true);
  m_model->clear();
  if (!m_treeString.isEmpty())
  {
    ModelInsertCommand *ins = new ModelInsertCommand(m_model);
    ins->setStartRow(0);
    ins->interpret(m_treeString);
    ins->doCommand();
  }

  QModelIndexList newList;
  foreach(const PersistentChange &change, m_changes)
  {
    const IndexFinder newFinder(m_model, change.newPath);
    newList << newFinder.getIndex();
  }
  m_model->changePersistentIndexList(oldList, newList);

  m_model->blockSignals(blocked);
  m_model->layoutChanged();
}




