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

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm 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 "vtkVVLesionSizingInterface.h"

#include "vtkObjectFactory.h"
#include "vtkCommand.h"
#include "vtkSmartPointer.h"
#include "vtkImageData.h"
#include "vtkRenderWindow.h"
#include "vtkMedicalImageProperties.h"

#include "vtkKWEntry.h"
#include "vtkKWEntryWithLabel.h"
#include "vtkKWEvent.h"
#include "vtkKWFrame.h"
#include "vtkKWFrameWithLabel.h"
#include "vtkKWIcon.h"
#include "vtkKWImageWidget.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLabel.h"
#include "vtkKWLabelWithLabel.h"
#include "vtkKWLabelWithLabelSet.h"
#include "vtkKWMenu.h"
#include "vtkKWMenuButton.h"
#include "vtkKWMenuButtonWithLabel.h"
#include "vtkKWMessage.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWMessageWithLabel.h"
#include "vtkKWProbeImageWidget.h"
#include "vtkKWProgressGauge.h"
#include "vtkKWPushButton.h"
#include "vtkKWRadioButton.h"
#include "vtkKWRadioButtonSet.h"
#include "vtkKWSeparator.h"
#include "vtkKWToolbar.h"
#include "vtkKWVolumeWidget.h"

#include "vtkVVContourSelector.h"
#include "vtkVVWindow.h"
#include "vtkVVDataItemPool.h"
#include "vtkVVDataItemVolume.h"
#include "vtkVVHandleWidget.h"
#include "vtkVVApplication.h"
#include "vtkVVSelectionFrame.h"
#include "vtkVVSelectionFrameLayoutManager.h"
#include "vtkVVDataItemVolumeContour.h"
#include "vtkVVDataItemVolumeContourCollection.h"

#include "vtkITKLesionSegmentationImageFilter4.h"
#include "vtkITKLesionSegmentationImageFilter7.h"
#include "vtkITKLesionSegmentationImageFilter8.h"

#include <vtksys/SystemTools.hxx>
#include <vtkstd/vector>
#include <sstream>
#include <assert.h>

#include "../Resources/vtkVVResources.h"

#ifndef max
#define max(x,y) ((x>y) ? (x) : (y))
#endif
#ifndef min 
#define min(x,y) ((x<y) ? (x) : (y))
#endif

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVLesionSizingInterface);
vtkCxxRevisionMacro(vtkVVLesionSizingInterface, "$Revision: 1.5 $");

//---------------------------------------------------------------------------
vtkVVLesionSizingInterface::vtkVVLesionSizingInterface()
{
  this->SetName("CT Lung Lesion Sizing");

  this->ContourSelector = NULL;
  this->LesionSizingFrame = NULL;
  this->ContourListFrame = NULL;
  this->InstructionsFrame = NULL;
  this->OptionsFrame = NULL;
  this->NextButton = NULL;
  this->StartButton = NULL;
  this->ButtonFrame = 0;
  this->CancelButton = 0;
  this->InstructionsMessage = NULL;
  this->Sep1 = NULL;
  this->OptionsRadioButtonSet = NULL;
  this->SegmentationAlgorithmRadioButtonSet = NULL;
  this->AdvancedOptionsFrame = NULL;
  this->State = vtkVVLesionSizingInterface::Start;
  this->LesionSegmentationFilter = NULL;
}

//---------------------------------------------------------------------------
vtkVVLesionSizingInterface::~vtkVVLesionSizingInterface()
{
  if (this->ContourSelector)
    {
    this->ContourSelector->Delete();
    this->ContourSelector = NULL;
    }
  if (this->LesionSizingFrame)
    {
    this->LesionSizingFrame->Delete();
    this->LesionSizingFrame = NULL;
    }
  if (this->ContourListFrame)
    {
    this->ContourListFrame->Delete();
    this->ContourListFrame = NULL;
    }
  if (this->AdvancedOptionsFrame)
    {
    this->AdvancedOptionsFrame->Delete();
    this->AdvancedOptionsFrame = NULL;
    }
  if (this->InstructionsFrame)
    {
    this->InstructionsFrame->Delete();
    this->InstructionsFrame = NULL;
    }
  if (this->InstructionsMessage)
    {
    this->InstructionsMessage->Delete();
    this->InstructionsMessage = NULL;
    }
  if (this->OptionsFrame)
    {
    this->OptionsFrame->Delete();
    this->OptionsFrame = NULL;
    }
  if (this->StartButton)
    {
    this->StartButton->Delete();
    this->StartButton = 0;
    }

  if (this->NextButton)
    {
    this->NextButton->Delete();
    this->NextButton = 0;
    }

  if (this->CancelButton)
    {
    this->CancelButton->Delete();
    this->CancelButton = 0;
    }

  if (this->ButtonFrame)
    {
    this->ButtonFrame->Delete();
    this->ButtonFrame = 0;
    }

  if (this->Sep1)
    {
    this->Sep1->Delete();
    this->Sep1 = 0;
    }
  if (this->OptionsRadioButtonSet)
    {
    this->OptionsRadioButtonSet->Delete();
    this->OptionsRadioButtonSet = 0;
    }
  if (this->SegmentationAlgorithmRadioButtonSet)
    {
    this->SegmentationAlgorithmRadioButtonSet->Delete();
    this->SegmentationAlgorithmRadioButtonSet = 0;
    }
}

// --------------------------------------------------------------------------
void vtkVVLesionSizingInterface::Create()
{
  if (this->IsCreated())
    {
    vtkErrorMacro("The panel is already created.");
    return;
    }

  // Create the superclass instance (and set the application)

  this->Superclass::Create();

  // Add page

  vtkKWIcon *icon = vtkKWIcon::New();
  icon->SetImage(image_VVLung,
                 image_VVLung_width, image_VVLung_height,
                 image_VVLung_pixel_size,
                 image_VVLung_length);

  ostrstream tk_cmd;
  vtkKWWidget *page;
  int page_id = this->AddPage(NULL, this->GetName());
  this->SetPageIcon(page_id, icon);

  page = this->GetPageWidget(page_id);

  icon->Delete();

  // Lesion sizing frame
  if (!this->LesionSizingFrame)
    {
    this->LesionSizingFrame = vtkKWFrameWithLabel::New();
    }
  this->LesionSizingFrame->SetParent(this->GetPagesParentWidget());
  this->LesionSizingFrame->Create();
  this->LesionSizingFrame->SetLabelText("CT Lung lesion Segmentation");
  this->LesionSizingFrame->SetPadX(2);
  this->LesionSizingFrame->SetPadY(2);

  tk_cmd << "place " << this->LesionSizingFrame->GetWidgetName()
         << " -relx 0 -rely 0 -relwidth 1.0 -relheight 0.35 " 
         << " -in " << page->GetWidgetName() << endl;

  vtkKWFrame *lesion_sizing_frame = this->LesionSizingFrame->GetFrame();

  this->InstructionsFrame = vtkKWFrame::New();
  this->InstructionsFrame->SetParent(lesion_sizing_frame);
  this->InstructionsFrame->Create();
  this->InstructionsFrame->SetBorderWidth(0);
  tk_cmd << "pack " << this->InstructionsFrame->GetWidgetName() 
         << " -side top -fill both -expand y -padx 2" << endl;

  this->InstructionsMessage = vtkKWLabelWithLabel::New();
  this->InstructionsMessage->SetParent(this->InstructionsFrame);
  this->InstructionsMessage->Create();
  this->InstructionsMessage->GetLabel()->SetImageToPredefinedIcon(
    vtkKWIcon::IconSilkHelp);
  this->InstructionsMessage->ExpandWidgetOn();
  this->InstructionsMessage->GetWidget()->AdjustWrapLengthToWidthOn();
  tk_cmd << "pack " << this->InstructionsMessage->GetWidgetName() 
         << " -side top -anchor nw -fill x -expand y" << endl;

  // Options if any

  this->OptionsFrame = vtkKWFrame::New();
  this->OptionsFrame->SetParent(lesion_sizing_frame);
  this->OptionsFrame->Create();
  this->OptionsFrame->SetBorderWidth(0);
  tk_cmd << "pack " << this->OptionsFrame->GetWidgetName() 
         << " -side top " << endl;

  // Segmentation options - Solid / Part-solid 
  
  this->OptionsRadioButtonSet = vtkKWRadioButtonSet::New();
  this->OptionsRadioButtonSet->SetParent(this->OptionsFrame);
  this->OptionsRadioButtonSet->Create();
  this->OptionsRadioButtonSet->SetBorderWidth(0);
  this->OptionsRadioButtonSet->SetReliefToGroove();
  this->OptionsRadioButtonSet->SetWidgetsPadY(0);
  vtkKWRadioButton *rb1 = this->OptionsRadioButtonSet->AddWidget(0);
  rb1->SetText("Solid");
  rb1->SetBorderWidth(0);
  vtkKWRadioButton *rb2 = this->OptionsRadioButtonSet->AddWidget(1);
  rb2->SetText("Part solid");
  rb2->SetBorderWidth(0);
  rb1->SetSelectedState(1);
  tk_cmd << "grid " << this->OptionsRadioButtonSet->GetWidgetName() 
         << " -row 0 -column 0 -sticky nsew -padx 14 -pady 4" << endl;
  

  // Advanced Options if any

  this->AdvancedOptionsFrame = vtkKWFrameWithLabel::New();
  this->AdvancedOptionsFrame->SetParent(lesion_sizing_frame);
  this->AdvancedOptionsFrame->SetLabelText("Advanced options");
  this->AdvancedOptionsFrame->Create();
  this->AdvancedOptionsFrame->SetBorderWidth(0);
  //tk_cmd << "pack " << this->AdvancedOptionsFrame->GetWidgetName() 
  //<< " -side top " << endl;

  // Advanced Segmentation options - algorithm

  this->SegmentationAlgorithmRadioButtonSet = vtkKWRadioButtonSet::New();
  this->SegmentationAlgorithmRadioButtonSet->SetParent(
    this->AdvancedOptionsFrame->GetFrame());
  this->SegmentationAlgorithmRadioButtonSet->Create();
  this->SegmentationAlgorithmRadioButtonSet->SetBorderWidth(0);
  this->SegmentationAlgorithmRadioButtonSet->SetReliefToGroove();
  this->SegmentationAlgorithmRadioButtonSet->SetWidgetsPadY(0);
  vtkKWRadioButton *rbs1 = this->SegmentationAlgorithmRadioButtonSet->AddWidget(0);
  rbs1->SetText("Fast Marching level set");
  rbs1->SetBorderWidth(0);
  vtkKWRadioButton *rbs2 = this->SegmentationAlgorithmRadioButtonSet->AddWidget(1);
  rbs2->SetText("Fast Marching followed by a refinement\nwith Geodesic Active Contour level set");
  rbs2->SetBorderWidth(0);
  vtkKWRadioButton *rbs3 = this->SegmentationAlgorithmRadioButtonSet->AddWidget(2);
  rbs3->SetText("Fast Marching followed by a refinement\nwith level sets, guided by canny, lung\nwall, vesselness and intensity feature");
  rbs3->SetBorderWidth(0);
  rbs3->SetSelectedState(1);
  tk_cmd << "grid " << this->SegmentationAlgorithmRadioButtonSet->GetWidgetName() 
         << " -row 0 -column 0 -sticky nsew -padx 6 -pady 4" << endl;  

  this->AdvancedOptionsFrame->CollapseFrame(); // collapse the advanced options 
  
  // Button frame is at the bottom of the interface

  this->ButtonFrame = vtkKWFrame::New();
  this->ButtonFrame->SetParent(lesion_sizing_frame);
  this->ButtonFrame->Create();
  this->ButtonFrame->SetBorderWidth(0);
  tk_cmd << "pack " << this->ButtonFrame->GetWidgetName() 
         << " -side bottom -fill x -padx 2" << endl;

  this->StartButton = vtkKWPushButton::New();
  this->StartButton->SetParent(this->ButtonFrame);
  vtkstd::string start(ks_("Wizard|Button|Start"));
  this->StartButton->SetText(start.c_str());
  this->StartButton->Create();
  this->StartButton->SetWidth(8);
  this->StartButton->SetBalloonHelpString(
    "Start a new segmentation, or restart an existing segmentation.");
  this->StartButton->SetCommand(this,"StartCallback");

  this->NextButton = vtkKWPushButton::New();
  this->NextButton->SetParent(this->ButtonFrame);
  vtkstd::string next(ks_("Wizard|Button|Next"));
  next += " >";
  this->NextButton->SetText(next.c_str());
  this->NextButton->Create();
  this->NextButton->SetWidth(8);
  this->NextButton->SetCommand(this,"NextCallback");

  this->CancelButton = vtkKWPushButton::New();
  this->CancelButton->SetParent(this->ButtonFrame);
  this->CancelButton->SetText(ks_("Wizard|Button|Cancel"));
  this->CancelButton->Create();
  this->CancelButton->SetWidth(8);
  this->CancelButton->SetBalloonHelpString(
    "Cancel execution of the current segmentation.");
  this->CancelButton->SetCommand(this,"CancelCallback");
  
  tk_cmd << "pack " << this->StartButton->GetWidgetName() 
         << " -side left -padx 0 -pady 6" << endl;
  tk_cmd << "pack " << this->NextButton->GetWidgetName() 
         << " -side left -pady 6" << endl;
  tk_cmd << "pack " << this->CancelButton->GetWidgetName() 
         << " -side right -padx 0 -pady 6" << endl;

  // Seperator before the buttons for good look and feel

  this->Sep1 = vtkKWSeparator::New();
  this->Sep1->SetParent(lesion_sizing_frame);
  this->Sep1->Create();
  this->Sep1->SetHeight(2);
  this->Sep1->SetBorderWidth(2);
  this->Sep1->SetReliefToGroove();

  tk_cmd << "pack " << this->Sep1->GetWidgetName() 
         << " -side bottom -fill x -padx 2" << endl;

  // The list of contours are populated on this frame

  if (!this->ContourListFrame)
    {
    this->ContourListFrame = vtkKWFrameWithLabel::New();
    }
  this->ContourListFrame->SetParent(this->GetPagesParentWidget());
  this->ContourListFrame->Create();
  this->ContourListFrame->SetLabelText("Segmentations");
  this->ContourListFrame->SetPadX(2);
  this->ContourListFrame->SetPadY(2);

  tk_cmd << "place " << this->ContourListFrame->GetWidgetName()
         << " -relx 0 -rely 0.35 -relwidth 1.0 -relheight 0.65 " 
         << " -in " << page->GetWidgetName() << endl;
  if (!this->ContourSelector)
    {
    this->ContourSelector = vtkVVContourSelector::New();
    }

  this->ContourSelector->SetParent(this->ContourListFrame->GetFrame());
  this->ContourSelector->Create();
  this->ContourSelector->SetListHeight(7);

  tk_cmd << "pack " << this->ContourSelector->GetWidgetName()
         << " -side top -anchor nw -fill both -expand t" << endl;

  // --------------------------------------------------------------
  // Pack 

  tk_cmd << ends;
  this->Script(tk_cmd.str());
  tk_cmd.rdbuf()->freeze(0);

  this->AddCallbackCommandObservers(); // add callback command observers

  // Update according to the current Window

  this->Update();
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::Update()
{
  this->Superclass::Update();
  this->UpdateInternal();

  if (this->Window && this->ContourSelector)
    {
    vtkVVDataItemVolume *v = vtkVVDataItemVolume::SafeDownCast(
      this->Window->GetSelectedDataItem() );
    this->ContourSelector->PopulatePresets(v);
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::StartCallback()
{
  if (vtkVVDataItemVolume *v = vtkVVDataItemVolume::SafeDownCast(
                            this->Window->GetSelectedDataItem() ))
    {
    vtkImageData *image = v->GetImageData();
    double spacing[3];
    image->GetSpacing(spacing);
    if (max(max(spacing[0], spacing[1]), spacing[2]) / 
          min(min(spacing[0], spacing[1]), spacing[2]) >= 3.0)
      {
      vtksys_stl::ostringstream os;
      os << "This dataset has a spacing of (" << spacing[0] << ","
         << spacing[1] << "," << spacing[2] << ") Your segmentation may"
         << " not be accurate." << ends;

      vtkKWMessageDialog *dialog = vtkKWMessageDialog::New();
      dialog->SetTitle("Warning");
      dialog->SetApplication(this->GetApplication());
      dialog->SetMasterWindow(this->GetWindow());
      dialog->Create();
      dialog->SetText(os.str().c_str());
      dialog->Invoke();
      dialog->Delete();
      }

    this->State = vtkVVLesionSizingInterface::DefiningBoundingBox;
    this->UpdateInternal();
    this->UpdateEnableState();
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::NextCallback()
{
  if (this->State == vtkVVLesionSizingInterface::DefiningBoundingBox)
    {
    this->State = vtkVVLesionSizingInterface::DefiningSeeds;
    }

  else if (this->State == vtkVVLesionSizingInterface::DefiningSeeds)
    {
    int nb_of_markers = vtkVVHandleWidget::GetNumberOfHandlesInDataItem(
          this->Window->GetSelectedDataItem());
    if (nb_of_markers == 0)
      {
      this->DisplayErrorMessage("You must place at least one seed within the lesion.");
      return;
      }

    this->State = vtkVVLesionSizingInterface::Segmenting;
    }

  else if (this->State == vtkVVLesionSizingInterface::Segmenting)
    {
    this->Segment();
    this->State = vtkVVLesionSizingInterface::Segmented;
    }

  this->UpdateInternal();
  this->UpdateEnableState();
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::CancelCallback()
{
  if (this->State != vtkVVLesionSizingInterface::Cancelling)
    {
    if (this->State == Segmenting && this->LesionSegmentationFilter)
      {
      // If we are running the segmentation, abort it
      // Set our state to cancel. The next time the filter reports progress 
      // updates, we'll abort it and reset the state.
      this->State = vtkVVLesionSizingInterface::Cancelling;
      }
    else
      {
      // If we are defining the seeds or the bounding box, abort and go back
      // to start. Nothing more to abort.
      this->State = vtkVVLesionSizingInterface::Start;
      }

    this->UpdateInternal();
    this->UpdateEnableState();    
    }
}

//---------------------------------------------------------------------------
int vtkVVLesionSizingInterface::DisplayErrorMessage( const char * text )
{
  vtkKWMessageDialog *dialog = vtkKWMessageDialog::New();
  dialog->SetTitle("User Error");
  dialog->SetApplication(this->GetApplication());
  dialog->SetMasterWindow(this->GetApplication()->GetNthWindow(0));
  dialog->SetOptions(vtkKWMessageDialog::Beep);
  dialog->Create();
  dialog->SetText(text);
  int ret = dialog->Invoke();
  dialog->Delete();
  return ret;
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::UpdateInternal()
{
  if (this->IsCreated())
    {
    if (this->State == vtkVVLesionSizingInterface::Start)
      {
      this->SetInstructionsText(
        "This module segments solid and part-solid lesions in the lung from CT datasets. You may load some 3D data and press Start to begin." ); 
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconSilkHelp);
      }
    else if (this->State == vtkVVLesionSizingInterface::DefiningBoundingBox)
      {
      this->SetInstructionsText(
        "Use the cropping planes from the toolbar to define a bounding box around the lesion to be segmented. Press Next when done. You may also skip this step. However the segmentation will take a long time and will be prone to leaks." ); 
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconCropTool);
      }
    else if (this->State == vtkVVLesionSizingInterface::DefiningSeeds)
      {
      this->SetInstructionsText(
        "Place one or more seeds within the lesion. Press Next when done." ); 
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconSeedTool);
      }
    else if (this->State == vtkVVLesionSizingInterface::Segmenting)
      {
      this->SetInstructionsText(
        "Select the lesion type (solid or part-solid). In the case of part-solid lesions, two segmentations will be produced, one representing the solid core and another including the part-solid regions. Press Next to run the segmentation algorithm." ); 
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconSilkHelp);
      }
    else if (this->State == vtkVVLesionSizingInterface::Cancelling)
      {
      this->Window->GetProgressGauge()->SetValue(0);
      this->SetInstructionsText("Cancelling. Please wait" ); 
      this->Window->SetStatusText("Cancelling execution..");
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconSilkCancel);
      }
    else
      {
      this->Window->GetProgressGauge()->SetValue(0);
      this->SetInstructionsText("Done! Press Start for a new segmentation." ); 
      this->SetInstructionsIconToPredefinedIcon(vtkKWIcon::IconSilkAccept);
      }

    this->PopulateOptions();
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::SetInstructionsText( const char * str )
{
  if (this->InstructionsMessage)
    {
    this->InstructionsMessage->GetWidget()->SetText(str);
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::SetInstructionsIconToPredefinedIcon(
  int icon_index)
{
  if (this->InstructionsMessage)
    {
    this->InstructionsMessage->GetLabel()->SetImageToPredefinedIcon(
      icon_index);
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::UpdateEnableState()
{
  this->Superclass::UpdateEnableState();

  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(data);
  vtkImageData *img_data = volume_data ? volume_data->GetImageData() : NULL;
  int *dim = img_data ? img_data->GetDimensions() : NULL;
  vtkMedicalImageProperties *med_prop = 
    volume_data ? volume_data->GetMedicalImageProperties() : NULL;

  int enabled = this->GetEnabled();
  if (!img_data ||
      dim[2] == 1 ||
      (med_prop && med_prop->GetModality() && strcmp(med_prop->GetModality(), 
                                                     "CT"))
    )
    {
    enabled = 0;
    }

  if (this->LesionSizingFrame)
    {
    this->LesionSizingFrame->SetEnabled(enabled);
    }

  if (this->ContourListFrame)
    {
    this->ContourListFrame->SetEnabled(enabled);
    }

  if (this->InstructionsMessage)
    {
    this->InstructionsMessage->SetEnabled(enabled);
    }

  if (this->AdvancedOptionsFrame)
    {
    this->AdvancedOptionsFrame->SetEnabled(enabled);
    }
  
  if (this->OptionsRadioButtonSet)
    {
    this->OptionsRadioButtonSet->SetEnabled(enabled);
    }
  
  if (this->SegmentationAlgorithmRadioButtonSet)
    {
    this->SegmentationAlgorithmRadioButtonSet->SetEnabled(enabled);
    }
  
  if (this->ContourSelector)
    {
    this->ContourSelector->SetEnabled(enabled);
    }
  
  // Enable the state buttons depending on the current state

  if (this->IsCreated())
    {
    if (!this->IsSupported(this->Window->GetSelectedDataItem()))
      {
      this->StartButton->SetEnabled(0);
      this->NextButton->SetEnabled(0);
      this->CancelButton->SetEnabled(0);
      }
    else if (this->State == vtkVVLesionSizingInterface::Start ||
        this->State == vtkVVLesionSizingInterface::Segmented)
      {
      this->StartButton->SetEnabled(enabled);
      this->NextButton->SetEnabled(0);
      this->CancelButton->SetEnabled(0);
      }
    else if (this->State > vtkVVLesionSizingInterface::Start &&
             this->State <= vtkVVLesionSizingInterface::Segmenting)
      {
      this->StartButton->SetEnabled(0);
      this->NextButton->SetEnabled(enabled);
      this->CancelButton->SetEnabled(enabled);
      }
    }

}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::Segment()
{
  // It seems the grab has no impact on the menubar, so try to disable
  // it manually
  if (this->Window)
    {
    this->Window->GetMenu()->SetEnabled(0);
    }
  this->CancelButton->Grab();

  vtkVVDataItemVolume *v = vtkVVDataItemVolume::SafeDownCast(
    this->Window->GetSelectedDataItem() );

  // Populate the right segmentation parameters
  const bool ispartsolid = 
    this->OptionsRadioButtonSet->GetWidget(1)->GetSelectedState();

  this->CreateNewLesionSegmentationStrategy( false );
  this->Segment(v);
  this->LesionSegmentationFilter->Delete();
  this->LesionSegmentationFilter = NULL;

  if (ispartsolid)
    {
    this->CreateNewLesionSegmentationStrategy( true );
    this->Segment(v);
    this->LesionSegmentationFilter->Delete();
    this->LesionSegmentationFilter = NULL;
    }

  this->ContourSelector->PopulatePresets(v);

  // Release grab
  this->CancelButton->ReleaseGrab();
  if (this->Window)
    {
    this->Window->UpdateMenuState();
    }  
}

//---------------------------------------------------------------------------
vtkVVDataItemVolumeContour * vtkVVLesionSizingInterface
::Segment( vtkVVDataItemVolume * data )
{
  typedef vtkITKLesionSegmentationImageFilterBase::RegionType RegionType;

  // Get the image data on which we will run the segmentation.

  vtkImageData *image = data->GetImageData();
  this->LesionSegmentationFilter->SetInput(image);

  // Observe the lesion segmentation filter for progress reporting

  this->AddCallbackCommandObserver(this->LesionSegmentationFilter, vtkCommand::ProgressEvent);
  this->AddCallbackCommandObserver(this->LesionSegmentationFilter, vtkCommand::EndEvent);

  // populate seeds. All the seeds on this image are used for the segmentation.

  const int nb_of_markers = vtkVVHandleWidget::GetNumberOfHandlesInDataItem(data);
  vtkITKLesionSegmentationImageFilterBase::PointListType seeds(nb_of_markers);
  for (int i = 0; i < nb_of_markers; ++i)
    {
    vtkVVHandleWidget *handle = 
        vtkVVHandleWidget::GetNthHandleInDataItem( data, i );
    double pos[3];
    handle->GetWorldPosition(pos);
    seeds[i].SetPosition(pos[0], pos[1], pos[2]);
    }

  this->LesionSegmentationFilter->SetSeeds(seeds);

  // Populate the region of interest. The ROI is set based on the cropping 
  // planes set from VolView. If no cropping planes are specified, the entire
  // image extents define the ROI.

  if (data->GetVolumeWidget(this->Window))
    { // TODO Cropping planes absent for 2D images ?

    double *croppingPlanes = data->GetVolumeWidget(this->Window)->
                                                  GetCroppingPlanes();
    double inputVolumeOrigin[3], inputVolumeSpacing[3];
    int inputVolumeDimensions[3], idx[6];
    image->GetOrigin(inputVolumeOrigin);
    image->GetSpacing(inputVolumeSpacing);
    image->GetDimensions(inputVolumeDimensions);
    
    // convert cropping planes into indices that represent VTK extents
    for ( unsigned int j = 0; j < 6; ++j )
      {
      idx[j] = (int)( (croppingPlanes[j]-
                       inputVolumeOrigin[j/2])/
                       inputVolumeSpacing[j/2] + 0.5);

      // Truncate them if they go out of bounds
      if (idx[j] < 0) 
        {
        idx[j] = 0;
        }
      if (idx[j] >= inputVolumeDimensions[j/2])
        {
        idx[j] = inputVolumeDimensions[j/2] -1;
        }
      }

    // Popualate an itk::ImageRegion from the VTK extents.
    RegionType::IndexType inputImageRegionStart;
    RegionType::SizeType  inputImageRegionSize;
    inputImageRegionStart[0] = static_cast<int>( idx[0] );
    inputImageRegionStart[1] = static_cast<int>( idx[2] );
    inputImageRegionStart[2] = static_cast<int>( idx[4] );
    inputImageRegionSize[0] =  static_cast<int>( (idx[1] - idx[0] + 1 ) );
    inputImageRegionSize[1] =  static_cast<int>( (idx[3] - idx[2] + 1 ) );
    inputImageRegionSize[2] =  static_cast<int>( (idx[5] - idx[4] + 1 ) );

    RegionType inputImageRegion( inputImageRegionStart, inputImageRegionSize );
    this->LesionSegmentationFilter->SetRegionOfInterest( inputImageRegion );

    }

  // Run the segmentation filter. Clock ticking...

  try 
    {
    this->LesionSegmentationFilter->Update();
    }
  catch (...) // don't do anything if ProcessAborted exception is called.
    {
    this->Window->SetStatusText("");
    return NULL;
    }
  
  // Disconnect the resulting segmentation from the pipeline using a 
  // shallow copy.
  vtkImageData *output = this->LesionSegmentationFilter->GetOutput();
  vtkSmartPointer< vtkImageData > disconnectedImage 
    = vtkSmartPointer< vtkImageData >::New();
  disconnectedImage->ShallowCopy(output);

  // Add the segmentation to VolView's RenderWidgets and UI.
  vtkVVDataItemVolumeContour *contour = data->GetContours()->AddNewItem();
  contour->SetImageData( disconnectedImage );
  contour->SetIsoValue( -0.5 );
  contour->Render();
  return contour;
}

//----------------------------------------------------------------------------
void vtkVVLesionSizingInterface::ProcessCallbackCommandEvents(
            vtkObject *caller, unsigned long e, void *calldata)
{
  if (vtkITKLesionSegmentationImageFilterBase * segmentationFilter
    = vtkITKLesionSegmentationImageFilterBase::SafeDownCast(caller))
    {
    // Check if we are cancelled..
    if (this->State == Cancelling)
      {
      segmentationFilter->AbortExecuteOn();
      }

    // Progress reporting from the segmentation filter.
    else if (e == vtkCommand::ProgressEvent)
      {
      this->Window->GetProgressGauge()->SetValue(
            segmentationFilter->GetProgress()*100);
      if (const char *statusText = segmentationFilter->GetStatusMessage())
        {
        this->Window->SetStatusText(statusText);
        }
      this->Window->GetApplication()->ProcessPendingEvents();
      }

    else if (e == vtkCommand::EndEvent)
      {
      this->Window->GetProgressGauge()->SetValue(0);
      this->Window->SetStatusText("Segmentation done.");
      }
    }
  
  this->Superclass::ProcessCallbackCommandEvents( caller, e, calldata );
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::PopulateOptions()
{
  if (this->State == vtkVVLesionSizingInterface::Segmenting)
    {
    ostrstream tk_cmd;
    tk_cmd << "pack " << this->OptionsFrame->GetWidgetName() 
           << " -side top -fill both -expand y" << endl;
      // No advanced options for now
    //tk_cmd << "pack " << this->AdvancedOptionsFrame->GetWidgetName() 
    //           << " -side top -fill both -expand y" << endl;
    tk_cmd << ends;
    this->Script(tk_cmd.str());
    tk_cmd.rdbuf()->freeze(0);
    }
  else
    {
    // remove the frame if we aren't showing anything in it.
    this->Script( "pack forget %s", this->OptionsFrame->GetWidgetName() );
    this->Script( "pack forget %s", this->AdvancedOptionsFrame->GetWidgetName() );
    }
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::CreateNewLesionSegmentationStrategy( bool ispartsolid )
{
  // Populate the right segmentation parameters
  if (this->SegmentationAlgorithmRadioButtonSet->GetWidget(0)->GetSelectedState())
    {
    this->LesionSegmentationFilter = vtkITKLesionSegmentationImageFilter4::New();
    }
  else if (this->SegmentationAlgorithmRadioButtonSet->GetWidget(1)->GetSelectedState())
    {
    this->LesionSegmentationFilter = vtkITKLesionSegmentationImageFilter7::New();
    }
  else if (this->SegmentationAlgorithmRadioButtonSet->GetWidget(2)->GetSelectedState())
    {
    this->LesionSegmentationFilter = vtkITKLesionSegmentationImageFilter8::New();
    }
  this->LesionSegmentationFilter->SetSigmoidBeta( ispartsolid ? -500.0 : -200.0 );
}

//---------------------------------------------------------------------------
int vtkVVLesionSizingInterface::IsSupported( vtkVVDataItem *d )
{
  // Make sure that the dataset is a 3D volume. The algorithms run only on 
  // 3D datasets.
  if (vtkVVDataItemVolume *data = vtkVVDataItemVolume::SafeDownCast( d ))
    {
    vtkImageData * image = data->GetImageData();
    int extents[6];
    image->GetExtent( extents );
    if (extents[1] - extents[0] < 3 || 
        extents[3] - extents[2] < 3 || 
        extents[5] - extents[4] < 3)
      {
      return 0;
      }
    }

  return 1;
}

//---------------------------------------------------------------------------
void vtkVVLesionSizingInterface::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}

