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

  Program:   CMB
  Module:    qtCMBArcWidgetManager.cxx

Copyright (c) 1998-2005 Kitware Inc. 28 Corporate Drive, Suite 204,
Clifton Park, NY, 12065, USA.

All rights reserved. No part of this software may be reproduced,
distributed,
or modified, in any form or by any means, without permission in writing from
Kitware Inc.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADiVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
=========================================================================*/
// .NAME qtCMBArcWidgetManager
// .SECTION Description
//  Create and controls the arc editing singelton widget
// .SECTION Caveats

#include "qtCMBArcWidgetManager.h"

#include "pqCMBArc.h"
#include "pqCMBSceneTree.h"
#include "pqCMBSceneNode.h"
#include "pqCMBCommonMainWindowCore.h"
#include "qtCMBArcWidget.h"
#include "qtCMBArcEditWidget.h"

#include "pqApplicationCore.h"
#include "pqObjectBuilder.h"
#include "pqDataRepresentation.h"
#include "pqPipelineSource.h"
#include "pqRenderView.h"
#include "pqSMAdaptor.h"
#include "pqServer.h"

#include "vtkCommand.h"
#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkProcessModule.h"
#include "vtkSMNewWidgetRepresentationProxy.h"
#include "vtkSMInputProperty.h"
#include "vtkSMIntVectorProperty.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxyManager.h"
#include "vtkSMRenderViewProxy.h"
#include "vtkSMSourceProxy.h"
#include "vtkAbstractWidget.h"
#include "vtkNew.h"
#include "vtkCMBSubArcModifyClientOperator.h"

//-----------------------------------------------------------------------------
qtCMBArcWidgetManager::qtCMBArcWidgetManager(pqServer *server, pqRenderView *view)
{
  //server and view need to be set before we call createContourWidget
  this->Server = server;
  this->View = view;
  this->Widget = NULL;
  this->Node = NULL;
  this->Arc = NULL;

  this->EditWidget = NULL;
  this->ActiveWidget = NULL;
}

//-----------------------------------------------------------------------------
qtCMBArcWidgetManager::~qtCMBArcWidgetManager()
{
  this->Server = NULL;
  this->Node = NULL;

  if ( this->Widget )
    {
    //if a widget is deleted without having an active view it throws errors
    if ( this->View && !this->Widget->view() )
      {
      this->Widget->setView(this->View);
      }

    delete this->Widget;
    }
  this->Widget = NULL;
  if ( this->EditWidget )
    {
    delete this->EditWidget;
    }
  this->EditWidget = NULL;
  this->View = NULL;
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::setActiveNode(pqCMBSceneNode *node)
{
  this->Node = node;
}

void qtCMBArcWidgetManager::setActiveArc(pqCMBArc* arc)
{
  this->Arc = arc;
}

//-----------------------------------------------------------------------------
bool qtCMBArcWidgetManager::hasActiveNode()
{
  return (this->Node != NULL);
}

bool qtCMBArcWidgetManager::hasActiveArc()
{
  return Arc != NULL;
}

bool qtCMBArcWidgetManager::isActive()
{
  return this->hasActiveNode() || this->hasActiveArc();
}

//-----------------------------------------------------------------------------
pqCMBSceneNode * qtCMBArcWidgetManager::getActiveNode()
{
  return this->Node;
}

pqCMBArc* qtCMBArcWidgetManager::getActiveArc()
{
  if(this->Node != NULL)
  {
    return dynamic_cast<pqCMBArc*>( this->Node->getDataObject() );
  }
  return this->Arc;
}

//-----------------------------------------------------------------------------
pqCMBArc* qtCMBArcWidgetManager::createpqCMBArc()
{
  //update the node to have everything but the actual contour data
  //we now create a contour data object
  pqApplicationCore* core = pqApplicationCore::instance();
  pqObjectBuilder* builder = core->getObjectBuilder();

  pqCMBArc* obj = new pqCMBArc();

  //now update the node with the real data object
  return obj;
}

//-----------------------------------------------------------------------------
int qtCMBArcWidgetManager::create()
{
  emit this->Busy();
  if ( !this->Node && !this->Arc )
    {
    emit this->Ready();
    return 0;
    }
  bool created = false;
  int normal;
  double planepos;
  if ( !this->Widget )
    {
    this->Widget = this->createDefaultContourWidget(normal, planepos);
    QObject::connect(this->Widget,SIGNAL(contourDone()),
      this,SLOT(updateArcNode()));
    created = true;
    }

  if ( !created )
    {
    this->Widget->setView(this->View);
    this->getDefaultArcPlane(normal, planepos);
    this->resetArcPlane(normal, planepos);
    this->Widget->setWidgetVisible(true);
    this->Widget->reset();
    this->Widget->removeAllNodes();
    }

  this->Widget->select();
  if(this->Node != NULL)
    {
    pqCMBSceneObjectBase* obj = this->Node->getDataObject();
    if ( obj  && obj->getType()==pqCMBSceneObjectBase::Arc)
      {
      dynamic_cast<pqCMBArc*>(obj)->setPlaneProjectionNormal(normal);
      dynamic_cast<pqCMBArc*>(obj)->setPlaneProjectionPosition(planepos);
      }
    }
  else
    {
    this->Arc->setPlaneProjectionNormal(normal);
    this->Arc->setPlaneProjectionPosition(planepos);
    }
  this->ActiveWidget = this->Widget;
  return 1;
}

//-----------------------------------------------------------------------------
int qtCMBArcWidgetManager::edit()
{
  emit this->Busy();
  if ( !this->Node && !this->Arc )
    {
    emit this->Ready();
    return 0;
    }
  if(!this->EditWidget)
    {
    this->EditWidget = new qtCMBArcEditWidget();
    QObject::connect(this->EditWidget,SIGNAL(
      arcModified(qtCMBArcWidget*, vtkIdType, vtkIdType)),
      this,SLOT(updateModifiedArc(qtCMBArcWidget*, vtkIdType, vtkIdType)));
    QObject::connect(this->EditWidget,SIGNAL(arcModificationfinished()),
      this,SLOT(editingFinished()));
    }
  pqCMBArc* arcObj = this->Arc;

  if(this->Node != NULL)
    {
    pqCMBSceneObjectBase* obj = this->Node->getDataObject();
    arcObj = dynamic_cast<pqCMBArc*>(obj);
    }

  this->EditWidget->setView(this->View);
  this->EditWidget->setArc(arcObj);
  this->EditWidget->setArcManager(this);
  this->ActiveWidget = this->EditWidget;

  return 1;
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::updateArcNode()
{
  if ( !this->Node && !this->Arc )
    {
    return;
    }

  //push the polydata from the widget representation to the poly source

  pqCMBArc* obj = (this->Node != NULL)?dynamic_cast<pqCMBArc*>
    (this->Node->getDataObject()):this->Arc;
  if ( obj )
    {
    vtkSMNewWidgetRepresentationProxy *widget = this->Widget->getWidgetProxy();

    //if the object hasn't been created yet update will call createArc
    //this way we don't have to check here
    QList<vtkIdType> newArcIds;
    vtkIdTypeArray *arcIdsFromSplit = vtkIdTypeArray::New();
    obj->updateArc(widget,arcIdsFromSplit);

    vtkIdType arcIdsSize = arcIdsFromSplit->GetNumberOfTuples();
    if(arcIdsSize > 0)
      {
      //convert this into a QList of vtkIdTypes so we can emit to the tree
      for(vtkIdType idx=0; idx < arcIdsSize; ++idx)
        {
        newArcIds.push_back(arcIdsFromSplit->GetValue(idx));
        }
      }
    arcIdsFromSplit->Delete();

    //make sure the model rep is visible, it would be hidden if we can from edit mode
    pqDataRepresentation *modelRep = obj->getRepresentation();
    if(modelRep)
      {
      modelRep->setVisible(true);
      }

    //pass onto the scene tree that this scene polyline is finished being editing
    //it needs the signal so that the tree can split the arcset into arcs.
    //Also this is need to make all the arc representation rerender to fix
    //any old end nodes hanging around
    if(this->Node != NULL)
      emit this->ArcSplit(this->Node,newArcIds);
    else
      emit this->ArcSplit2(this->Arc, newArcIds);
   }

  //update the object
  this->Widget->setWidgetVisible(false);
  this->Widget->setVisible(false);
  this->Widget->getWidgetProxy()->UpdatePropertyInformation();
  this->Widget->setView(NULL);
  this->ActiveWidget = NULL;

  this->Node = NULL;
  this->Arc = NULL;
  emit this->Ready();
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::editingFinished()
{
  this->ActiveWidget = NULL;
  this->Node = NULL;
  this->Arc = NULL;
  emit this->Ready();
  emit this->Finish();
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::updateModifiedArc(
  qtCMBArcWidget* subArcWidget, vtkIdType startPID, vtkIdType endPID)
{
  if ( (!this->Node && !this->Arc) || this->ActiveWidget != this->EditWidget)
    {
    return;
    }

  //push the polydata from the widget representation to the poly source
  pqCMBArc* obj = (this->Node != NULL)?dynamic_cast<pqCMBArc*>(this->Node->getDataObject()):this->Arc;
  if ( obj )
    {
    vtkSMNewWidgetRepresentationProxy *widget = subArcWidget->getWidgetProxy();

    //if the object hasn't been created yet update will call createArc
    //this way we don't have to check here
    QList<vtkIdType> newArcIds;
    vtkNew<vtkIdTypeArray> arcIdsFromSplit;
    //call the update arc operator
    vtkNew<vtkCMBSubArcModifyClientOperator> updateAndSplitOp;
    updateAndSplitOp->SetStartPointId(startPID);
    updateAndSplitOp->SetEndPointId(endPID);
    bool valid = updateAndSplitOp->Operate(obj->getArcId(),widget,
      vtkSMSourceProxy::SafeDownCast(obj->getSource()->getProxy()));
    if (!valid)
      {
      //we didn't update ourselves, most likely bad widget representation
      //ie. a representation that has 1 or 0 points
      return;
      }

    //copy the arc ids to create new arcs for
    arcIdsFromSplit->DeepCopy(updateAndSplitOp->GetCreatedArcs());

    vtkIdType arcIdsSize = arcIdsFromSplit->GetNumberOfTuples();
    if(arcIdsSize > 0)
      {
      //convert this into a QList of vtkIdTypes so we can emit to the tree
      for(vtkIdType idx=0; idx < arcIdsSize; ++idx)
        {
        newArcIds.push_back(arcIdsFromSplit->GetValue(idx));
        }
      }

    //make sure the model rep is visible, it would be hidden if we can from edit mode
    pqDataRepresentation *modelRep = obj->getRepresentation();
    if(modelRep)
      {
      modelRep->setVisible(true);
      }

    //pass onto the scene tree that this scene polyline is finished being editing
    //it needs the signal so that the tree can split the arcset into arcs.
    //Also this is need to make all the arc representation rerender to fix
    //any old end nodes hanging around
    if(this->Node != NULL) emit this->ArcSplit(this->Node,newArcIds);
    else emit this->ArcSplit2(this->Arc,newArcIds);
    }
}

//-----------------------------------------------------------------------------
qtCMBArcWidget* qtCMBArcWidgetManager::createDefaultContourWidget(
  int& normal, double& planePos)
{
  this->getDefaultArcPlane(normal, planePos);
  return this->createContourWidget(normal, planePos);
}

//-----------------------------------------------------------------------------
qtCMBArcWidget* qtCMBArcWidgetManager::createContourWidget(
   int normal, double position)
{
  vtkSMProxy* pointplacer = vtkSMProxyManager::GetProxyManager()->NewProxy(
    "point_placers", "BoundedPlanePointPlacer");

  qtCMBArcWidget *widget= new qtCMBArcWidget(
    pointplacer, pointplacer, NULL);

  vtkSMProxy* repProxy =
    widget->getWidgetProxy()->GetRepresentationProxy();
  widget->setObjectName("CmbSceneContourWidget");

  vtkSMPropertyHelper(pointplacer, "ProjectionNormal").Set(normal);
  vtkSMPropertyHelper(pointplacer, "ProjectionPosition").Set(position);
  widget->setLineInterpolator(0);
  widget->setPointPlacer(pointplacer);
  pointplacer->UpdateVTKObjects();
  pointplacer->Delete();

  vtkSMPropertyHelper(widget->getWidgetProxy(), "AlwaysOnTop").Set(1);


  widget->setView( this->View );

  //this block is needed to create the widget in the right order
  //we need to set on the proxy enabled, not the widget
  //than we need to call Initialize
  widget->setWidgetVisible( this->View != NULL );
  vtkSMPropertyHelper(widget->getWidgetProxy(), "Enabled").Set(1);
  widget->getWidgetProxy()->UpdateVTKObjects();
  //widget->getWidgetProxy()->GetWidget()->SetEnabled(true);
  widget->getWidgetProxy()->InvokeCommand("Initialize");

  return widget;
}

//-----------------------------------------------------------------------------
pqCMBArc* qtCMBArcWidgetManager::createLegacyV1Contour(
  const int &normal,const double &position,const int &closedLoop,
  vtkDoubleArray* nodePositions, vtkIdTypeArray* SelIndices)
{

  qtCMBArcWidget* contourWidget =
    this->createContourWidget(normal,position);

  vtkSMNewWidgetRepresentationProxy *widgetProxy =
    contourWidget->getWidgetProxy();

  if(nodePositions && nodePositions->GetNumberOfTuples() > 0)
    {
    QList<QVariant> values;
    double pointPos[3];
    for(vtkIdType i=0; i<nodePositions->GetNumberOfTuples(); i++)
      {
      nodePositions->GetTuple(i,pointPos);
      values << pointPos[0] << pointPos[1] << pointPos[2];
      }
    pqSMAdaptor::setMultipleElementProperty(
      widgetProxy->GetRepresentationProxy()->GetProperty("NodePositions"),
      values);
    }

  if ( SelIndices && SelIndices->GetNumberOfTuples() > 0 )
    {
    QList<QVariant> values;
    for(vtkIdType i=0; i<SelIndices->GetNumberOfTuples(); i++)
      {
      values << SelIndices->GetValue(i);
      }
    pqSMAdaptor::setMultipleElementProperty(
      widgetProxy->GetRepresentationProxy()->GetProperty("SelectNodes"),
      values);
    }

  //push all the node positions down to the server before
  //we call on close loop, or else close loop will fail
  widgetProxy->UpdateVTKObjects();

  if ( closedLoop )
    {
    widgetProxy->InvokeCommand("CloseLoop");
    }

  pqCMBArc *obj = this->createpqCMBArc();
  obj->createArc(widgetProxy);

  //obj->SetPlaneProjectionNormal(normal);
  //obj->SetPlaneProjectionPosition(position);

  //now we need to delete the widget
  delete contourWidget;

  return obj;
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::getDefaultArcPlane(
  int& orthoPlane, double& projpos)
{
  double focalPt[3], position[3], viewUp[3], viewDirection[3];
  double cameraDistance, parallelScale;
  pqCMBCommonMainWindowCore::getViewCameraInfo(
    this->View, focalPt, position, viewDirection, cameraDistance,
                      viewUp, parallelScale);
  projpos = 0;
  QList<QVariant> values =
    pqSMAdaptor::getMultipleElementProperty(
    this->View->getProxy()->GetProperty("CameraFocalPointInfo"));
  projpos = values[2].toDouble();
  orthoPlane = 2; // z axis
  if (viewDirection[0] < -.99 || viewDirection[0] > .99)
    {
    projpos = values[0].toDouble();
    orthoPlane = 0; // x axis
    }
  else if (viewDirection[1] < -.99 || viewDirection[1] > .99)
    {
    orthoPlane = 1; // y axis;
    projpos = values[1].toDouble();
    }
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::resetArcPlane(
  int normal, double planePos)
{
  vtkSMProxyProperty* proxyProp =
    vtkSMProxyProperty::SafeDownCast(
    this->Widget->getWidgetProxy()->GetProperty("PointPlacer"));
  if (proxyProp && proxyProp->GetNumberOfProxies())
    {
    vtkSMProxy* pointplacer = proxyProp->GetProxy(0);
    vtkSMPropertyHelper(pointplacer, "ProjectionNormal").Set(normal);
    vtkSMPropertyHelper(pointplacer, "ProjectionPosition").Set(planePos);
    pointplacer->MarkModified(pointplacer);
    pointplacer->UpdateVTKObjects();
    }
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::modifyArc(
  vtkIdType startPID, vtkIdType endPID, int opType)
{
  if ( (!this->Node && !this->Arc) || this->ActiveWidget != this->EditWidget)
    {
    return;
    }
  pqCMBArc* obj = (this->Node != NULL)?dynamic_cast<pqCMBArc*>(this->Node->getDataObject()):this->Arc;
  if ( obj )
    {
    //call the update arc operator
    vtkNew<vtkCMBSubArcModifyClientOperator> modifyOp;
    modifyOp->SetStartPointId(startPID);
    modifyOp->SetEndPointId(endPID);
    bool valid = modifyOp->Operate(obj->getArcId(),NULL,
      vtkSMSourceProxy::SafeDownCast(obj->getSource()->getProxy()),opType);
    if (!valid)
      {
      return;
      }

    //make sure the model rep is visible, it would be hidden if we can from edit mode
    pqDataRepresentation *modelRep = obj->getRepresentation();
    if(modelRep)
      {
      obj->updateRepresentation();
      modelRep->setVisible(true);
      }
    // if there are new arcs, emit proper signals
    if(opType == vtkCMBSubArcModifyClientOperator::OpMAKEARC)
      {
      QList<vtkIdType> newArcIds;
      vtkNew<vtkIdTypeArray> arcIdsFromSplit;
      //copy the arc ids to create new arcs for
      arcIdsFromSplit->DeepCopy(modifyOp->GetCreatedArcs());

      vtkIdType arcIdsSize = arcIdsFromSplit->GetNumberOfTuples();
      if(arcIdsSize > 0)
        {
        //convert this into a QList of vtkIdTypes so we can emit to the tree
        for(vtkIdType idx=0; idx < arcIdsSize; ++idx)
          {
          newArcIds.push_back(arcIdsFromSplit->GetValue(idx));
          }
        }
      //pass onto the scene tree that this scene polyline is finished being editing
      //it needs the signal so that the tree can split the arcset into arcs.
      //Also this is need to make all the arc representation rerender to fix
      //any old end nodes hanging around
      if(this->Node != NULL) emit this->ArcSplit(this->Node,newArcIds);
      else emit this->ArcSplit2(this->Arc,newArcIds);
      }
    else if(this->Node != NULL)
      {
      //this is required so that the arc can tell polygons that it has changed
      //so that those polygons can remesh
      emit this->ArcModified(this->Node);
      }
    else
      {
      emit this->ArcModified2(this->Arc);
      }
   }
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::straightenArc(vtkIdType startPID, vtkIdType endPID)
{
  this->modifyArc(startPID, endPID,
    vtkCMBSubArcModifyClientOperator::OpSTRAIGHTEN);
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::collapseSubArc(vtkIdType startPID, vtkIdType endPID)
{
  this->modifyArc(startPID, endPID,
    vtkCMBSubArcModifyClientOperator::OpCOLLAPSE);
}

//-----------------------------------------------------------------------------
void qtCMBArcWidgetManager::makeArc(vtkIdType startPID, vtkIdType endPID)
{
  this->modifyArc(startPID, endPID,
    vtkCMBSubArcModifyClientOperator::OpMAKEARC);
}
