//------------------------------------------------------------------------------
void vtkSlicerCollisionWarningLogic::RegisterNodes()
{
  if( ! this->GetMRMLScene() )
  {
    vtkWarningMacro( "MRML scene not yet created" );
    return;
  }

  this->GetMRMLScene()->RegisterNodeClass( vtkSmartPointer< vtkMRMLCollisionWarningNode >::New() );
}
void vtkOSMesaRenderWindow::MakeCurrent()
{
  TraceBegin("vtkOSMesaRenderWindow::MakeCurrent");

  if (this->Internal->OffScreenContextId) 
    {
    if (OSMesaMakeCurrent(this->Internal->OffScreenContextId, 
                          this->Internal->OffScreenWindow, GL_UNSIGNED_BYTE, 
                          this->Size[0], this->Size[1]) != GL_TRUE) 
      {
      vtkWarningMacro("failed call to OSMesaMakeCurrent");
      }
    }

  TraceEnd("vtkOSMesaRenderWindow::MakeCurrent");
}
void vtkSlicerCollisionWarningLogic::SetSecondModelNode( vtkMRMLModelNode* newModel, vtkMRMLCollisionWarningNode* moduleNode )
{
  if ( moduleNode == NULL )
  {
    vtkWarningMacro( "SetSecondModelNode: Module node is invalid" );
    return;
  }

  // Get the original color of the old model node
  vtkMRMLModelNode* previousModel=moduleNode->GetSecondModelNode();

  if (previousModel==newModel)
  {
    // no change
    return;
  }

  double previousOriginalColor[3]={0.5,0.5,0.5};
  if(previousModel)
  {
    moduleNode->GetOriginalColor(previousOriginalColor);
  }

  // Save the original color of the new model node
  if(newModel!=NULL)
  {
    double originalColor[3]={0.5,0.5,0.5};
    if ( newModel->GetDisplayNode() != NULL )
    {
      newModel->GetDisplayNode()->GetColor(originalColor);
    }
    moduleNode->SetOriginalColor(originalColor);
  }

  // Switch to the new model node
  moduleNode->SetAndObserveSecondModelNodeID( (newModel!=NULL) ? newModel->GetID() : NULL );

  // Restore the color of the old model node
  if(previousModel!=NULL && previousModel->GetDisplayNode()!=NULL)
  {
    previousModel->GetDisplayNode()->SetColor(previousOriginalColor[0],previousOriginalColor[1],previousOriginalColor[2]);
  }
}
//------------------------------------------------------------------------------
void vtkSlicerCollisionWarningLogic::OnMRMLSceneNodeRemoved( vtkMRMLNode* node )
{
  if ( node == NULL || this->GetMRMLScene() == NULL )
  {
    vtkWarningMacro( "OnMRMLSceneNodeRemoved: Invalid MRML scene or node" );
    return;
  }

  if ( node->IsA( "vtkMRMLCollisionWarningNode" ) )
  {
    vtkDebugMacro( "OnMRMLSceneNodeRemoved" );
    vtkUnObserveMRMLNodeMacro( node );
    for (std::deque< vtkWeakPointer< vtkMRMLCollisionWarningNode > >::iterator it=this->WarningSoundPlayingNodes.begin(); it!=this->WarningSoundPlayingNodes.end(); ++it)
    {
      if (it->GetPointer()==node)
      {
        this->WarningSoundPlayingNodes.erase(it);
        break;
      }
    }
    this->SetWarningSoundPlaying(!this->WarningSoundPlayingNodes.empty());
  }
}
//------------------------------------------------------------------------------
void vtkSlicerCollisionWarningLogic::OnMRMLSceneNodeAdded( vtkMRMLNode* node )
{
  if ( node == NULL || this->GetMRMLScene() == NULL )
  {
    vtkWarningMacro( "OnMRMLSceneNodeAdded: Invalid MRML scene or node" );
    return;
  }

  vtkMRMLCollisionWarningNode* bwNode = vtkMRMLCollisionWarningNode::SafeDownCast(node);
  if ( bwNode )
  {
    vtkDebugMacro( "OnMRMLSceneNodeAdded: Module node added." );
    vtkUnObserveMRMLNodeMacro( bwNode ); // Remove previous observers.
    vtkNew<vtkIntArray> events;
    events->InsertNextValue( vtkCommand::ModifiedEvent );
    events->InsertNextValue( vtkMRMLCollisionWarningNode::InputDataModifiedEvent );
    vtkObserveMRMLNodeEventsMacro( bwNode, events.GetPointer() );
    if(bwNode->GetPlayWarningSound() && bwNode->IsToolTipInsideModel())
    {
      // Add to list of playing nodes (if not there already)
      std::deque< vtkWeakPointer< vtkMRMLCollisionWarningNode > >::iterator foundPlayingNodeIt = this->WarningSoundPlayingNodes.begin();    
      for (; foundPlayingNodeIt!=this->WarningSoundPlayingNodes.end(); ++foundPlayingNodeIt)
      {
        if (foundPlayingNodeIt->GetPointer()==bwNode)
        {
          // found, current bw node is already in the playing list
          break;
        }
      }
      if (foundPlayingNodeIt==this->WarningSoundPlayingNodes.end())
      {
        this->WarningSoundPlayingNodes.push_back(bwNode);
      }
      this->SetWarningSoundPlaying(true);
    }
  }
}
void StreamTracer::SetIntegratorType(int type)
{
    vtkInitialValueProblemSolver* ivp=0;
    switch (type)
    {
    case RUNGE_KUTTA2:
        ivp = vtkRungeKutta2::New();
        break;
    case RUNGE_KUTTA4:
        ivp = vtkRungeKutta4::New();
        break;
    case RUNGE_KUTTA45:
        ivp = vtkRungeKutta45::New();
        break;
    default:
        vtkWarningMacro("Unrecognized integrator type. Keeping old one.");
        break;
    }
    if (ivp)
    {
        this->SetIntegrator(ivp);
        ivp->Delete();
    }
}
void vtkSlicerCollisionWarningLogic::UpdateToolState( vtkMRMLCollisionWarningNode* bwNode )
{
  if ( bwNode == NULL )
  {
    return;
  }

  vtkMRMLModelNode* modelNode = bwNode->GetWatchedModelNode();
  vtkMRMLTransformNode* toolToRasNode = bwNode->GetToolTransformNode();
  vtkMRMLModelNode* secondModelNode = bwNode->GetSecondModelNode();

  if ( modelNode == NULL || secondModelNode == NULL )
  {
    bwNode->SetClosestDistanceToModelFromToolTip(0);
    return;
  }

  //vtkCollisionDetectionFilter only accepts triangles, hope it works this way
  vtkSmartPointer<vtkTriangleFilter> triangleFilter0 = vtkSmartPointer<vtkTriangleFilter>::New();
  vtkSmartPointer<vtkTriangleFilter> triangleFilter1 = vtkSmartPointer<vtkTriangleFilter>::New();
  triangleFilter0->SetInputConnection(modelNode->GetPolyDataConnection());
  triangleFilter1->SetInputConnection(secondModelNode->GetPolyDataConnection());
  //triangleFilters are set

  vtkPolyData* body = triangleFilter0->GetOutput();
  if ( body == NULL )
  {
    vtkWarningMacro( "No surface model in first node" );
    return;
  }

  vtkPolyData* secondBody = triangleFilter1->GetOutput();
  if ( secondBody == NULL )
  {
    vtkWarningMacro( "No surface model in second node" );
    return;
  }

  vtkSmartPointer< vtkCollisionDetectionFilter> collisionDetectionFilter = vtkSmartPointer< vtkCollisionDetectionFilter>::New();
  collisionDetectionFilter->SetCollisionModeToFirstContact(); // should be faster

// start transform first model
  // Transform the first body poly data if there is a parent transform.
  vtkMRMLTransformNode* bodyParentTransform = modelNode->GetParentTransformNode();
  if ( bodyParentTransform != NULL )
  {
    vtkSmartPointer< vtkGeneralTransform > bodyToRasTransform = vtkSmartPointer< vtkGeneralTransform >::New();
    bodyParentTransform->GetTransformToWorld( bodyToRasTransform );

    vtkSmartPointer< vtkTransformPolyDataFilter > bodyToRasFilter = vtkSmartPointer< vtkTransformPolyDataFilter >::New();
#if (VTK_MAJOR_VERSION <= 5)
    bodyToRasFilter->SetInput( body );
#else
    bodyToRasFilter->SetInputData( body );
#endif
    bodyToRasFilter->SetTransform( bodyToRasTransform );
    bodyToRasFilter->Update();

    collisionDetectionFilter->SetInput(0, bodyToRasFilter->GetOutput() );
  }
  else
  {
    collisionDetectionFilter->SetInput(0, body ); // no idea how expensive
  } // don't care about expensive now, jut want this to work


// second model ----------------------------------
  vtkMRMLTransformNode* secondBodyParentTransform = secondModelNode->GetParentTransformNode();
  if ( secondBodyParentTransform != NULL )
  {
    vtkSmartPointer< vtkGeneralTransform > secondBodyToRasTransform = vtkSmartPointer< vtkGeneralTransform >::New();
    secondBodyParentTransform->GetTransformToWorld( secondBodyToRasTransform );

    vtkSmartPointer< vtkTransformPolyDataFilter > secondBodyToRasFilter = vtkSmartPointer< vtkTransformPolyDataFilter >::New();
#if (VTK_MAJOR_VERSION <= 5)
    secondBodyToRasFilter->SetInput( secondBody );
#else
    secondBodyToRasFilter->SetInputData( secondBody );
#endif
    secondBodyToRasFilter->SetTransform( secondBodyToRasTransform );
    secondBodyToRasFilter->Update();

    collisionDetectionFilter->SetInput(0, secondBodyToRasFilter->GetOutput() );
  }
  else
  {
    collisionDetectionFilter->SetInput(0, secondBody ); // no idea how expensive
  }
// end transform second model
  vtkSmartPointer<vtkMatrix4x4> matrix0 = vtkSmartPointer<vtkMatrix4x4>::New();
  vtkSmartPointer<vtkMatrix4x4> matrix1 = vtkSmartPointer<vtkMatrix4x4>::New();
  collisionDetectionFilter->SetMatrix(0, matrix0);
  collisionDetectionFilter->SetMatrix(1, matrix1);
  collisionDetectionFilter->GenerateScalarsOff();

  if (collisionDetectionFilter->GetNumberOfContacts() > 0)
  {
     bwNode->SetCollision(true);
  }
     else
  {
     bwNode->SetCollision(false);
  }



  // Note: Performance could be improved by
  // - keeping the filter in memory for all the observed nodes.
  // - in case of linear transform of model and tooltip: transform only the tooltip (with the tooltip to model transform),
  //   and not transform the model at all
  //   --> not valid for collision detection



}
int vtkFractureTrace::RequestData(vtkInformation *request,
                                  vtkInformationVector **inputVector, vtkInformationVector *outputVector)
{
    // get the info objects
    vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
    vtkInformation *outInfo = outputVector->GetInformationObject(0);

    // get the input and ouptut
    this->input = vtkPolyData::SafeDownCast(
                      inInfo->Get(vtkDataObject::DATA_OBJECT()));
    this->output = vtkPolyData::SafeDownCast(
                       outInfo->Get(vtkDataObject::DATA_OBJECT()));

    vtkCellArray* inCells = this->input->GetPolys();
    inCells->InitTraversal();
    vtkIdType nCells = inCells->GetNumberOfCells();

    inPoints = input->GetPoints();
    vtkIdType nPoints = inPoints->GetNumberOfPoints();

    this->append = vtkAppendPolyData::New();

    vtkIntArray* isNormal = vtkIntArray::New();
    isNormal->SetName("Normal(0)_Dip(1)_Intersection(2)");

    vtkIdType npts;
    vtkIdType* pts;
    for(vtkIdType i=0; i<nCells; ++i)
    {
        inCells->GetNextCell(npts, pts);
        if(npts < 3)
        {
            vtkWarningMacro("The cells should at least have three points");
            continue;
        }
        double pt1[3], pt2[3], pt3[3];
        inPoints->GetPoint(pts[0], pt1);
        inPoints->GetPoint(pts[1], pt2);
        inPoints->GetPoint(pts[2], pt3);

        double v1[] = {pt2[0]-pt1[0], pt2[1]-pt1[1], pt2[2]-pt1[2]};
        double v2[] = {pt3[0]-pt1[0], pt3[1]-pt1[1], pt3[2]-pt1[2]};
        double N[3];
        vtkMath::Cross(v1, v2, N);
        vtkMath::Normalize(N);

        double center[3];
        this->computeFractureCenter(npts, pts, center);
        double length = this->computeNormalLength(npts, pts);

        // projection of [0,0,-1] on the plane with normal V
        double dip[] = {N[0]*N[2], N[1]*N[2], -1.0 + N[2]*N[2]};
        vtkMath::Normalize(dip);

        int n = this->addArrow(center, N, length, 0);
        //for(int count=0; count<n; ++count) isNormal->InsertNextValue(0);

        n = this->addArrow(center, dip, length, 1);
        //for(int count=0; count<n; ++count) isNormal->InsertNextValue(1);

        vtkIdType traversal = inCells->GetTraversalLocation();
        for(vtkIdType j=i+1; j<nCells; ++j)
        {
            vtkIdType npts2;
            vtkIdType* pts2;
            inCells->GetNextCell(npts2, pts2);
            if(npts2 < 3)
                continue;

            double p1[3], p2[3], p3[3];
            inPoints->GetPoint(pts2[0], p1);
            inPoints->GetPoint(pts2[1], p2);
            inPoints->GetPoint(pts2[2], p3);

            double v1[] = {p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]};
            double v2[] = {p3[0]-p1[0], p3[1]-p1[1], p3[2]-p1[2]};
            double N2[3];
            vtkMath::Cross(v1, v2, N2);
            vtkMath::Normalize(N2);

            double V[3];
            vtkMath::Cross(N, N2, V); // the direction of the line
            double norm = vtkMath::Normalize(V);

            // check if the two fracture are parallel
            if(norm == 0)
                continue;

            // perform bounding box intersection first

            double s1, s2, a, b;

            // ax + by + cz + s = 0
            s1 = (N[0]*pt1[0] + N[1]*pt1[1] + N[2]*pt1[2]);
            s2 = (N2[0]*p1[0] + N2[1]*p1[1] + N2[2]*p1[2]);

            double n1n2dot = vtkMath::Dot(N, N2);
            double n1normsqr = 1.;//vtkMath::Dot(N,N);
            double n2normsqr = 1.;//vtkMath::Dot(N2, N2);

            a = (s2 * n1n2dot - s1 * n2normsqr) / (n1n2dot*n1n2dot - n1normsqr * n2normsqr);
            b = (s1 * n1n2dot - s2 * n2normsqr) / (n1n2dot*n1n2dot - n1normsqr * n2normsqr);

            double lPoint[3]; // the point on the line
            lPoint[0] = a*N[0] + b*N2[0];
            lPoint[1] = a*N[1] + b*N2[1];
            lPoint[2] = a*N[2] + b*N2[2];

            double lPoint2[] = {lPoint[0]+V[0], lPoint[1]+V[1], lPoint[2]+V[2]};

            double t1, t2, t3, t4;
            if(!this->intersectWithCell(lPoint, V, npts, pts, t1, t2)) continue;
            if(!this->intersectWithCell(lPoint, V, npts2, pts2, t3, t4)) continue;

            double tLeft = std::max(t1, t3);
            double tRight = std::min(t2, t4);
            if(tLeft >= tRight)
                continue;

            p1[0] = lPoint[0] + V[0]*tLeft;
            p1[1] = lPoint[1] + V[1]*tLeft;
            p1[2] = lPoint[2] + V[2]*tLeft;

            p2[0] = lPoint[0] + V[0]*tRight;
            p2[1] = lPoint[1] + V[1]*tRight;
            p2[2] = lPoint[2] + V[2]*tRight;

            vtkLineSource *line = vtkLineSource::New();
            line->SetPoint1(p1);
            line->SetPoint2(p2);
            line->SetResolution(2);
            line->Update();
            vtkIntArray* props = vtkIntArray::New();
            props->SetName("Normal(0)_Dip(1)_Intersection(2)");
            props->InsertNextValue(2);
            line->GetOutput()->GetCellData()->AddArray(props);
            append->AddInput(line->GetOutput());
            line->Delete();
        }
        inCells->SetTraversalLocation(traversal);
    }

    this->append->Update();
    this->output->DeepCopy(append->GetOutput());
    this->append->Delete();
    return 1;
}