/*=========================================================================

   Program:   ParaView
   Module:    pqPipelineModel.cxx

   Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
   All rights reserved.

   ParaView is a free software; you can redistribute it and/or modify it
   under the terms of the ParaView license version 1.2.

   See License_v1.2.txt for the full ParaView license.
   A copy of this license can be obtained by contacting
   Kitware Inc.
   28 Corporate Drive
   Clifton Park, NY 12065
   USA

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

=========================================================================*/
#include "pqPipelineModel.h"

#include "pqBoxChartView.h"
#include "pqLiveInsituManager.h"
#include "pqLiveInsituVisualizationManager.h"
#include "pqOutputPort.h"
#include "pqPipelineFilter.h"
#include "pqPipelineSource.h"
#include "pqServer.h"
#include "pqServerManagerModel.h"
#include "pqServerManagerObserver.h"
#include "pqSpreadSheetView.h"
#include "pqUndoStack.h"
#include "pqXYBagChartView.h"
#include "pqXYBarChartView.h"
#include "pqXYChartView.h"
#include "pqXYFunctionalBagChartView.h"
#include "pqXYHistogramChartView.h"
#include "vtkCommand.h"
#include "vtkNew.h"
#include "vtkPVXMLElement.h"
#include "vtkSMLiveInsituLinkProxy.h"
#include "vtkSMParaViewPipelineControllerWithRendering.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMViewProxy.h"

#include <QApplication>
#include <QFont>
#include <QString>
#include <QStyle>
#include <QSignalMapper>
#include <QtDebug>

class ModifiedLiveInsituLink : public vtkCommand
{
public:
  ModifiedLiveInsituLink(pqServer* insituSession,
                         pqPipelineModel* pipelineModel) :
    InsituServer(insituSession), PipelineModel(pipelineModel)
  {
  }
  virtual void Execute(
    vtkObject* vtkNotUsed(caller), unsigned long, void* vtkNotUsed(data))
  {
    this->PipelineModel->updateData(this->InsituServer);
  }

private:
  pqServer* InsituServer;
  pqPipelineModel* PipelineModel;
};


//-----------------------------------------------------------------------------
class pqPipelineModelDataItem : public QObject
{
  bool InConstructor;
public:
   enum IconType
    {
    SERVER,
    LINK,
    GEOMETRY,
    BAGCHART,
    BARCHART,
    BOXCHART,
    FUNCTIONALBAGCHART,
    HISTOGRAMCHART,
    LINECHART,
    TABLE,
    INDETERMINATE,
    EYEBALL,
    EYEBALL_GRAY,
    INSITU_EXTRACT,
    INSITU_EXTRACT_GRAY,
    INSITU_SERVER_RUNNING,
    INSITU_SERVER_PAUSED,
    INSITU_BREAKPOINT,
    INSITU_WRITER_PARAMETERS,
    LAST
    };

  static vtkNew<vtkSMParaViewPipelineControllerWithRendering> Controller;
public:
  pqPipelineModel* Model;
  pqPipelineModelDataItem* Parent;
  QList<pqPipelineModelDataItem*> Children;
  pqServerManagerModelItem* Object;
  pqPipelineModel::ItemType Type;
  IconType VisibilityIcon;
  bool Selectable;

  // This is a terrible iVar, agreed. But it makes my life easier.
  // This is valid only for elements of Type==Proxy. These refer to the link
  // items present for this item, if any. This list is automatically kept
  // updated.
  QList<pqPipelineModelDataItem*> Links;

  pqPipelineModelDataItem(QObject* p,
    pqServerManagerModelItem* object,
    pqPipelineModel::ItemType itemType,
    pqPipelineModel* model) :QObject(p)
    {
    this->InConstructor = true;
    this->Selectable = true;
    this->Model = model;
    this->Parent = NULL;
    this->Object = object;
    this->Type = itemType;
    this->VisibilityIcon = LAST;
    if (itemType == pqPipelineModel::Link)
      {
      pqPipelineModelDataItem* proxyItem = model->getDataItem(
        object, NULL, pqPipelineModel::Proxy);
      Q_ASSERT(proxyItem != 0);
      proxyItem->Links.push_back(this);
      }
    if (this->Object)
      {
      this->updateVisibilityIcon(this->Model->view(), false);
      }
    this->InConstructor = false;
    }
  ~pqPipelineModelDataItem()
    {
    if (this->Type == pqPipelineModel::Link && this->Model->Internal)
      {
      pqPipelineModelDataItem* proxyItem = this->Model->getDataItem(
        this->Object, NULL, pqPipelineModel::Proxy);
      if (proxyItem)
        {
        proxyItem->Links.removeAll(this);
        }
      }
    }

  pqPipelineModelDataItem& operator=(const pqPipelineModelDataItem& other)
    {
    this->Object = other.Object;
    this->Type = other.Type;
    this->VisibilityIcon = other.VisibilityIcon;
    foreach (pqPipelineModelDataItem* otherChild, other.Children)
      {
      pqPipelineModelDataItem* child = new pqPipelineModelDataItem(this,
        NULL, pqPipelineModel::Invalid , this->Model);
      this->addChild(child);
      *child = *otherChild;
      }
    return *this;
    }

  // no need to call this generally. Only needed when operator = is used.
  void updateLinks()
    {
    if (this->Type == pqPipelineModel::Link)
      {
      pqPipelineModelDataItem* proxyItem = this->Model->getDataItem(
        this->Object, NULL, pqPipelineModel::Proxy);
      Q_ASSERT(proxyItem != 0);
      proxyItem->Links.push_back(this);
      }
    foreach (pqPipelineModelDataItem* child, this->Children)
      {
      child->updateLinks();
      }
    }

  pqPipelineModel::ItemType getType()
    {
    return this->Type;
    }
  int getIndexInParent()
    {
    if (!this->Parent)
      {
      return 0;
      }
    return this->Parent->Children.indexOf(this);
    }

  IconType getIconType() const
    {
    switch (this->Type)
      {
    case pqPipelineModel::Server:
      {
      pqServer* server = qobject_cast<pqServer*>(this->Object);
      vtkSMLiveInsituLinkProxy* proxy = pqLiveInsituManager::linkProxy(server);
      return proxy ?
        ((vtkSMPropertyHelper(proxy, "SimulationPaused").GetAs<int>() == 1)?
         INSITU_SERVER_PAUSED : INSITU_SERVER_RUNNING):
        SERVER;
      }

    case pqPipelineModel::Proxy:
        {
        pqPipelineSource* source = qobject_cast<pqPipelineSource*>(
            this->Object);
        if (source->getNumberOfOutputPorts() > 1)
          {
          // icon is shown on the output ports.
          return INDETERMINATE;
          }
        return this->getIconType(source->getOutputPort(0));
        }

    case pqPipelineModel::Port:
        {
        pqOutputPort* port = qobject_cast<pqOutputPort*>(this->Object);
        return this->getIconType(port);
        }

    case pqPipelineModel::Link:
        return LINK;

    case pqPipelineModel::Invalid:
        return INDETERMINATE;
      }
    return INDETERMINATE;
    }

  void addChild(pqPipelineModelDataItem* child)
    {
    if (child->Parent)
      {
      qCritical() << "child has parent.";
      return;
      }
    child->setParent(this);
    child->Parent = this;
    this->Children.push_back(child);
    }

  void removeChild(pqPipelineModelDataItem* child)
    {
    if (child->Parent != this)
      {
      qCritical() << "Cannot remove a non-child.";
      return;
      }
    child->setParent(NULL);
    child->Parent = NULL;
    this->Children.removeAll(child);
    }

  // returns true when the icon has changed.
  bool updateVisibilityIcon(pqView* view, bool traverse_subtree)
    {
    IconType newIcon = LAST;
    switch (this->Type)
      {
    case pqPipelineModel::Proxy:
    case pqPipelineModel::Link:
        {
        pqPipelineSource* source =
          qobject_cast<pqPipelineSource*>(this->Object);
        if (source && source->getNumberOfOutputPorts() == 1)
          {
          newIcon = this->getVisibilityIcon(source->getOutputPort(0), view);
          }
        }
      break;

    case pqPipelineModel::Port:
        {
        pqOutputPort* port = qobject_cast<pqOutputPort*>(this->Object);
        newIcon = this->getVisibilityIcon(port, view);
        }
      break;

      case pqPipelineModel::Server:
        {
        pqServer* server = qobject_cast<pqServer*>(this->Object);
        newIcon = (pqLiveInsituManager::isInsituServer(server) ?
                   this->getBreakpointVisibilityIcon() : LAST);
        }
        break;

    default:
      break;
      }

    bool ret_val = false;
    if (this->VisibilityIcon != newIcon)
      {
      this->VisibilityIcon = newIcon;
      if (!this->InConstructor && this->Model)
        {
        this->Model->itemDataChanged(this);
        }
      ret_val = true;
      }
    if (traverse_subtree)
      {
      foreach (pqPipelineModelDataItem* child, this->Children)
        {
        child->updateVisibilityIcon(view, traverse_subtree);
        }
      }
    return ret_val;
    }

  bool isModified() const
    {
    pqProxy* proxy = qobject_cast<pqProxy*>(this->Object);
    return (proxy && (proxy->modifiedState() != pqProxy::UNMODIFIED));
    }

private:
  IconType getBreakpointVisibilityIcon() const
  {
    pqLiveInsituManager* server = pqLiveInsituManager::instance();
    return (server->hasBreakpoint() ? INSITU_BREAKPOINT : LAST);
  }

  IconType getVisibilityIcon(pqOutputPort* port, pqView* view) const
    {
    if (!port) { return LAST; }

    // I'm not a huge fan of this hacky way we are dealing with catalyst. We'll
    // have to come back and cleanly address how the pqPipelineModel deals with
    // "visibility" in a more generic, session-centric way.
    if (pqLiveInsituManager::isInsituServer(port->getServer()))
      {
      if (pqLiveInsituManager::isWriterParametersProxy(port->getSourceProxy()))
        {
        return LAST;
        }
      pqLiveInsituVisualizationManager* mgr =
        pqLiveInsituManager::managerFromInsitu(port->getServer());
      if (mgr)
        {
        return mgr->hasExtracts(port)? INSITU_EXTRACT :INSITU_EXTRACT_GRAY;
        }
      return LAST;
      }

    if (!view)
      {
      // If no view is present, it implies that a suitable type of view
      // will be created.
      return EYEBALL_GRAY;
      }
    if (this->Controller->GetVisibility(port->getSourceProxy(),
        port->getPortNumber(), view->getViewProxy()))
      {
      return EYEBALL;
      }
    return view->getViewProxy()->CanDisplayData(port->getSourceProxy(),
      port->getPortNumber())? EYEBALL_GRAY : LAST;
    }
  IconType getIconType(pqOutputPort* port) const
    {
    if (port->getSource()->property("INSITU_EXTRACT").toBool())
      {
      return INSITU_EXTRACT;
      }
    else if (pqLiveInsituManager::isWriterParametersProxy(
               port->getSourceProxy()))
      {
      return INSITU_WRITER_PARAMETERS;
      }

    QString type = this->Controller->GetPreferredViewType(
      port->getSourceProxy(), port->getPortNumber());
    if (type == pqXYBagChartView::XYBagChartViewType())
      {
      return BAGCHART;
      }
    if (type == pqXYBarChartView::XYBarChartViewType())
      {
      return BARCHART;
      }
    if (type == pqBoxChartView::chartViewType())
      {
      return BOXCHART;
      }
    if (type == pqXYFunctionalBagChartView::XYFunctionalBagChartViewType())
      {
      return FUNCTIONALBAGCHART;
      }
    if (type == pqXYHistogramChartView::XYHistogramChartViewType())
      {
      return HISTOGRAMCHART;
      }
    if (type == pqXYChartView::XYChartViewType())
      {
      return LINECHART;
      }
    if (type == pqSpreadSheetView::spreadsheetViewType())
      {
      return TABLE;
      }
    return GEOMETRY;
    }
};

vtkNew<vtkSMParaViewPipelineControllerWithRendering> pqPipelineModelDataItem::Controller;

//-----------------------------------------------------------------------------
class pqPipelineModelInternal
{
public:
  pqPipelineModelInternal(pqPipelineModel* parent):
    Root(parent, NULL, pqPipelineModel::Invalid, parent)
  {
  this->ModifiedFont.setBold(true);
  this->DelayedUpdateVisibilityTimer.setSingleShot(true);
  }

  QFont ModifiedFont;
  pqPipelineModelDataItem Root;
  pqTimer DelayedUpdateVisibilityTimer;
  QList<QPointer<pqPipelineSource> > DelayedUpdateVisibilityItems;
};

//-----------------------------------------------------------------------------
void pqPipelineModel::constructor()
{
  this->FilterRoleSession = NULL;
  this->Internal = new pqPipelineModelInternal(this);
  QObject::connect(&this->Internal->DelayedUpdateVisibilityTimer,
    SIGNAL(timeout()), this, SLOT(delayedUpdateVisibilityTimeout()));

  this->Editable = true;
  this->View = NULL;

  QObject::connect(pqLiveInsituManager::instance(),
                   SIGNAL(connectionInitiated(pqServer*)),
                   this,
                   SLOT(onInsituConnectionInitiated(pqServer*)));

  // Initialize the pixmap list.
  this->PixmapList = new QPixmap[pqPipelineModelDataItem::LAST+1];

  this->PixmapList[pqPipelineModelDataItem::SERVER].load(
    ":/pqWidgets/Icons/pqServer16.png");
  this->PixmapList[pqPipelineModelDataItem::LINK].load(
    ":/pqWidgets/Icons/pqLinkBack16.png");
  this->PixmapList[pqPipelineModelDataItem::GEOMETRY].load(
    ":/pqWidgets/Icons/pq3DView16.png");
  this->PixmapList[pqPipelineModelDataItem::BAGCHART].load(
    ":/pqWidgets/Icons/pqBagChart16.png");
  this->PixmapList[pqPipelineModelDataItem::BARCHART].load(
    ":/pqWidgets/Icons/pqHistogram16.png");
  this->PixmapList[pqPipelineModelDataItem::BOXCHART].load(
    ":/pqWidgets/Icons/pqBoxChart16.png");
  this->PixmapList[pqPipelineModelDataItem::FUNCTIONALBAGCHART].load(
    ":/pqWidgets/Icons/pqFunctionalBagChart16.png");
  this->PixmapList[pqPipelineModelDataItem::HISTOGRAMCHART].load(
    ":/pqWidgets/Icons/pqHistogram16.png");
  this->PixmapList[pqPipelineModelDataItem::LINECHART].load(
    ":/pqWidgets/Icons/pqLineChart16.png");
  this->PixmapList[pqPipelineModelDataItem::TABLE].load(
    ":/pqWidgets/Icons/pqSpreadsheet16.png");
  this->PixmapList[pqPipelineModelDataItem::INDETERMINATE].load(
    ":/pqWidgets/Icons/pq3DView16.png");
  this->PixmapList[pqPipelineModelDataItem::EYEBALL].load(
    ":/pqWidgets/Icons/pqEyeball16.png");
  this->PixmapList[pqPipelineModelDataItem::EYEBALL_GRAY].load(
    ":/pqWidgets/Icons/pqEyeballd16.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_EXTRACT].load(
    ":/pqWidgets/Icons/pqLinkIn16.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_EXTRACT_GRAY].load(
    ":/pqWidgets/Icons/pqLinkIn16d.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_SERVER_RUNNING].load(
    ":/pqWidgets/Icons/pqInsituServerRunning16.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_SERVER_PAUSED].load(
    ":/pqWidgets/Icons/pqInsituServerPaused16.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_BREAKPOINT].load(
    ":/pqWidgets/Icons/pqInsituBreakpoint16.png");
  this->PixmapList[pqPipelineModelDataItem::INSITU_WRITER_PARAMETERS].load(
    ":/pqWidgets/Icons/pqSave32.png");
}

//-----------------------------------------------------------------------------
pqPipelineModel::pqPipelineModel(QObject *p)
  : QAbstractItemModel(p),
    LinkCallback(NULL)
{
  this->constructor();
}

//-----------------------------------------------------------------------------
pqPipelineModel::pqPipelineModel(
  const pqPipelineModel &other,
  QObject *parentObject)
  : QAbstractItemModel(parentObject),
    LinkCallback(NULL)
{
  this->constructor();
  this->Internal->Root = other.Internal->Root;
  this->Internal->Root.updateLinks();
}

//-----------------------------------------------------------------------------
pqPipelineModel::pqPipelineModel(const pqServerManagerModel &other,
    QObject *parentObject)
  : QAbstractItemModel(parentObject),
    LinkCallback(NULL)
{
  this->constructor();

  // Build a pipeline model from the current server manager model.
  QList<pqPipelineSource *> sources;
  QList<pqPipelineSource *>::Iterator source;
  QList<pqServer *> servers = other.findItems<pqServer*>();
  QList<pqServer *>::Iterator server = servers.begin();
  for( ; server != servers.end(); ++server)
    {
    // Add the server to the model.
    this->addServer(*server);

    // Add the sources for the server.
    sources = other.findItems<pqPipelineSource *>(*server);
    for(source = sources.begin(); source != sources.end(); ++source)
      {
      this->addSource(*source);
      }

    // Set up the pipeline connections.
    for(source = sources.begin(); source != sources.end(); ++source)
      {
      int numOutputPorts = (*source)->getNumberOfOutputPorts();
      for(int port = 0; port < numOutputPorts; port++)
        {
        int numConsumers = (*source)->getNumberOfConsumers(port);
        for(int i = 0; i < numConsumers; ++i)
          {
          this->addConnection(*source, (*source)->getConsumer(port, i), port);
          }
        }
      }
    }
}

//-----------------------------------------------------------------------------
pqPipelineModel::~pqPipelineModel()
{
  // setting this->Internal to NULL keeps the ~pqPipelineModelDataItem() from
  // trying to update the link connections.
  pqPipelineModelInternal* internal = this->Internal;
  this->Internal = NULL;
  delete internal;

  if (this->PixmapList)
    {
    delete [] this->PixmapList;
    }
  if (this->LinkCallback)
    {
    this->LinkCallback->Delete();
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::onInsituConnectionInitiated(pqServer* displaySession)
{
  pqLiveInsituVisualizationManager* manager =
    pqLiveInsituManager::instance()->managerFromDisplay(displaySession);
  vtkSMLiveInsituLinkProxy* proxy =
    pqLiveInsituManager::linkProxy(manager->insituSession());
  if (manager && proxy)
    {
    if (this->LinkCallback)
      {
      this->LinkCallback->Delete();
      }
    this->LinkCallback =
      new ModifiedLiveInsituLink(manager->insituSession(), this);
    proxy->AddObserver (vtkCommand::ModifiedEvent, this->LinkCallback);
    QObject::connect(
      pqLiveInsituManager::instance(),
      SIGNAL(breakpointAdded(pqServer*)),
      this, SLOT(updateDataServer(pqServer*)));
    QObject::connect(
      pqLiveInsituManager::instance(),
      SIGNAL(breakpointRemoved(pqServer*)),
      this, SLOT(updateDataServer(pqServer*)));
    }
}

//-----------------------------------------------------------------------------
pqPipelineModel::ItemType pqPipelineModel::getTypeFor(
    const QModelIndex &idx) const
{
  if (idx.isValid() && idx.model() == this)
    {
    pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
      idx.internalPointer());
    return item->getType();
    }

  return pqPipelineModel::Invalid;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::setSelectable(const QModelIndex &idx, bool selectable)
{
  if(idx.isValid() && idx.model() == this)
    {
    pqPipelineModelDataItem*item = reinterpret_cast<pqPipelineModelDataItem*>(
      idx.internalPointer());
    item->Selectable = selectable;
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::setSubtreeSelectable(pqServerManagerModelItem *smitem,
  bool selectable)
{
  pqOutputPort* port = qobject_cast<pqOutputPort*>(smitem);
  if (port && port->getSource())
    {
    smitem = port->getSource();
    }

  pqPipelineModelDataItem* item;
  pqServer* server = qobject_cast<pqServer*>(smitem);
  if (server)
    {
    item = this->getDataItem(smitem, &this->Internal->Root, pqPipelineModel::Server);
    }
  else
    {
    item = this->getDataItem(smitem,
      &this->Internal->Root, pqPipelineModel::Proxy);
    }
  this->setSubtreeSelectable(item, selectable);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::setSubtreeSelectable(pqPipelineModelDataItem* item,
  bool selectable)
{
  if (item)
    {
    item->Selectable = selectable;
    foreach (pqPipelineModelDataItem* link, item->Links)
      {
      link->Selectable = selectable;
      }
    foreach (pqPipelineModelDataItem* child, item->Children)
      {
      this->setSubtreeSelectable(child, selectable);
      }
    }
}

//-----------------------------------------------------------------------------
bool pqPipelineModel::isSelectable(const QModelIndex &idx) const
{
  if(idx.isValid() && idx.model() == this)
    {
    pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
        idx.internalPointer());
    return item->Selectable;
    }

  return false;
}
//-----------------------------------------------------------------------------
QModelIndex pqPipelineModel::getNextIndex(const QModelIndex idx,
    const QModelIndex &root) const
{
  // If the index has children, return the first child.
  if(this->rowCount(idx) > 0)
    {
    return this->index(0, 0, idx);
    }

  // Search up the parent chain for an index with more children.
  QModelIndex current = idx;
  while(current.isValid() && current != root)
    {
    QModelIndex parentIndex = current.parent();
    if(current.row() < this->rowCount(parentIndex) - 1)
      {
      return this->index(current.row() + 1, 0, parentIndex);
      }

    current = parentIndex;
    }

  return QModelIndex();
}

//-----------------------------------------------------------------------------
int pqPipelineModel::rowCount(const QModelIndex &parentIndex) const
{
  if (parentIndex.isValid() && parentIndex.model() == this)
    {
    pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
      parentIndex.internalPointer());
    return item->Children.size();
    }
  return this->Internal->Root.Children.size();
}

//-----------------------------------------------------------------------------
int pqPipelineModel::columnCount(const QModelIndex &) const
{
  return 2;
}

//-----------------------------------------------------------------------------
bool pqPipelineModel::hasChildren(const QModelIndex &parentIndex) const
{
  return this->rowCount(parentIndex) > 0;
}

//-----------------------------------------------------------------------------
QModelIndex pqPipelineModel::index(int row, int column,
    const QModelIndex &parentIndex) const
{
  // Make sure the row and column number is within range.
  int rows = this->rowCount(parentIndex);
  int columns = this->columnCount(parentIndex);
  if(row < 0 || row >= rows || column < 0 || column >= columns)
    {
    return QModelIndex();
    }

  pqPipelineModelDataItem* parentItem = 0;
  if(parentIndex.isValid())
    {
    parentItem = reinterpret_cast<pqPipelineModelDataItem*>(
      parentIndex.internalPointer());
    }
  else
    {
    // The parent refers to the model root.
    parentItem = &this->Internal->Root;
    }

  return this->createIndex(row, column, parentItem->Children[row]);
}

//-----------------------------------------------------------------------------
QModelIndex pqPipelineModel::parent(const QModelIndex &idx) const
{
  if(idx.isValid() && idx.model() == this)
    {
    pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
        idx.internalPointer());

    pqPipelineModelDataItem* _parent = item->Parent;
    return this->getIndex(_parent);
    }

  return QModelIndex();
}

//-----------------------------------------------------------------------------
QVariant pqPipelineModel::data(const QModelIndex &idx, int role) const
{
  if (!idx.isValid() || idx.model() != this)
    {
    return QVariant();
    }

  pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
    idx.internalPointer());

  pqServer *server = qobject_cast<pqServer*>(item->Object);
  pqPipelineSource *source = qobject_cast<pqPipelineSource *>(item->Object);
  pqOutputPort* port = qobject_cast<pqOutputPort*>(item->Object);
  switch (role)
    {
  case Qt::ToolTipRole:
    if (source && source->getProxy()->HasAnnotation("tooltip"))
      {
      return QVariant(source->getProxy()->GetAnnotation("tooltip"));
      }
  case Qt::DisplayRole:
    if (idx.column() == 1)
      {
      return QIcon(this->PixmapList[item->VisibilityIcon]);
      }
    // *** don't break.
  case Qt::EditRole:
    if (idx.column() == 0)
      {
      if(server)
        {
        return QVariant(server->getResource().toURI());
        }
      else if(source)
        {
        return QVariant(source->getSMName());
        }
      else if (port)
        {
        return port->getPortName();
        }
      else
        {
        qDebug() << "Cannot decide type.";
        }
      }
    break;

  case Qt::DecorationRole:
    if (idx.column() == 0 && this->PixmapList)
      {
      if (item && item->getType() != pqPipelineModel::Invalid)
        {
        return QVariant(this->PixmapList[item->getIconType()]);
        }
      }
    break;

  case Qt::FontRole:
      {
      if (idx.column() == 0 && item->isModified())
        {
        return qVariantFromValue<QFont>(this->Internal->ModifiedFont);
        }
      break;
      }
  case pqPipelineModel::AnnotationFilterRole:
      {
      if(!this->FilterRoleAnnotationKey.isEmpty() && source)
        {
        return QVariant(
            source->getProxy()->HasAnnotation(
                this->FilterRoleAnnotationKey.toLatin1().data()));
        }
      return QVariant(true);
      }
  case pqPipelineModel::SessionFilterRole:
      {
      if(this->FilterRoleSession && server)
        {
        // We just want to make sure we are pointing to the same session
        return ((void*)server->session() == (void*)this->FilterRoleSession);
        }
      return QVariant(true);
      }

    }

  return QVariant();
}

//-----------------------------------------------------------------------------
bool pqPipelineModel::setData(const QModelIndex &idx, const QVariant &value,
    int)
{
  if(value.toString().isEmpty())
    {
    return false;
    }

  QString name = value.toString();
  pqPipelineSource *source = qobject_cast<pqPipelineSource *>(
      this->getItemFor(idx));
  if (source && source->getSMName() != name)
    {
    BEGIN_UNDO_SET(
      QString("Rename %1 to %2").arg(source->getSMName()).arg(name));
    source->rename(name);
    END_UNDO_SET();
    }

  return true;
}

//-----------------------------------------------------------------------------
Qt::ItemFlags pqPipelineModel::flags(const QModelIndex &idx) const
{
  Qt::ItemFlags _flags = Qt::ItemIsEnabled;

  if (idx.column() == 0)
    {
    pqPipelineModelDataItem *item = reinterpret_cast<pqPipelineModelDataItem*>(
      idx.internalPointer());
    if (item->Selectable)
      {
      _flags |= Qt::ItemIsSelectable;
      }

    if (this->Editable &&
      item->Type == pqPipelineModel::Proxy)
      {
      _flags |= Qt::ItemIsEditable;
      }
    }

  return _flags;
}

//-----------------------------------------------------------------------------
pqServerManagerModelItem* pqPipelineModel::getItemFor(const QModelIndex& idx) const
{
  if (idx.isValid() && idx.model() == this)
    {
    pqPipelineModelDataItem* item =reinterpret_cast<pqPipelineModelDataItem*>(
      idx.internalPointer());
    return item->Object;
    }
  return NULL;
}

//-----------------------------------------------------------------------------
QModelIndex pqPipelineModel::getIndexFor(pqServerManagerModelItem *item) const
{
  pqPipelineModelDataItem* dataItem = this->getDataItem(item,
    &this->Internal->Root);
  if (!dataItem)
    {
    pqOutputPort* port = qobject_cast<pqOutputPort*>(item);
    if (port && port->getSource()->getNumberOfOutputPorts() == 1)
      {
      return this->getIndexFor(port->getSource());
      }
    }
  return this->getIndex(dataItem);
}

//-----------------------------------------------------------------------------
pqPipelineModelDataItem* pqPipelineModel::getDataItem(pqServerManagerModelItem* item,
  pqPipelineModelDataItem* _parent = 0,
  pqPipelineModel::ItemType type /*= pqPipelineModel::Invalid*/
  ) const
{
  if (_parent == 0)
    {
    _parent = &this->Internal->Root;
    }

  if (!item)
    {
    return 0;
    }

  if (_parent->Object == item &&
    (type == pqPipelineModel::Invalid ||
     type == _parent->Type))
    {
    return _parent;
    }

  foreach(pqPipelineModelDataItem* child, _parent->Children)
    {
    pqPipelineModelDataItem* retVal = this->getDataItem(item, child, type);
    if (retVal &&
      (type == pqPipelineModel::Invalid ||
       type == retVal->Type))
      {
      return retVal;
      }
    }
  return 0;
}

//-----------------------------------------------------------------------------
QModelIndex pqPipelineModel::getIndex(pqPipelineModelDataItem* dataItem) const
{
  if (dataItem && dataItem->Parent)
    {
    int rowNo = dataItem->getIndexInParent();
    if (rowNo != -1)
      {
      return this->createIndex(rowNo, 0, dataItem);
      }
    }

  // QModelIndex() implies the ROOT.
  return QModelIndex();
}


//-----------------------------------------------------------------------------
void pqPipelineModel::addChild(pqPipelineModelDataItem* _parent,
  pqPipelineModelDataItem* child)
{
  if (!_parent || !child)
    {
    qDebug() << "addChild cannot have null arguments.";
    return;
    }

  QModelIndex parentIndex = this->getIndex(_parent);
  int row = _parent->Children.size();

  this->beginInsertRows(parentIndex, row, row);
  _parent->addChild(child);
  this->endInsertRows();

  if(row == 0)
    {
    emit this->firstChildAdded(parentIndex);
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::removeChildFromParent(
  pqPipelineModelDataItem* child)
{
  if (!child)
    {
    qDebug() << "removeChild cannot have null arguments.";
    return;
    }

  pqPipelineModelDataItem* _parent = child->Parent;
  if (!_parent)
    {
    qDebug() << "cannot remove ROOT.";
    return;
    }

  QModelIndex parentIndex = this->getIndex(_parent);
  int row = child->getIndexInParent();

  this->beginRemoveRows(parentIndex, row, row);
  _parent->removeChild(child);
  this->endRemoveRows();
}



//-----------------------------------------------------------------------------
void pqPipelineModel::serverDataChanged()
{
  // TODO: we should determine which server data actually chnaged
  // and invalidate only that one. FOr now, just invalidate all.

  int max = this->Internal->Root.Children.size()-1;
  if (max >= 0)
    {
    QModelIndex minIndex = this->getIndex(this->Internal->Root.Children[0]);
    QModelIndex maxIndex = this->getIndex(this->Internal->Root.Children[max]);
    emit this->dataChanged(minIndex, maxIndex);
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::itemDataChanged(pqPipelineModelDataItem* item)
{
  QModelIndex idx = this->getIndex(item);
  emit this->dataChanged(idx, idx);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::addServer(pqServer *server)
{
  if(!server)
    {
    return;
    }

  pqPipelineModelDataItem* item = new pqPipelineModelDataItem(
    this, server, pqPipelineModel::Server, this);
  this->addChild(&this->Internal->Root, item);
  QObject::connect(server,
    SIGNAL(nameChanged(pqServerManagerModelItem*)),
    this, SLOT(updateData(pqServerManagerModelItem*)));
}

//-----------------------------------------------------------------------------
void pqPipelineModel::removeServer(pqServer *server)
{
  pqPipelineModelDataItem* item = this->getDataItem(server,
    &this->Internal->Root, pqPipelineModel::Server);

  if (!item)
    {
    qDebug() << "Requesting to remove a non-added server.";
    return;
    }

  this->removeChildFromParent(item);

  delete item;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::addSource(pqPipelineSource* source)
{
  pqServer* server = source->getServer();
  pqPipelineModelDataItem* _parent = this->getDataItem(server,
    &this->Internal->Root, pqPipelineModel::Server);

  if (!_parent)
    {
    qDebug() << "Could not locate server on which the source is being added.";
    return;
    }

  pqPipelineModelDataItem* item = new pqPipelineModelDataItem(
    this, source, pqPipelineModel::Proxy, this);
  item->Object = source;
  item->Type = pqPipelineModel::Proxy; // source->getType();

  // Add the 'source' to the server.
  this->addChild(_parent, item);

  int numOutputPorts = source->getNumberOfOutputPorts();
  if (numOutputPorts > 1)
    {
    // add output-ports for this source.
    for (int cc=0; cc < numOutputPorts; cc++)
      {
      pqPipelineModelDataItem* opport = new pqPipelineModelDataItem(
        this, source->getOutputPort(cc), pqPipelineModel::Port, this);
      this->addChild(item, opport);
      }
    }

  QObject::connect(source,
    SIGNAL(visibilityChanged(pqPipelineSource*, pqDataRepresentation*)),
    this, SLOT(delayedUpdateVisibility(pqPipelineSource*)));

  QObject::connect(source,
    SIGNAL(nameChanged(pqServerManagerModelItem*)),
    this, SLOT(updateData(pqServerManagerModelItem*)));
  QObject::connect(source,
    SIGNAL(modifiedStateChanged(pqServerManagerModelItem*)),
    this, SLOT(updateData(pqServerManagerModelItem*)));
}

//-----------------------------------------------------------------------------
void pqPipelineModel::removeSource(pqPipelineSource* source)
{
  pqPipelineModelDataItem* item = this->getDataItem(source,
    &this->Internal->Root, pqPipelineModel::Proxy);

  if (!item)
    {
    // qDebug() << "Requesting to remove a non-added source.";
    return;
    }

  while (item->Links.size() > 0)
    {
    pqPipelineModelDataItem* link = item->Links.last();
    this->removeChildFromParent(link);
    delete link;
    }

  this->removeChildFromParent(item);
  if (item->Children.size())
    {
    // Move the children to the server.
    pqServer* server = source->getServer();
    pqPipelineModelDataItem* _parent = this->getDataItem(server,
      &this->Internal->Root, pqPipelineModel::Server);
    if (!_parent)
      {
      _parent = &this->Internal->Root;
      }

    QList<pqPipelineModelDataItem*> childrenToMove;
    foreach(pqPipelineModelDataItem* child, item->Children)
      {
      if (child->Type == pqPipelineModel::Port)
        {
        childrenToMove.append(child->Children);
        }
      else
        {
        childrenToMove.push_back(child);
        }
      }

    foreach (pqPipelineModelDataItem* child, childrenToMove)
      {
      child->Parent = NULL;
      this->addChild(_parent, child);
      }
    }

  delete item;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::addConnection(pqPipelineSource *source,
    pqPipelineSource *sink, int sourceOutputPort)
{
  if(!source || !sink)
    {
    qDebug() << "Cannot connect a null source or sink.";
    return;
    }

  // Note: this slot is invoked after the connection has been set.

  // If fanIn == 1, take the sink form the server list
  // and put it under the source.
  // If fanIn == 2, take the sink from  the under the source that it was put
  //  when fanIn == 1, replace that with a link. Add a new link for the new
  //  connection and put the source under the server list.
  // If fanIn > 2, just create a link for this new connection. We have already
  //  flagged it as a link when fanIn == 2.

  pqPipelineFilter* filter = qobject_cast<pqPipelineFilter*>(sink);
  if (!filter)
    {
    qDebug() << "Sink has to be a filter.";
    return;
    }

  pqPipelineModelDataItem* srcItem = this->getDataItem(source,
    &this->Internal->Root, pqPipelineModel::Proxy);
  pqPipelineModelDataItem* sinkItem = this->getDataItem(sink,
    &this->Internal->Root, pqPipelineModel::Proxy);
  if (!srcItem || !sinkItem)
    {
    qDebug() << "Connection involves a non-added source. Ignoring.";
    return;
    }

  if (source->getNumberOfOutputPorts() > 1)
    {
    srcItem = srcItem->Children[sourceOutputPort];
    }

  // NOTE-TO-SELF: Never use actual values of current connections from the
  // sources/filters -- think of change input dialog to know why.

  pqPipelineModelDataItem* currentParent = sinkItem->Parent;
  if (currentParent->Type == pqPipelineModel::Server &&
    sinkItem->Links.size() > 0)
    {
    // sink has previously been identified as a "fan-in". We simply, create a
    // new link object for it.
    pqPipelineModelDataItem* link =
      new pqPipelineModelDataItem(this, sink, pqPipelineModel::Link, this);
    this->addChild(srcItem, link);
    return;
    }

  if (currentParent->Type == pqPipelineModel::Proxy ||
    currentParent->Type == pqPipelineModel::Port)
    {

    // this filter had just 1 input previously, now it has 2. So we need to
    // upgrade it to a fan-in.
    pqPipelineModelDataItem* linkOld = new pqPipelineModelDataItem(
      this, sink, pqPipelineModel::Link, this);
    this->addChild(currentParent, linkOld);

    pqPipelineModelDataItem* link =
      new pqPipelineModelDataItem(this, sink, pqPipelineModel::Link, this);
    this->addChild(srcItem, link);

    pqServer* server = sink->getServer();
    pqPipelineModelDataItem* serverItem = this->getDataItem(server,
      &this->Internal->Root, pqPipelineModel::Server);

    this->removeChildFromParent(sinkItem);
    this->addChild(serverItem, sinkItem);
    return;
    }

  // Adding the first input connection for this sink.
  // Remove the sink from where ever.
  this->removeChildFromParent(sinkItem);

  // Add to the children of the source.
  this->addChild(srcItem, sinkItem);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::removeConnection(pqPipelineSource *source,
    pqPipelineSource *sink, int sourceOutputPort)
{
  if(!source || !sink)
    {
    qDebug() << "Cannot disconnect a null source or sink.";
    return;
    }

  pqPipelineModelDataItem* sinkItem = this->getDataItem(sink,
    &this->Internal->Root, pqPipelineModel::Proxy);
  pqPipelineModelDataItem* srcItem = this->getDataItem(source,
    &this->Internal->Root, pqPipelineModel::Proxy);

  if (!sinkItem || !srcItem)
    {
    // qDebug() << "Connection involves a non-added source. Ignoring.";
    return;
    }

  // Note: this slot is invoked after the connection has been broken.

  if (sinkItem->Links.size() == 0)
    {
    // Simplest case, sink had just 1 input.
    pqServer* server = sink->getServer();
    pqPipelineModelDataItem* serverItem = this->getDataItem(server,
      &this->Internal->Root, pqPipelineModel::Server);
    if (!serverItem)
      {
      qDebug() << "Failed to locate data item for server.";
      return;
      }

    this->removeChildFromParent(sinkItem);
    this->addChild(serverItem, sinkItem);
    return;
    }


  if (source->getNumberOfOutputPorts() > 1)
    {
    srcItem = srcItem->Children[sourceOutputPort];
    }

  // Has a fan-in for sure.
  // Remove the link item under the source.
  pqPipelineModelDataItem* linkItem = this->getDataItem(sink,
    srcItem, pqPipelineModel::Link);
  Q_ASSERT(linkItem != 0);
  this->removeChildFromParent(linkItem);
  delete linkItem;

  // locate all links for sink. If number of links == 1, remove the link and
  // move the sink to where the link was.
  if (sinkItem->Links.size() == 1)
    {
    linkItem = sinkItem->Links[0];
    pqPipelineModelDataItem* parentItem = linkItem->Parent;
    this->removeChildFromParent(linkItem);
    delete linkItem;

    this->removeChildFromParent(sinkItem);

    this->addChild(parentItem, sinkItem);
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::setView(pqView *newview)
{
  if (this->View == newview)
    {
    return;
    }
  this->View = newview;
  // update all VisibilityIcons.
  this->Internal->Root.updateVisibilityIcon(newview, true);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::delayedUpdateVisibility(pqPipelineSource* source)
{
  this->Internal->DelayedUpdateVisibilityItems.removeAll(source);
  this->Internal->DelayedUpdateVisibilityItems.push_front(source);
  this->Internal->DelayedUpdateVisibilityTimer.start(0);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::delayedUpdateVisibilityTimeout()
{
  foreach (pqPipelineSource* source,
    this->Internal->DelayedUpdateVisibilityItems)
    {
    if (source)
      {
      this->updateVisibility(source);
      }
    }
  this->Internal->DelayedUpdateVisibilityItems.clear();
}

//-----------------------------------------------------------------------------
void pqPipelineModel::updateVisibility(pqPipelineSource* source,
                                       ItemType type /* = Proxy */)
{
  pqPipelineModelDataItem* item = this->getDataItem(source,
    &this->Internal->Root, type);
  if (item)
    {
    item->updateVisibilityIcon(this->View, false);
    foreach (pqPipelineModelDataItem* child, item->Children)
      {
      if (child->Type == Port)
        {
        child->updateVisibilityIcon(this->View, false);
        }
      }

    foreach (pqPipelineModelDataItem* link, item->Links)
      {
      link->updateVisibilityIcon(this->View, false);
      }
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::updateData(pqServerManagerModelItem* source,
                                 ItemType type /* = Proxy */)
{
  pqPipelineModelDataItem* item = this->getDataItem(
    source, &this->Internal->Root, type);
  if (item)
    {
    item->updateVisibilityIcon(this->View, false);
    this->itemDataChanged(item);
    foreach (pqPipelineModelDataItem* link, item->Links)
      {
      item->updateVisibilityIcon(this->View, false);
      this->itemDataChanged(link);
      }
    }
  else // source is null, so update everything
    {
    foreach(pqPipelineModelDataItem* child, this->Internal->Root.Children)
      {
      this->itemDataChanged(child);
      }
    }
}

//-----------------------------------------------------------------------------
void pqPipelineModel::updateDataServer(pqServer* server)
{
  updateData(server, Invalid);
}

//-----------------------------------------------------------------------------
void pqPipelineModel::setModifiedFont(const QFont& font)
{
  this->Internal->ModifiedFont = font;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::enableFilterAnnotationKey(const QString &expectedAnnotation)
{
  this->FilterRoleAnnotationKey = expectedAnnotation;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::disableFilterAnnotationKey()
{
  this->FilterRoleAnnotationKey.clear();
}

//-----------------------------------------------------------------------------
void pqPipelineModel::enableFilterSession(vtkSession* session)
{
this->FilterRoleSession = session;
}

//-----------------------------------------------------------------------------
void pqPipelineModel::disableFilterSession()
{
this->FilterRoleSession = NULL;
}
