//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

#include "pqCMBModifierArcManager.h"

#include "pqCMBLIDARPieceObject.h"

#include "ui_qtArcEditWidget.h"

#include "pqApplicationCore.h"
#include "pqCMBArc.h"
#include "pqObjectBuilder.h"

#include <QDebug>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QGridLayout>
#include <QHeaderView>
#include <QPointer>
#include <QTableWidget>
#include <QTableWidgetItem>

#include <fstream>
#include <sstream>

#include "pqCMBModifierArc.h"
#include "pqDataRepresentation.h"
#include "pqPipelineSource.h"
#include "pqRenderView.h"
#include "pqSMAdaptor.h"
#include "smtk/extension/paraview/widgets/qtArcWidget.h"
#include "ui_qtArcFunctionControl.h"
#include "ui_qtModifierArcDialog.h"
#include "vtkPVArcInfo.h"
#include "vtkSMRenderViewProxy.h"
#include "vtkSMRepresentationProxy.h"
#include <vtksys/SystemTools.hxx>

#include "smtk/extension/vtk/widgets/vtkSMTKArcRepresentation.h"
#include "vtkCommand.h"
#include "vtkContourWidget.h"
#include "vtkNew.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxyProperty.h"
#include "vtkSMRepresentationProxy.h"
#include "vtkSMSourceProxy.h"
#include "vtkSMVectorProperty.h"

#include "cmbManualProfileFunction.h"
#include "cmbProfileFunction.h"
#include "cmbProfileWedgeFunction.h"
#include "pqDataRepresentation.h"
#include "pqPipelineSource.h"
#include "qtCMBManualFunctionWidget.h"
#include "qtCMBProfileWedgeFunctionWidget.h"
#include "vtkSMNewWidgetRepresentationProxy.h"

#include "vtkCMBArcEditClientOperator.h"

#include "pqRepresentationHelperFunctions.h"

#include <vtkActor.h>
#include <vtkGlyph3D.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRendererCollection.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>

#include "vtkSMCMBGlyphPointSourceProxy.h"

namespace
{

// enum for different column types
enum DataTableCol
{
  VisibilityCol = 0,
  Id,
  Mode,
  Relative
};

enum EditMode
{
  NoEditMode = 0,
  EditFunction,
  EditArc
};

QStringList pointModesStrings = QStringList() << "Continuous"
                                              << "End Points"
                                              << "Advance";
}

class pqCMBModifierArcManagerInternal
{
public:
  QPointer<pqPipelineSource> ArcPointSelectSource;
  vtkSMNewWidgetRepresentationProxy* editableWidget;
  QPointer<pqPipelineSource> SphereSource;
  QPointer<pqDataRepresentation> Representation;
  QPointer<pqPipelineSource> LineGlyphFilter;
  Ui_qtArcFunctionControl* UI;
  Ui_qtModifierArcDialog* UI_Dialog;
  Ui_ArcEditWidget* arcEditWidget;
  EditMode mode;
  qtArcWidget* CurrentArcWidget;
  int selectedRow;
  vtkBoundingBox boundingBox;
};

pqCMBModifierArcManager::pqCMBModifierArcManager(
  QLayout* layout, pqServer* serverInput, pqRenderView* renderer)
{
  useNormal = false;
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();
  this->view = renderer;
  this->server = serverInput;
  this->Internal = new pqCMBModifierArcManagerInternal;
  //this->Internal->PointSelectionWidget = NULL;
  this->Internal->editableWidget = NULL;

  this->Internal->SphereSource = builder->createSource("sources", "SphereSource", serverInput);
  this->Internal->LineGlyphFilter =
    builder->createSource("filters", "ArcPointGlyphingFilter", serverInput);
  this->Internal->Representation = builder->createDataRepresentation(
    this->Internal->LineGlyphFilter->getOutputPort(0), this->view, "GeometryRepresentation");

  //vtkSMSourceProxy* source = this->Internal->SphereSource->getProxy();
  //pqSMAdaptor::setElementProperty(this->Internal->SphereSource->getProxy()->GetProperty("SetRadius"),1000);

  this->Internal->SphereSource->getProxy()->MarkModified(this->Internal->SphereSource->getProxy());

  vtkSMProxy* repProxy = this->Internal->Representation->getProxy();
  RepresentationHelperFunctions::CMB_COLOR_REP_BY_ARRAY(repProxy, "Color", vtkDataObject::POINT);

  pqSMAdaptor::setEnumerationProperty(repProxy->GetProperty("Representation"), "3D Glyphs");
  //vtkSMPropertyHelper(repProxy, "ImmediateModeRendering").Set(0);
  vtkSMPropertyHelper(repProxy, "GlyphType").Set(this->Internal->SphereSource->getProxy());
  vtkSMPropertyHelper(repProxy, "Scaling").Set(true);
  vtkSMPropertyHelper(repProxy, "ScaleMode").Set(2);
  vtkSMPropertyHelper(repProxy, "Orient").Set(true);
  vtkSMPropertyHelper(repProxy, "OrientationMode").Set(1);
  vtkSMPropertyHelper(repProxy, "MapScalars").Set(0);
  vtkSMPropertyHelper(repProxy, "SelectScaleArray").Set("Scaling");
  vtkSMPropertyHelper(repProxy, "SelectOrientationVectors").Set("Orientation");

  // This is a work arround for a bug in ParaView
  vtkSMPropertyHelper(repProxy, "ScaleFactor").Set(1.0);

  repProxy->UpdateVTKObjects();

  this->CurrentModifierArc = NULL;
  this->ManualFunctionWidget = NULL;
  this->WedgeFunctionWidget = NULL;
  this->selectedFunction = NULL;
  this->Internal->UI = new Ui_qtArcFunctionControl();
  QWidget* w = new QWidget();
  this->Internal->UI->setupUi(w);
  if (layout != NULL)
  {
    layout->addWidget(w);
    this->Dialog = NULL;
    this->Internal->UI_Dialog = NULL;
  }
  else
  {
    this->Dialog = new QDialog();
    this->Internal->UI_Dialog = new Ui_qtModifierArcDialog();
    this->Internal->UI_Dialog->setupUi(this->Dialog);
    this->Internal->UI_Dialog->modifierLayout->addWidget(w);
    QPushButton* applyButton =
      this->Internal->UI_Dialog->buttonBox->button(QDialogButtonBox::Apply);
    connect(applyButton, SIGNAL(clicked()), this, SLOT(accepted()));
    //connect(this->Dialog, SIGNAL(rejected()), this, SLOT(clear()));
  }
  functionLayout = new QGridLayout(this->Internal->UI->functionControlArea);
  functionLayout->setMargin(0);

  this->TableWidget = this->Internal->UI->ModifyArcTable;
  this->clear();
  this->initialize();
  QObject::connect(this->Internal->UI->addLineButton, SIGNAL(clicked()), this, SLOT(addLine()),
    Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->removeLineButton, SIGNAL(clicked()), this, SLOT(removeArc()),
    Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->buttonUpdateLine, SIGNAL(clicked()), this, SLOT(update()),
    Qt::UniqueConnection);

  QObject::connect(
    this->Internal->UI->EditArc, SIGNAL(clicked()), this, SLOT(editArc()), Qt::UniqueConnection);

  QObject::connect(this->Internal->UI->DeleteFunction, SIGNAL(clicked()), this,
    SLOT(deleteFunction()), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->CloneFunction, SIGNAL(clicked()), this,
    SLOT(cloneFunction()), Qt::UniqueConnection);

  QObject::connect(this->Internal->UI->FunctionName, SIGNAL(currentIndexChanged(int)), this,
    SLOT(onFunctionSelectionChange()), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->FunctionType, SIGNAL(currentIndexChanged(int)), this,
    SLOT(functionTypeChanged(int)), Qt::UniqueConnection);

  QObject::connect(
    this->Internal->UI->Save, SIGNAL(clicked()), this, SLOT(onSaveArc()), Qt::UniqueConnection);
  QObject::connect(
    this->Internal->UI->Load, SIGNAL(clicked()), this, SLOT(onLoadArc()), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->importFunctions, SIGNAL(clicked()), this,
    SLOT(importFunction()), Qt::UniqueConnection);

  QObject::connect(this->Internal->UI->FunctionName, SIGNAL(editTextChanged(QString const&)), this,
    SLOT(nameChanged(QString)), Qt::UniqueConnection);

  this->ArcWidgetManager = new qtCMBArcWidgetManager(serverInput, renderer);
  QObject::connect(this->ArcWidgetManager, SIGNAL(ArcSplit2(pqCMBArc*, QList<vtkIdType>)), this,
    SLOT(doneModifyingArc()), Qt::UniqueConnection);
  QObject::connect(this->ArcWidgetManager, SIGNAL(ArcModified2(pqCMBArc*)), this,
    SLOT(doneModifyingArc()), Qt::UniqueConnection);
  QObject::connect(
    this->ArcWidgetManager, SIGNAL(Finish()), this, SLOT(doneModifyingArc()), Qt::UniqueConnection);
  QObject::connect(this->ArcWidgetManager, SIGNAL(selectedId(vtkIdType)), this,
    SLOT(addPoint(vtkIdType)), Qt::UniqueConnection);
  QObject::connect(
    this, SIGNAL(selectionChanged(int)), this, SLOT(selectLine(int)), Qt::UniqueConnection);
  QObject::connect(this, SIGNAL(orderChanged()), this, SLOT(sendOrder()), Qt::UniqueConnection);

  QObject::connect(this->Internal->UI->ComputeChange, SIGNAL(clicked()), this,
    SLOT(computeChange()), Qt::UniqueConnection);
  this->check_save();
  this->addPointMode = false;

  this->Internal->arcEditWidget = new Ui_ArcEditWidget();
  this->Internal->arcEditWidget->setupUi(this->Internal->UI->EditControl);
  this->Internal->mode = NoEditMode;
  this->updateUiControls();
  this->Internal->CurrentArcWidget = NULL;
  this->Internal->selectedRow = -1;

  this->Internal->UI->amountAdded->setText("NA");
  this->Internal->UI->amountRemoved->setText("NA");
  this->Internal->UI->PointBathChangeControl->hide();
}

pqCMBModifierArcManager::~pqCMBModifierArcManager()
{
  if (this->Internal->ArcPointSelectSource)
  {
    pqApplicationCore::instance()->getObjectBuilder()->destroy(
      this->Internal->ArcPointSelectSource);
    this->Internal->ArcPointSelectSource = NULL;
  }
  delete this->Internal->arcEditWidget;
  delete this->Internal->UI;
  delete this->Internal;
  this->clear();
  delete this->Dialog;
  delete this->ArcWidgetManager;
  //delete this->Internal->PointSelectionWidget;
  //TODO clean up editableWidget
}

void pqCMBModifierArcManager::initialize()
{
  this->TableWidget->setColumnCount(4);
  this->TableWidget->setHorizontalHeaderLabels(
    QStringList() << tr("Apply") << tr("ID") << tr("       Mode       ") << tr("Relative"));

  this->TableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
  this->TableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
  this->TableWidget->verticalHeader()->hide();

  QObject::connect(this->TableWidget, SIGNAL(itemSelectionChanged()), this,
    SLOT(onCurrentObjectChanged()), Qt::QueuedConnection);
  QObject::connect(this->TableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this,
    SLOT(onItemChanged(QTableWidgetItem*)), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->DatasetControl, SIGNAL(clicked(bool)),
    this->Internal->UI->DatasetTable, SLOT(setVisible(bool)), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->DatasetTable, SIGNAL(itemChanged(QTableWidgetItem*)), this,
    SLOT(onDatasetChange(QTableWidgetItem*)), Qt::UniqueConnection);
  QObject::connect(this->TableWidget, SIGNAL(itemSelectionChanged()), this,
    SLOT(onSelectionChange()), Qt::UniqueConnection);

  this->Internal->UI->DatasetControl->setChecked(false);
  this->Internal->UI->DatasetTable->hide();

  this->Internal->UI->points->setColumnCount(2);
  this->Internal->UI->points->setHorizontalHeaderLabels(
    QStringList() << tr("Point") << tr("Function"));
  this->Internal->UI->points->setSelectionMode(QAbstractItemView::SingleSelection);
  this->Internal->UI->points->setSelectionBehavior(QAbstractItemView::SelectRows);
  this->Internal->UI->points->verticalHeader()->hide();

  QObject::connect(this->Internal->UI->points, SIGNAL(itemSelectionChanged()), this,
    SLOT(onPointsSelectionChange()), Qt::UniqueConnection);
  QObject::connect(
    this->Internal->UI->AddPoint, SIGNAL(clicked()), this, SLOT(addPoint()), Qt::UniqueConnection);
  QObject::connect(this->Internal->UI->RemovePoint, SIGNAL(clicked()), this, SLOT(deletePoint()),
    Qt::UniqueConnection);
}

void pqCMBModifierArcManager::accepted()
{
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      for (unsigned int i = 0; i < this->ArcLines.size(); ++i)
      {
        if (this->ArcLines[i] != NULL)
        {
          this->ArcLines[i]->updateArc(source, this->Internal->boundingBox);
        }
      }
    }
  }
  this->sendOrder();
  //this->clear();
  if (this->Dialog != NULL)
    this->Dialog->hide();
  emit(applyFunctions());
}

void pqCMBModifierArcManager::showDialog()
{
  if (this->Dialog != NULL)
  {
    this->Dialog->show();
  }
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      for (unsigned int i = 0; i < this->ArcLines.size(); ++i)
      {
        if (this->ArcLines[i] != NULL)
        {
          QList<QVariant> v;
          v << this->ArcLines[i]->getId();
          pqSMAdaptor::setMultipleElementProperty(source->GetProperty("AddArc"), v);
          v.clear();
          v << this->ArcLines[i]->getId() << 1;
          pqSMAdaptor::setMultipleElementProperty(source->GetProperty("ArcEnable"), v);
          source->UpdateVTKObjects();
        }
      }
    }
  }
  addApplyControl();
}

void pqCMBModifierArcManager::clear()
{
  selectLine(-1);
  this->CurrentModifierArc = NULL;
  this->Internal->mode = NoEditMode;
  this->selectFunction(NULL);
  this->selectedFunction = NULL;
  this->TableWidget->clearContents();
  this->TableWidget->setRowCount(0);
  this->Internal->UI->points->clearContents();
  this->Internal->UI->points->setRowCount(0);
  for (unsigned int i = 0; i < ArcLines.size(); ++i)
  {
    delete ArcLines[i];
  }

  ArcLines.clear();
  ServerProxies.clear();
  ServerRows.clear();
  ArcLinesApply.clear();
  this->Internal->boundingBox.Reset();
  this->updateUiControls();
}

void pqCMBModifierArcManager::enableSelection()
{
  this->TableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
}

void pqCMBModifierArcManager::disableSelection()
{

  this->TableWidget->setSelectionMode(QAbstractItemView::NoSelection);
}

void pqCMBModifierArcManager::onClearSelection()
{
  this->TableWidget->blockSignals(true);
  this->TableWidget->clearSelection();
  this->onSelectionChange();
  this->updateUiControls();
  //TODO clear the selected values form system
  this->TableWidget->blockSignals(false);
}

void pqCMBModifierArcManager::AddLinePiece(pqCMBModifierArc* dataObj, int visible)
{
  this->TableWidget->insertRow(this->TableWidget->rowCount());
  int row = this->TableWidget->rowCount() - 1;

  Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);

  setRow(row, dataObj);

  QTableWidgetItem* objItem = new QTableWidgetItem();
  QVariant vdata;
  vdata.setValue(static_cast<void*>(dataObj));
  objItem->setData(Qt::UserRole, vdata);

  this->TableWidget->setItem(row, VisibilityCol, objItem);
  objItem->setFlags(commFlags | Qt::ItemIsUserCheckable);
  objItem->setCheckState(visible ? Qt::Checked : Qt::Unchecked);

  this->TableWidget->resizeColumnsToContents();
  connect(
    dataObj, SIGNAL(functionChanged(int)), this, SLOT(onLineChange(int)), Qt::UniqueConnection);
  //unselectAllRows();
  this->TableWidget->selectRow(row);
  if (visible)
    emit orderChanged();
}

void pqCMBModifierArcManager::AddFunction(cmbProfileFunction* fun)
{
  QVariant vdata;
  vdata.setValue(static_cast<void*>(fun));
  this->Internal->UI->FunctionName->addItem(fun->getName().c_str(), vdata);
  this->Internal->UI->DeleteFunction->setEnabled(this->Internal->UI->FunctionName->count() > 1);
}

void pqCMBModifierArcManager::unselectAllRows()
{
  this->TableWidget->setRangeSelected(
    QTableWidgetSelectionRange(0, 0, this->TableWidget->rowCount() - 1, Id), false);
}

void pqCMBModifierArcManager::setRow(int row, pqCMBModifierArc* dataObj)
{
  Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);

  QVariant vdata;
  vdata.setValue(static_cast<void*>(dataObj));

  QString s = QString::number(dataObj->getId());
  QTableWidgetItem* v = new QTableWidgetItem(s);
  this->TableWidget->setItem(row, Id, v);
  v->setFlags(commFlags);

  QTableWidgetItem* objItem = new QTableWidgetItem();
  objItem->setFlags(commFlags | ((useNormal) ? Qt::NoItemFlags : Qt::ItemIsUserCheckable));
  objItem->setCheckState(dataObj->isRelative() ? Qt::Checked : Qt::Unchecked);
  this->TableWidget->setItem(row, Relative, objItem);
}

void pqCMBModifierArcManager::onCurrentObjectChanged()
{
}

void pqCMBModifierArcManager::onLineChange(int id)
{
  pqCMBModifierArc* line = NULL;
  for (unsigned int i = 0; i < ArcLines.size(); ++i)
  {
    if (ArcLines[i] != NULL && ArcLines[i]->getId() == id)
    {
      line = ArcLines[i];
    }
  }
  if (line == NULL)
    return;
  for (int i = 0; i < this->TableWidget->rowCount(); ++i)
  {
    QTableWidgetItem* tmp = this->TableWidget->item(i, Id);
    if (tmp->text().toInt() == id)
    {
      this->setRow(i, line);
    }
  }
}

bool pqCMBModifierArcManager::remove(int id, bool all)
{
  this->TableWidget->blockSignals(true);

  QList<QTableWidgetSelectionRange> selected = this->TableWidget->selectedRanges();
  int row = selected.front().topRow();
  int sid = this->TableWidget->item(row, Id)->text().toInt();

  if (id == -1) //Just remove the selected one
  {
    id = sid;
  }
  if (id == sid)
  {
    this->TableWidget->removeRow(row);
  }
  if (all)
  {
    int r = this->TableWidget->rowCount() - 1;
    for (; r >= 0; --r)
    {
      QTableWidgetItem* tmp = this->TableWidget->item(r, Id);
      if (tmp->text().toInt() == id)
      {
        this->TableWidget->removeRow(r);
      }
    }
  }
  else
  {
    for (int i = 0; i < this->TableWidget->rowCount(); ++i)
    {
      QTableWidgetItem* tmp = this->TableWidget->item(i, Id);
      if (id == tmp->text().toInt())
      {
        this->TableWidget->blockSignals(false);
        emit(orderChanged());
        onClearSelection();
        return true;
      }
    }
  }
  ArcLines[id] = NULL;
  this->TableWidget->blockSignals(false);
  emit(orderChanged());
  onClearSelection();
  this->check_save();
  return false;
}

void pqCMBModifierArcManager::onItemChanged(QTableWidgetItem* item)
{
  int id = this->TableWidget->item(item->row(), Id)->text().toInt();
  pqCMBModifierArc* dl = ArcLines[id];
  if (dl == NULL)
    return;
  if (item->column() == VisibilityCol)
  {
    emit(orderChanged());
  }
  if (item->column() == Relative)
  {
    bool relative = item->checkState() == Qt::Checked;
    dl->setRelative(relative);
    if (WedgeFunctionWidget)
    {
      WedgeFunctionWidget->setRelative(relative);
    }
  }
}

void pqCMBModifierArcManager::onDatasetChange(QTableWidgetItem* item)
{
  if (this->CurrentModifierArc == NULL)
    return;
  int id = this->CurrentModifierArc->getId();
  if (item->column() == 0)
  {
    QString fname = this->Internal->UI->DatasetTable->item(item->row(), 1)->text();
    int pieceIdx = this->Internal->UI->DatasetTable->item(item->row(), 2)->text().toInt();
    bool visible = item->checkState() == Qt::Checked;
    ArcLinesApply[id][fname][pieceIdx] = visible;
    emit orderChanged();
  }
}

void pqCMBModifierArcManager::applyAgain()
{
  Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
  this->TableWidget->blockSignals(true);
  QList<QTableWidgetSelectionRange> selected = this->TableWidget->selectedRanges();
  if (!selected.empty())
  {
    int row = selected.front().topRow();
    int id = this->TableWidget->item(row, Id)->text().toInt();
    bool visible = this->TableWidget->item(row, VisibilityCol)->checkState() == Qt::Checked;
    pqCMBModifierArc* dl = ArcLines[id];
    this->TableWidget->insertRow(this->TableWidget->rowCount());
    row = this->TableWidget->rowCount() - 1;

    setRow(row, dl);

    QTableWidgetItem* objItem = new QTableWidgetItem();
    QVariant vdata;
    vdata.setValue(static_cast<void*>(dl));
    objItem->setData(Qt::UserRole, vdata);

    this->TableWidget->setItem(row, VisibilityCol, objItem);
    objItem->setFlags(commFlags | Qt::ItemIsUserCheckable);
    objItem->setCheckState(visible ? Qt::Checked : Qt::Unchecked);

    this->TableWidget->resizeColumnsToContents();
    if (visible)
      emit orderChanged();
  }
  this->TableWidget->blockSignals(false);
}

void pqCMBModifierArcManager::onSelectionChange()
{
  this->TableWidget->blockSignals(true);
  if (this->Internal->selectedRow != -1)
  {
    QTableWidgetItem* tmp = this->TableWidget->item(this->Internal->selectedRow, Id);
    if (tmp != NULL)
    {
      int id = tmp->text().toInt();
      pqCMBModifierArc* ma = ArcLines[id];
      tmp = this->TableWidget->item(this->Internal->selectedRow, Relative);
      tmp->setFlags((tmp->flags() | Qt::ItemIsUserCheckable) ^ Qt::ItemIsUserCheckable);

      QTableWidgetItem* qtwi = new QTableWidgetItem(pointModesStrings[ma->getFunctionMode()]);
      Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
      this->TableWidget->setCellWidget(this->Internal->selectedRow, Mode, NULL);
      this->TableWidget->setItem(this->Internal->selectedRow, Mode, qtwi);
      qtwi->setFlags(commFlags);
    }
    this->Internal->selectedRow = -1;
  }
  QList<QTableWidgetSelectionRange> selected = this->TableWidget->selectedRanges();
  if (!selected.empty())
  {
    int row = selected.front().topRow();
    this->Internal->selectedRow = row;
    int id = this->TableWidget->item(row, Id)->text().toInt();
    pqCMBModifierArc* ma = ArcLines[id];

    QTableWidgetItem* tmp = this->TableWidget->item(this->Internal->selectedRow, Relative);
    tmp->setFlags(tmp->flags() | ((useNormal) ? Qt::NoItemFlags : Qt::ItemIsUserCheckable));

    switch (this->Internal->mode)
    {
      case EditArc:
      {
        QTableWidgetItem* qtwi = new QTableWidgetItem(pointModesStrings[ma->getFunctionMode()]);
        Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
        this->TableWidget->setCellWidget(this->Internal->selectedRow, Mode, NULL);
        this->TableWidget->setItem(this->Internal->selectedRow, Mode, qtwi);
        tmp->setFlags((tmp->flags() | Qt::ItemIsUserCheckable) ^ Qt::ItemIsUserCheckable);
        qtwi->setFlags(commFlags);
        break;
      }
      case NoEditMode:
        this->Internal->mode = EditFunction;
      case EditFunction:
      {
        QComboBox* combo = new QComboBox();
        combo->addItems(pointModesStrings);
        combo->setCurrentIndex(ma->getFunctionMode());
        this->TableWidget->setItem(this->Internal->selectedRow, Mode, NULL);
        this->TableWidget->setCellWidget(this->Internal->selectedRow, Mode, combo);
        QObject::connect(combo, SIGNAL(currentIndexChanged(int)), this,
          SLOT(functionModeChanged(int)), Qt::UniqueConnection);
        break;
      }
    }
    emit selectionChanged(id);
  }
  else
  {
    this->Internal->mode = NoEditMode;
  }
  this->TableWidget->blockSignals(false);
}

void pqCMBModifierArcManager::onFunctionSelectionChange()
{
  cmbProfileFunction* fun = NULL;
  int index = this->Internal->UI->FunctionName->currentIndex();
  if (index != -1)
  {
    QVariant d = this->Internal->UI->FunctionName->itemData(index);
    void* dv = d.value<void*>();
    fun = static_cast<cmbProfileFunction*>(dv);
  }
  selectFunction(fun);
}

void pqCMBModifierArcManager::onPointsSelectionChange()
{
  QList<QTableWidgetItem*> selected = this->Internal->UI->points->selectedItems();
  if (!selected.empty())
  {
    vtkSMSourceProxy* pointDisplaySource =
      vtkSMSourceProxy::SafeDownCast(this->Internal->LineGlyphFilter->getProxy());
    QVariant qv = selected[0]->data(Qt::UserRole);
    pqCMBModifierArc::pointFunctionWrapper* wrapper =
      static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
    this->Internal->UI->FunctionName->setCurrentIndex(
      this->Internal->UI->FunctionName->findText(wrapper->getName().c_str()));
    selectFunction(const_cast<cmbProfileFunction*>(wrapper->getFunction()));
    {
      pointDisplaySource->InvokeCommand("clearVisible");
      double ml = this->Internal->boundingBox.GetMaxLength() * 0.015625;
      if (this->CurrentModifierArc->getFunctionMode() != pqCMBModifierArc::Single)
      {
        pqSMAdaptor::setElementProperty(
          pointDisplaySource->GetProperty("setVisible"), wrapper->getPointIndex());
        pqSMAdaptor::setElementProperty(pointDisplaySource->GetProperty("setScale"), ml);
        pointDisplaySource->UpdateVTKObjects();
      }
      pqSMAdaptor::setElementProperty(pointDisplaySource->GetProperty("setVisible"), -1);
      pointDisplaySource->UpdatePipeline();
      this->Internal->Representation->getProxy()->UpdateVTKObjects();
      this->view->forceRender();
      emit requestRender();
    }
  }
}

std::vector<int> pqCMBModifierArcManager::getCurrentOrder(QString fname, int pindx)
{
  std::vector<int> result;
  for (int i = 0; i < this->TableWidget->rowCount(); ++i)
  {
    QTableWidgetItem* tmp = this->TableWidget->item(i, Id);
    int id = tmp->text().toInt();
    tmp = this->TableWidget->item(i, VisibilityCol);
    if (tmp->checkState() == Qt::Checked && ArcLinesApply[id][fname][pindx])
    {
      result.push_back(id);
    }
  }
  return result;
}

void pqCMBModifierArcManager::addLine()
{
  emit this->addingNewArc();
  selectLine(-2);
}

void pqCMBModifierArcManager::selectLine(int sid)
{
  vtkSMSourceProxy* pointDisplaySource =
    vtkSMSourceProxy::SafeDownCast(this->Internal->LineGlyphFilter->getProxy());
  if (this->CurrentModifierArc != NULL)
  {
    if (sid == this->CurrentModifierArc->getId())
      return;
    pqSMAdaptor::setInputProperty(pointDisplaySource->GetProperty("Input"), NULL, 0);
    pointDisplaySource->MarkModified(pointDisplaySource);
    pointDisplaySource->UpdateVTKObjects();
    this->CurrentModifierArc->switchToNotEditable();
    this->CurrentModifierArc = NULL;
    this->Internal->UI->removeLineButton->setEnabled(false);
    this->Internal->UI->buttonUpdateLine->setEnabled(false);
    this->Internal->UI->addLineButton->setEnabled(true);
    if (this->Internal->UI_Dialog != NULL)
    {
      QPushButton* applyButton =
        this->Internal->UI_Dialog->buttonBox->button(QDialogButtonBox::Apply);
      applyButton->setEnabled(true);
    }
  }
  if (sid == -1 || sid < -2)
  {
    this->Internal->UI->points->setRowCount(0);
    this->Internal->mode = NoEditMode;
    return;
  }
  else if (sid == -2) //create new one
  {
    this->Internal->mode = EditArc;
    pqCMBModifierArc* dig = new pqCMBModifierArc();
    this->ArcWidgetManager->setActiveArc(dig->GetCmbArc());
    this->ArcWidgetManager->create();
    QWidget* tmpWidget = this->ArcWidgetManager->getActiveWidget();
    tmpWidget->hide();
    this->Internal->CurrentArcWidget = dynamic_cast<qtArcWidget*>(tmpWidget);

    QObject::connect(this->Internal->arcEditWidget->Close, SIGNAL(toggled(bool)),
      this->Internal->CurrentArcWidget, SLOT(closeLoop(bool)), Qt::UniqueConnection);
    QObject::connect(this->Internal->arcEditWidget->EditMode, SIGNAL(toggled(bool)),
      this->Internal->CurrentArcWidget, SLOT(updateMode()), Qt::UniqueConnection);
    QObject::connect(this->Internal->arcEditWidget->ModifyMode, SIGNAL(toggled(bool)),
      this->Internal->CurrentArcWidget, SLOT(ModifyMode()), Qt::UniqueConnection);
    this->Internal->arcEditWidget->EditMode->setChecked(true);

    addNewArc(dig);

    this->disableSelection();
    this->Internal->UI->addLineButton->setEnabled(false);
    if (this->Internal->UI_Dialog != NULL)
    {
      QPushButton* applyButton =
        this->Internal->UI_Dialog->buttonBox->button(QDialogButtonBox::Apply);
      applyButton->setEnabled(false);
    }
  }
  else
  {
    if (static_cast<size_t>(sid) < ArcLines.size() && ArcLines[sid] != NULL)
    {
      this->CurrentModifierArc = ArcLines[sid];
      this->CurrentModifierArc->switchToEditable();
      this->Internal->mode = EditFunction;
      this->ArcWidgetManager->setActiveArc(this->CurrentModifierArc->GetCmbArc());
      this->Internal->UI->removeLineButton->setEnabled(true);
      this->Internal->UI->buttonUpdateLine->setEnabled(true);
    }
  }
  if (this->CurrentModifierArc != NULL)
  {
    if (this->CurrentModifierArc->GetCmbArc()->getSource() != NULL)
    {
      pqSMAdaptor::setInputProperty(pointDisplaySource->GetProperty("Input"),
        this->CurrentModifierArc->GetCmbArc()->getSource()->getProxy(), 0);
      pointDisplaySource->MarkModified(pointDisplaySource);
      pointDisplaySource->UpdateVTKObjects();
    }
    this->updateLineFunctions();
    this->setDatasetTable(sid);
    this->setUpPointsTable();
    this->Internal->UI->points->selectRow(0);
  }
  this->updateUiControls();
}

void pqCMBModifierArcManager::addNewArc(pqCMBModifierArc* dig)
{
  this->CurrentModifierArc = dig;
  int sid = static_cast<int>(ArcLines.size());
  ArcLines.push_back(dig);
  addApplyControl();
  //TODO THIS NEEDS TO BE BETTER
  dig->setId(sid);
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      QList<QVariant> v;
      v << sid;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("AddArc"), v);
      v.clear();
      v << sid << 1;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("ArcEnable"), v);
      source->UpdateVTKObjects();
    }
  }
  this->AddLinePiece(dig);
  this->check_save();
}

void pqCMBModifierArcManager::update()
{
  switch (this->Internal->mode)
  {
    case EditArc:
      this->Internal->mode = EditFunction;
      this->Internal->CurrentArcWidget->finishContour();
      if (this->Internal->UI_Dialog != NULL)
      {
        QPushButton* applyButton =
          this->Internal->UI_Dialog->buttonBox->button(QDialogButtonBox::Apply);
        applyButton->setEnabled(true);
      }
      break;
    case NoEditMode:
    case EditFunction:
      break;
  }

  if (this->CurrentModifierArc == NULL)
  {
    return;
  }
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      if (source == NULL)
        continue;
      this->CurrentModifierArc->updateArc(source, this->Internal->boundingBox);
    }
  }
  setUpPointsTable();
  updateUiControls();
  emit(functionsUpdated());
  emit(requestRender());
  double removed = 0;
  double added = 0;

  bool isN1 = false;
  bool allNull = true;
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      if (source == NULL)
        continue;
      allNull = false;
      source->UpdatePropertyInformation();
      double r = pqSMAdaptor::getElementProperty(source->GetProperty("AmountRemoved")).toDouble();
      double a = pqSMAdaptor::getElementProperty(source->GetProperty("AmountAdded")).toDouble();
      if (r == -1 || a == -1)
      {
        isN1 = true;
        continue;
      }
      removed += r;
      added += a;
    }
  }
  if (isN1)
  {
    this->Internal->UI->amountAdded->setText("NA");
    this->Internal->UI->amountRemoved->setText("NA");
    if (!allNull)
      this->Internal->UI->PointBathChangeControl->show();
  }
  else
  {
    this->Internal->UI->amountAdded->setText(QString::number(added));
    this->Internal->UI->amountRemoved->setText(QString::number(removed));
  }
  if (allNull)
    this->Internal->UI->PointBathChangeControl->hide();
}

void pqCMBModifierArcManager::computeChange()
{
  double spacing = this->Internal->UI->SamplingSize->value();
  double radius = this->Internal->UI->SearchRadius->value();
  double removed = 0;
  double added = 0;

  QList<QVariant> v;
  v << spacing << radius;

  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      if (source == NULL)
        continue;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("computeDisplacementChange"), v);
      source->UpdateVTKObjects();
      source->UpdatePropertyInformation();
      double r = pqSMAdaptor::getElementProperty(source->GetProperty("AmountRemoved")).toDouble();
      double a = pqSMAdaptor::getElementProperty(source->GetProperty("AmountAdded")).toDouble();
      if (r == -1 || a == -1)
      {
        continue;
      }
      removed += r;
      added += a;
    }
  }
  this->Internal->UI->amountAdded->setText(QString::number(added));
  this->Internal->UI->amountRemoved->setText(QString::number(removed));
}

void pqCMBModifierArcManager::removeArc()
{
  if (this->CurrentModifierArc == NULL)
  {
    return;
  }
  int id = this->CurrentModifierArc->getId();
  this->enableSelection();
  selectLine(-1);

  pqCMBModifierArc* tmp = ArcLines[id];
  bool hasMore = this->remove(id, false);
  if (!hasMore)
  {
    QObject::disconnect(tmp, SIGNAL(requestRender()), this, SIGNAL(requestRender()));
    ArcLines[id] = NULL;
    foreach (QString filename, ServerProxies.keys())
    {
      foreach (int pieceIdx, ServerProxies[filename].keys())
      {
        vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
        tmp->removeFromServer(source);
      }
    }

    delete tmp;
  }
  emit(requestRender());
}

void pqCMBModifierArcManager::addProxy(QString s, int pid, vtkBoundingBox box, pqPipelineSource* ps)
{
  assert(ps != NULL);
  assert(!s.isNull());
  vtkSMSourceProxy* source = NULL;
  source = vtkSMSourceProxy::SafeDownCast(ps->getProxy());
  if (source == NULL)
  {
    return;
  }
  ServerProxies[s].insert(pid, DataSource(box, source));
  setUpTable();

  this->Internal->boundingBox.AddBox(box);

  for (unsigned int i = 0; i < this->ArcLines.size(); ++i)
  {
    if (this->ArcLines[i] != NULL)
    {
      QList<QVariant> v;
      v << this->ArcLines[i]->getId();
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("AddArc"), v);
      v.clear();
      v << this->ArcLines[i]->getId() << 1;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("ArcEnable"), v);
      source->UpdateVTKObjects();
      this->ArcLines[i]->updateArc(source, this->Internal->boundingBox);
      ArcLinesApply[i][s][pid] = true;
    }
  }

  this->sendOrder();
}

void pqCMBModifierArcManager::clearProxies()
{
  ServerProxies.clear();
  ServerRows.clear();
  ArcLinesApply.clear();
  if (!this->ArcLines.empty())
  {
    QMap<QString, QMap<int, bool> > newVis;
    ArcLinesApply.resize(this->ArcLines.size(), newVis);
  }
  this->Internal->boundingBox.Reset();
}

void pqCMBModifierArcManager::updateLineFunctions()
{
  if (this->CurrentModifierArc == NULL)
    return;
  pqCMBModifierArc* dig = this->CurrentModifierArc;
  std::vector<cmbProfileFunction*> funs;
  dig->getFunctions(funs);
  this->Internal->UI->FunctionName->blockSignals(true);
  this->Internal->UI->FunctionName->clear();
  for (unsigned int i = 0; i < funs.size(); ++i)
  {
    this->AddFunction(funs[i]);
  }
  this->functionModeChanged(CurrentModifierArc->getFunctionMode());
  QString name;
  QList<QTableWidgetItem*> selected = this->Internal->UI->points->selectedItems();
  if (!selected.empty())
  {
    QVariant qv = selected[0]->data(Qt::UserRole);
    pqCMBModifierArc::pointFunctionWrapper* wrapper =
      static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
    name = wrapper->getName().c_str();
  }

  this->Internal->UI->FunctionName->setCurrentIndex(
    this->Internal->UI->FunctionName->findText(name));
  this->Internal->UI->FunctionName->blockSignals(false);
  onFunctionSelectionChange();
}

void pqCMBModifierArcManager::selectFunction(cmbProfileFunction* fun)
{
  delete ManualFunctionWidget;
  delete WedgeFunctionWidget;
  ManualFunctionWidget = NULL;
  WedgeFunctionWidget = NULL;
  selectedFunction = NULL;
  if (fun == NULL)
  {
    this->Internal->UI->CloneFunction->setEnabled(false);
    this->Internal->UI->DeleteFunction->setEnabled(false);
    this->Internal->UI->Save->setEnabled(false);
  }
  else
  {
    this->Internal->UI->CloneFunction->setEnabled(true);
    this->Internal->UI->DeleteFunction->setEnabled(true);
    this->Internal->UI->Save->setEnabled(true);
    this->Internal->UI->FunctionType->setCurrentIndex(static_cast<int>(fun->getType()));
    selectedFunction = fun;
    this->nameChanged(fun->getName().c_str());
    QList<QTableWidgetItem*> selected = this->Internal->UI->points->selectedItems();
    if (!selected.empty())
    {
      QVariant qv = selected[0]->data(Qt::UserRole);
      pqCMBModifierArc::pointFunctionWrapper* wrapper =
        static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
      this->Internal->UI->points->item(selected[0]->row(), 1)
        ->setText(selectedFunction->getName().c_str());
      this->Internal->UI->points->resizeColumnsToContents();
      switch (this->CurrentModifierArc->getFunctionMode())
      {
        case pqCMBModifierArc::EndPoints:
          if (wrapper->getPointIndex() != 0)
          {
            CurrentModifierArc->setEndFun(selectedFunction->getName());
            break;
          }
        case pqCMBModifierArc::Single:
          CurrentModifierArc->setStartFun(selectedFunction->getName());
          break;
        case pqCMBModifierArc::PointAssignment:
          CurrentModifierArc->addFunctionAtPoint(wrapper->getPointId(), selectedFunction);
      }
    }

    switch (fun->getType())
    {
      case cmbProfileFunction::MANUAL:
        ManualFunctionWidget =
          new qtCMBManualFunctionWidget(dynamic_cast<cmbManualProfileFunction*>(fun), NULL);
        functionLayout->addWidget(this->ManualFunctionWidget);
        break;
      case cmbProfileFunction::WEDGE:
        WedgeFunctionWidget =
          new qtCMBProfileWedgeFunctionWidget(NULL, dynamic_cast<cmbProfileWedgeFunction*>(fun));
        functionLayout->addWidget(this->WedgeFunctionWidget);
        break;
    }
  }
}

void pqCMBModifierArcManager::functionTypeChanged(int type)
{
  if (selectedFunction == NULL)
    return;
  if (type == selectedFunction->getType())
    return;
  std::string name = selectedFunction->getName();
  cmbProfileFunction* fun = this->CurrentModifierArc->setFunction(
    name, static_cast<cmbProfileFunction::FunctionType>(type));
  int index = this->Internal->UI->FunctionName->currentIndex();
  if (index != -1)
  {
    QVariant vdata;
    vdata.setValue(static_cast<void*>(fun));
    this->Internal->UI->FunctionName->setItemData(index, vdata); //todo
  }

  selectFunction(fun);
}

void pqCMBModifierArcManager::doneModifyingArc()
{
  if (this->CurrentModifierArc != NULL)
  {
    foreach (QString filename, ServerProxies.keys())
    {
      foreach (int pieceIdx, ServerProxies[filename].keys())
      {
        vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
        this->CurrentModifierArc->updateArc(source, this->Internal->boundingBox);
      }
    }
  }

  this->enableSelection();
  this->CurrentModifierArc = NULL;
  //this->unselectAllRows();
  emit this->modifyingArcDone();
  {
    QList<QTableWidgetSelectionRange> selected = this->TableWidget->selectedRanges();
    if (!selected.empty())
    {
      int row = selected.front().topRow();
      int id = this->TableWidget->item(row, Id)->text().toInt();
      selectLine(id);
    }
  }
  this->onSelectionChange();
}

void pqCMBModifierArcManager::addApplyControl()
{
  QMap<QString, QMap<int, bool> > newVis;
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      newVis[filename][pieceIdx] = true;
    }
  }
  ArcLinesApply.resize(this->ArcLines.size(), newVis);
}

void pqCMBModifierArcManager::sendOrder()
{
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      vtkSMSourceProxy* source = ServerProxies[filename][pieceIdx].source;
      std::vector<int> order = this->getCurrentOrder(filename, pieceIdx);
      QList<QVariant> v;
      v << static_cast<int>(order.size());
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("ResizeOrder"), v);
      source->UpdateVTKObjects();
      v.clear();
      v << -1;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("ResizeOrder"), v);
      source->UpdateVTKObjects();
      for (unsigned int i = 0; i < order.size(); ++i)
      {
        v.clear();
        v << static_cast<int>(i) << order[i];
        pqSMAdaptor::setMultipleElementProperty(source->GetProperty("SetOrderValue"), v);
        source->UpdateVTKObjects();
      }
      v.clear();
      v << -1 << -1;
      pqSMAdaptor::setMultipleElementProperty(source->GetProperty("SetOrderValue"), v);
      source->UpdateVTKObjects();
      source->UpdatePipeline();
      source->UpdatePropertyInformation();
    }
  }
  emit requestRender();
}

void pqCMBModifierArcManager::setUpTable()
{
  QTableWidget* tmp = this->Internal->UI->DatasetTable;
  tmp->blockSignals(true);
  tmp->clearContents();
  tmp->setRowCount(0);
  Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
  tmp->setSelectionMode(QAbstractItemView::NoSelection);
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      tmp->insertRow(tmp->rowCount());
      int row = tmp->rowCount() - 1;
      ServerRows[filename][pieceIdx] = row;
      QTableWidgetItem* objItem = new QTableWidgetItem();
      QVariant vdata;
      objItem->setData(Qt::UserRole, vdata);

      tmp->setItem(row, 0, objItem);
      objItem->setFlags(commFlags | Qt::ItemIsUserCheckable);
      objItem->setCheckState(Qt::Checked);

      objItem = new QTableWidgetItem(filename);
      tmp->setItem(row, 1, objItem);
      objItem->setFlags(commFlags);

      objItem = new QTableWidgetItem(QString::number(pieceIdx));
      tmp->setItem(row, 2, objItem);
      objItem->setFlags(commFlags);
    }
  }
  tmp->resizeColumnsToContents();
  tmp->blockSignals(false);
}

void pqCMBModifierArcManager::setUpPointsTable()
{
  pqCMBModifierArc::pointFunctionWrapper* wrapper = NULL;
  QList<QTableWidgetItem*> selected = this->Internal->UI->points->selectedItems();
  if (!selected.empty())
  {
    QVariant qv = selected[0]->data(Qt::UserRole);
    wrapper = static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
  }
  QTableWidget* tmp = this->Internal->UI->points;
  tmp->setSelectionMode(QAbstractItemView::SingleSelection);
  tmp->setSelectionBehavior(QAbstractItemView::SelectRows);
  tmp->blockSignals(true);

  tmp->clearContents();
  tmp->setRowCount(0);
  if (CurrentModifierArc == NULL)
  {
    tmp->blockSignals(false);
    return;
  }
  tmp->setSelectionMode(QAbstractItemView::SingleSelection);

  std::vector<pqCMBModifierArc::pointFunctionWrapper const*> points;
  CurrentModifierArc->getPointFunctions(points);
  for (size_t i = 0; i < points.size(); ++i)
  {
    this->addItemToTable(points[i], wrapper == points[i] || (wrapper == NULL && i == 0));
  }
  tmp->resizeColumnsToContents();
  tmp->blockSignals(false);
  onPointsSelectionChange();
}

void pqCMBModifierArcManager::setDatasetTable(int inId)
{
  if (inId < 0 || static_cast<size_t>(inId) >= ArcLines.size())
    return;
  this->Internal->UI->DatasetTable->blockSignals(true);
  foreach (QString filename, ServerProxies.keys())
  {
    foreach (int pieceIdx, ServerProxies[filename].keys())
    {
      int row = ServerRows[filename][pieceIdx];
      bool visOnDs = ArcLinesApply[inId][filename][pieceIdx];
      this->Internal->UI->DatasetTable->item(row, 0)->setCheckState(
        visOnDs ? Qt::Checked : Qt::Unchecked);
    }
  }
  this->Internal->UI->DatasetTable->blockSignals(false);
}

void pqCMBModifierArcManager::disableAbsolute()
{
  useNormal = true;
  this->TableWidget->blockSignals(true);
  for (unsigned int i = 0; i < ArcLines.size(); ++i)
  {
    QTableWidgetItem* tmp = this->TableWidget->item(i, Relative);
    tmp->setFlags((tmp->flags() | Qt::ItemIsUserCheckable) ^ Qt::ItemIsUserCheckable);
    tmp->setCheckState(Qt::Checked);

    ArcLines[i]->setRelative(true);
  }
  this->TableWidget->blockSignals(false);
}

void pqCMBModifierArcManager::enableAbsolute()
{
  useNormal = false;
  for (unsigned int i = 0; i < ArcLines.size(); ++i)
  {
    QTableWidgetItem* tmp = this->TableWidget->item(i, Relative);
    tmp->setFlags(tmp->flags() | Qt::ItemIsUserCheckable);
  }
}

void pqCMBModifierArcManager::onSaveArc()
{
  QString fileName =
    QFileDialog::getSaveFileName(NULL, tr("Save File"), "", tr("Function Profile (*.mar)"));
  if (fileName.isEmpty())
  {
    return;
  }

  QList<pqOutputPort*> ContourInputs;
  std::ofstream out(fileName.toStdString().c_str());
  out << 2 << "\n";

  CurrentModifierArc->write(out);
  pqOutputPort* port = CurrentModifierArc->GetCmbArc()->getSource()->getOutputPort(0);
  ContourInputs.push_back(port);

  {
    QFileInfo fi(fileName);
    std::string fname =
      QFileInfo(fi.dir(), fi.baseName() + ".vtp").absoluteFilePath().toStdString();
    QMap<QString, QList<pqOutputPort*> > namedInputs;
    namedInputs["Input"] = ContourInputs;

    //now that we have collected all the contour info, write out the file
    pqApplicationCore* core = pqApplicationCore::instance();
    pqObjectBuilder* builder = core->getObjectBuilder();
    pqPipelineSource* writer = builder->createFilter(
      "writers", "SceneGenV2ContourWriter", namedInputs, core->getActiveServer());

    vtkSMSourceProxy* proxy = vtkSMSourceProxy::SafeDownCast(writer->getProxy());

    pqSMAdaptor::setElementProperty(proxy->GetProperty("FileName"), fname.c_str());
    proxy->UpdateProperty("FileName");
    proxy->UpdatePipeline();

    //now that the file has been written, delete the writer
    builder->destroy(writer);
  }
}

void pqCMBModifierArcManager::onLoadArc()
{
  QStringList fileNames =
    QFileDialog::getOpenFileNames(NULL, "Open File...", "", "Function Profile (*.mar)");
  if (fileNames.count() == 0)
  {
    return;
  }

  std::string fname = fileNames[0].toStdString();
  size_t start = ArcLines.size();
  int rc = this->TableWidget->rowCount();
  {
    QFileInfo fi(fileNames[0]);
    pqPipelineSource* reader = NULL;
    QStringList files;
    files << QFileInfo(fi.dir(), fi.baseName() + ".vtp").absoluteFilePath();

    pqApplicationCore* core = pqApplicationCore::instance();
    pqObjectBuilder* builder = core->getObjectBuilder();
    builder->blockSignals(true);

    reader = builder->createReader("sources", "XMLPolyDataReader", files, core->getActiveServer());
    builder->blockSignals(false);

    if (reader)
    {
      pqPipelineSource* extractContour =
        builder->createFilter("filters", "CmbExtractContours", reader);
      vtkSMSourceProxy* proxy = vtkSMSourceProxy::SafeDownCast(extractContour->getProxy());
      proxy->UpdatePipeline();
      proxy->UpdatePropertyInformation();
      int max = pqSMAdaptor::getElementProperty(proxy->GetProperty("NumberOfContoursInfo")).toInt();
      pqCMBModifierArc* dig = new pqCMBModifierArc(proxy);
      addNewArc(dig);
      for (int i = 1; i < max; ++i)
      {
        pqSMAdaptor::setElementProperty(proxy->GetProperty("ContourIndex"), i);
        proxy->UpdateProperty("ContourIndex");
        proxy->UpdatePipeline();
        pqCMBModifierArc* dig2 = new pqCMBModifierArc(proxy);
        QObject::connect(dig2, SIGNAL(requestRender()), this, SIGNAL(requestRender()));
        addNewArc(dig2);
      }
    }
  }
  std::ifstream in(fname.c_str());
  int version;
  in >> version;

  size_t at = start + 0;
  ArcLines[at]->read(in);
  this->setRow(rc + 0, ArcLines[at]);
  ArcLines[at]->switchToNotEditable();

  accepted();
  onClearSelection();
  this->CurrentModifierArc = NULL;
  this->check_save();
}

void pqCMBModifierArcManager::importFunction()
{
  QStringList fileNames =
    QFileDialog::getOpenFileNames(NULL, "Open File...", "", "Function Profile (*.mar)");
  if (fileNames.count() == 0)
  {
    return;
  }

  std::string fname = fileNames[0].toStdString();
  std::ifstream in(fname.c_str());
  int version;
  in >> version;
  this->CurrentModifierArc->read(in, true);
  this->updateLineFunctions();
}

void pqCMBModifierArcManager::check_save()
{
  for (unsigned int i = 0; i < ArcLines.size(); ++i)
  {
    if (ArcLines[i] != NULL)
    {
      this->Internal->UI->Save->setEnabled(true);
      return;
    }
  }
  this->Internal->UI->Save->setEnabled(false);
}

void pqCMBModifierArcManager::nameChanged(QString n)
{
  if (selectedFunction == NULL || CurrentModifierArc == NULL)
    return;
  if (n.isEmpty())
    return;

  if (CurrentModifierArc->updateLabel(n.toStdString(), selectedFunction))
  {
    int index = this->Internal->UI->FunctionName->currentIndex();
    if (index != -1)
    {
      this->Internal->UI->FunctionName->setItemText(index, n);
    }
    QPalette palette;
    palette.setColor(QPalette::Base, Qt::white);
    palette.setColor(QPalette::Text, Qt::black);
    this->Internal->UI->FunctionName->setPalette(palette);
    for (int row = 0; row < this->Internal->UI->points->rowCount(); ++row)
    {
      QTableWidgetItem* item = this->Internal->UI->points->item(row, 1);
      pqCMBModifierArc::pointFunctionWrapper* other =
        static_cast<pqCMBModifierArc::pointFunctionWrapper*>(
          item->data(Qt::UserRole).value<void*>());
      item->setText(other->getName().c_str());
    }
    this->Internal->UI->points->resizeColumnsToContents();
  }
  else
  {
    QPalette palette;
    palette.setColor(QPalette::Base, Qt::red);
    palette.setColor(QPalette::Text, Qt::black);
    this->Internal->UI->FunctionName->setPalette(palette);
  }
}

void pqCMBModifierArcManager::setToDefault()
{
  //TODO redo this
  //if(selectedFunction && CurrentModifierArc)
  //{
  //  CurrentModifierArc->setStartFun(selectedFunction->getName());
  //}
}

void pqCMBModifierArcManager::deleteFunction()
{
  if (selectedFunction && CurrentModifierArc)
  {
    this->Internal->UI->FunctionName->blockSignals(true);
    if (CurrentModifierArc->deleteFunction(selectedFunction->getName()))
    {
      int index = this->Internal->UI->FunctionName->currentIndex();
      if (index != -1)
      {
        this->Internal->UI->FunctionName->removeItem(index);
        if (index != 0)
        {
          index--;
        }
        this->Internal->UI->FunctionName->setCurrentIndex(index);
      }
      onFunctionSelectionChange();
    }
    this->Internal->UI->FunctionName->blockSignals(false);
  }
  this->Internal->UI->DeleteFunction->setEnabled(this->Internal->UI->FunctionName->count() > 1);
}

void pqCMBModifierArcManager::cloneFunction()
{
  if (selectedFunction && CurrentModifierArc)
  {
    int count = this->Internal->UI->FunctionName->count();
    this->AddFunction(CurrentModifierArc->cloneFunction(selectedFunction->getName()));
    this->Internal->UI->FunctionName->setCurrentIndex(count);
  }
}

void pqCMBModifierArcManager::functionModeChanged(int m)
{
  if (CurrentModifierArc == NULL)
    return;
  pqCMBModifierArc::FunctionMode mode = static_cast<pqCMBModifierArc::FunctionMode>(m);
  if (mode == CurrentModifierArc->getFunctionMode())
    return;
  CurrentModifierArc->setFunctionMode(mode);
  this->Internal->UI->pointsFrame->show();
  switch (CurrentModifierArc->getFunctionMode())
  {
    case pqCMBModifierArc::EndPoints:
      this->Internal->UI->AddPoint->hide();
      this->Internal->UI->RemovePoint->hide();
      break;
    case pqCMBModifierArc::PointAssignment:
      this->Internal->UI->AddPoint->show();
      this->Internal->UI->RemovePoint->show();
      break;
    case pqCMBModifierArc::Single:
      this->Internal->UI->pointsFrame->hide();
      this->pointDisplayed(0);
      break;
  }
  this->setUpPointsTable();
}

void pqCMBModifierArcManager::pointDisplayed(int index)
{
  QString name;
  if (CurrentModifierArc == NULL)
    return;
  switch (CurrentModifierArc->getFunctionMode())
  {
    case pqCMBModifierArc::EndPoints:
      if (index == 1)
      {
        name = CurrentModifierArc->getEndFun()->getName().c_str();
        break;
      }
    case pqCMBModifierArc::Single:
      //this->ArcWidgetManager->highlightPoint(0);
      name = CurrentModifierArc->getStartFun()->getName().c_str();
      break;
    case pqCMBModifierArc::PointAssignment:
      break;
  }
  this->Internal->UI->FunctionName->setCurrentIndex(
    this->Internal->UI->FunctionName->findText(name));
}

int pqCMBModifierArcManager::addPoint(vtkIdType id)
{
  this->addPointMode = false;
  std::vector<cmbProfileFunction*> funs;
  CurrentModifierArc->getFunctions(funs);
  int result = -1;
  if (!CurrentModifierArc->pointHasFunction(id))
  {
    result = this->addItemToTable(CurrentModifierArc->addFunctionAtPoint(id, funs[0]), true);
  }
  this->updateUiControls();
  onSelectionChange();
  return result;
}

class vtkPointSelectedCallback : public vtkCommand
{
  friend class pqCMBModifierArcManager;

public:
  static vtkPointSelectedCallback* New()
  {
    vtkPointSelectedCallback* cb = new vtkPointSelectedCallback;

    return cb;
  }

  void Execute(
    vtkObject* vtkNotUsed(caller), unsigned long pointID, void* vtkNotUsed(callData)) override
  {
    int index = static_cast<int>(pointID);
    vtkSMNewWidgetRepresentationProxy* widgetProxy = this->arcWidget->widgetProxy();
    vtkContourWidget* widget = vtkContourWidget::SafeDownCast(widgetProxy->GetWidget());
    vtkSMTKArcRepresentation* widgetRep =
      vtkSMTKArcRepresentation::SafeDownCast(widget->GetRepresentation());
    int row = -1;
    if (index >= 0 && index < this->arcInfo->GetNumberOfPoints())
    {
      vtkIdType id;
      this->arcInfo->GetPointID(index, id);
      row = manager->addPoint(id);
    }

    //vtkCMBArcRepresentation is replaced by vtkSMTKArcRepresentation
    widgetRep->SetPointSelectMode(0);
    this->arcWidget->finishContour();
    this->arcWidget->setEnableInteractivity(false);
    this->arcWidget->setVisible(false);
    this->arcWidget->deemphasize();
    this->arcWidget->hide();
    this->arcWidget->widgetProxy()->UpdatePropertyInformation();
    this->arcWidget->setView(NULL);
    this->arcWidget->hide();
    if (row >= 0)
      manager->Internal->UI->points->selectRow(row);
  }

private:
  qtArcWidget* arcWidget;
  vtkPVArcInfo* arcInfo;
  pqCMBModifierArcManager* manager;
};

void pqCMBModifierArcManager::addPoint()
{
  this->editArc();
  this->Internal->mode = EditFunction;

  vtkSMNewWidgetRepresentationProxy* widgetProxy = this->Internal->CurrentArcWidget->widgetProxy();
  vtkContourWidget* widget = vtkContourWidget::SafeDownCast(widgetProxy->GetWidget());
  vtkSMTKArcRepresentation* widgetRep =
    vtkSMTKArcRepresentation::SafeDownCast(widget->GetRepresentation());
  widgetRep->PickableOn();
  widgetRep->SetPointSelectMode(1);
  vtkPointSelectedCallback* psc = vtkPointSelectedCallback::New();
  psc->arcWidget = this->Internal->CurrentArcWidget;
  psc->manager = this;
  psc->arcInfo = this->CurrentModifierArc->GetCmbArc()->getArcInfo();

  widgetRep->SetPointSelectCallBack(psc);

  this->addPointMode = true;
  this->updateUiControls();
}

void pqCMBModifierArcManager::deletePoint()
{
  if (this->addPointMode)
  {
    ArcWidgetManager->cancelSelectPoint();
    this->addPointMode = false;
    return;
  }

  QList<QTableWidgetItem*> selected = this->Internal->UI->points->selectedItems();
  if (!selected.empty())
  {
    QVariant qv = selected[0]->data(Qt::UserRole);
    pqCMBModifierArc::pointFunctionWrapper* wrapper =
      static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
    CurrentModifierArc->removeFunctionAtPoint(wrapper->getPointId());
    this->Internal->UI->points->removeRow(selected[0]->row());
  }
}

int pqCMBModifierArcManager::addItemToTable(
  pqCMBModifierArc::pointFunctionWrapper const* mp, bool select)
{
  if (mp == NULL)
    return -1;
  QTableWidget* tmp = this->Internal->UI->points;

  Qt::ItemFlags commFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);

  double pt[3];
  vtkPVArcInfo* ai = CurrentModifierArc->GetCmbArc()->getArcInfo();
  ai->GetPointLocation(mp->getPointIndex(), pt);

  int row = tmp->rowCount();
  if (tmp->rowCount() != 0)
  {
    //If there are enough points, binary would be significantly faster
    for (row = 0; row < tmp->rowCount(); ++row)
    {
      QTableWidgetItem* item = tmp->item(row, 0);
      QVariant qv = item->data(Qt::UserRole);
      pqCMBModifierArc::pointFunctionWrapper* wrapper =
        static_cast<pqCMBModifierArc::pointFunctionWrapper*>(qv.value<void*>());
      if (mp->getPointIndex() < wrapper->getPointIndex())
        break;
    }
  }

  tmp->insertRow(row);

  QTableWidgetItem* qtwi = new QTableWidgetItem(QString::number(mp->getPointIndex()));
  QVariant vdata;
  vdata.setValue(static_cast<void*>(const_cast<pqCMBModifierArc::pointFunctionWrapper*>(mp)));
  qtwi->setData(Qt::UserRole, vdata);
  qtwi->setFlags(commFlags);
  tmp->setItem(row, 0, qtwi);
  qtwi = new QTableWidgetItem(QString(mp->getName().c_str()));
  qtwi->setData(Qt::UserRole, vdata);
  qtwi->setFlags(commFlags);
  tmp->setItem(row, 1, qtwi);
  if (select)
  {
    tmp->selectRow(row);
  }
  if (tmp->rowCount() == 1)
  {
    tmp->resizeColumnsToContents();
  }
  return row;
}

void pqCMBModifierArcManager::updateUiControls()
{
  switch (this->Internal->mode)
  {
    case NoEditMode:
      this->Internal->UI->EditControl->hide();
      this->Internal->UI->FunctionWidget->hide();
      this->Internal->UI->buttonUpdateLine->setEnabled(false);
      this->Internal->UI->addLineButton->setEnabled(true);
      this->Internal->UI->EditArc->setEnabled(false);
      this->Internal->UI->pointsFrame->hide();
      this->Internal->UI->DatasetControl->hide();
      this->Internal->UI->Load->setEnabled(true);
      this->Internal->UI->importFunctions->setEnabled(false);
      break;
    case EditArc:
      this->Internal->UI->EditControl->show();
      this->Internal->UI->FunctionWidget->hide();
      this->Internal->UI->buttonUpdateLine->setEnabled(true);
      this->Internal->UI->addLineButton->setEnabled(false);
      this->Internal->UI->EditArc->setEnabled(false);
      this->Internal->UI->pointsFrame->hide();
      this->Internal->UI->DatasetControl->hide();
      this->Internal->UI->Load->setEnabled(false);
      this->Internal->UI->importFunctions->setEnabled(false);
      break;
    case EditFunction:
      this->Internal->UI->EditControl->hide();
      this->Internal->UI->FunctionWidget->show();
      this->Internal->UI->buttonUpdateLine->setEnabled(true);
      this->Internal->UI->addLineButton->setEnabled(!this->addPointMode);
      this->Internal->UI->EditArc->setEnabled(!this->addPointMode);
      this->Internal->UI->DatasetControl->show();
      this->Internal->UI->Load->setEnabled(true);
      this->Internal->UI->importFunctions->setEnabled(true);
      if (this->CurrentModifierArc->getFunctionMode() == pqCMBModifierArc::Single)
      {
        this->Internal->UI->pointsFrame->hide();
      }
      else
      {
        this->Internal->UI->pointsFrame->show();
      }
      break;
  }
}

void pqCMBModifierArcManager::editArc()
{
  this->Internal->mode = EditArc;
  this->updateUiControls();
  this->onSelectionChange();

  this->ArcWidgetManager->setActiveArc(this->CurrentModifierArc->GetCmbArc());

  this->Internal->CurrentArcWidget->setView(this->view);

  vtkSMPropertyHelper(this->Internal->CurrentArcWidget->widgetProxy(), "Enabled").Set(1);
  this->Internal->CurrentArcWidget->widgetProxy()->UpdateVTKObjects();

  this->Internal->CurrentArcWidget->setEnableInteractivity(true);
  this->Internal->CurrentArcWidget->setEnabled(true);

  vtkNew<vtkCMBArcEditClientOperator> editOp;
  editOp->SetArcIsClosed(CurrentModifierArc->GetCmbArc()->isClosedLoop());
  editOp->Operate(CurrentModifierArc->GetCmbArc()->getSource()->getProxy(),
    this->Internal->CurrentArcWidget->widgetProxy());
  this->Internal->arcEditWidget->ModifyMode->setChecked(true);
  this->Internal->CurrentArcWidget->emphasize();
  this->Internal->CurrentArcWidget->setVisible(true);
  this->view->forceRender();
}
