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

  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 "vtkVVContourSelector.h"

#include "vtkVVDataItemVolumeContour.h"
#include "vtkObjectFactory.h"
#include "vtkSmartPointer.h"
#include "vtkKWInternationalization.h"
#include "vtkKWMultiColumnList.h"
#include "vtkKWMultiColumnListWithScrollbars.h"
#include "vtkKWPushButtonSet.h"
#include "vtkKWPushButton.h"
#include "vtkKWIcon.h"
#include "vtkCommand.h"
#include "vtkKWPushButton.h"
#include "vtkKWToolbar.h"
#include "vtkKWComboBox.h"
#include "vtkKWEvent.h"

#include "vtkVVDataItemVolume.h"
#include "vtkVVDataItemVolumeContour.h"
#include "vtkVVDataItemVolumeContourCollection.h"
#include "vtkVVSelectionFrame.h"

#include "vtkKWRenderWidgetPro.h"
#include <vtksys/stl/string>
#include <vtksys/stl/vector>
#include <sstream>

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVContourSelector);
vtkCxxRevisionMacro(vtkVVContourSelector, "$Revision: 1.9 $");

//----------------------------------------------------------------------------
class vtkVVContourSelectorInternals
{
public:
  vtksys_stl::string ContourValueColumnName;
  vtksys_stl::string VisibilityColumnName;
  vtksys_stl::string ColorColumnName;
};

//----------------------------------------------------------------------------
vtkVVContourSelector::vtkVVContourSelector()
{
  this->Internals = new vtkVVContourSelectorInternals;

  this->Internals->VisibilityColumnName    = "Visibility";
  this->Internals->ColorColumnName         = "Color";
  this->Internals->ContourValueColumnName  = "Volume";
  this->ContourDetails                     = NULL;
  this->ApplyPresetOnSelection             = 0;
  this->SelectSpinButtonsVisibility        = 0;  
}

//----------------------------------------------------------------------------
vtkVVContourSelector::~vtkVVContourSelector()
{
  delete this->Internals;
  this->Internals = NULL;

  if (this->ContourDetails)
    {
    this->ContourDetails->Delete();
    this->ContourDetails = NULL;
    }

  // Delete all presets

  // We do not have much choice here but to call DeleteAllPresets(), even
  // though it is done in the destructor of the superclass too. The problem
  // with this code is that we override the virtual function DeAllocatePreset()
  // which is used by DeleteAllPresets(). At the time it is called by
  // the superclass, the virtual table of the subclass is gone, and
  // our DeAllocatePreset() is never called.

  this->DeleteAllPresets();
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::DeAllocatePreset(int id)
{
  this->Superclass::DeAllocatePreset(id);

  this->SetPresetContour(id, NULL);
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::SetPresetContour(
  int id, vtkVVDataItemVolumeContour *ptr)
{
  if (this->HasPreset(id))
    {
    vtkVVDataItemVolumeContour *prev_ptr = (vtkVVDataItemVolumeContour*)
      this->GetPresetUserSlotAsPointer(id, "Contour");
    if (prev_ptr == ptr)
      {
      return 1;
      }
    if (prev_ptr)
      {
      prev_ptr->UnRegister(this);
      }
    this->SetPresetUserSlotAsPointer(id, "Contour", ptr);
    if (ptr)
      {
      if (ptr->GetDescription())
        {
        this->SetPresetComment(id, ptr->GetDescription());
        }
      ptr->Register(this);
      }
    return 1;
    }

  return 0;
}

//----------------------------------------------------------------------------
vtkVVDataItemVolumeContour* vtkVVContourSelector::GetPresetContour(int id)
{
  return (vtkVVDataItemVolumeContour*)
    this->GetPresetUserSlotAsPointer(id, "Contour");
}

//----------------------------------------------------------------------------
vtkVVDataItemVolumeContour* vtkVVContourSelector
::GetCurrentlySelectedContour()
{
  int id = this->GetIdOfPresetAtRow(
               this->PresetList->GetWidget()->GetIndexOfFirstSelectedRow());
  return this->GetPresetContour( id );
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::CreateWidget()
{
  // Check if already created

  if (this->IsCreated())
    {
    vtkErrorMacro(<< this->GetClassName() << " already created");
    return;
    }

  // Call the superclass to create the whole contour

  this->Superclass::CreateWidget();

  // --------------------------------------------------------------
  // Interactor Widget Details

  if (!this->ContourDetails)
    {
    this->ContourDetails = vtkKWMultiColumnListWithScrollbars::New();
    }

  this->ContourDetails->SetParent(this);
  this->ContourDetails->Create();
  this->ContourDetails->HorizontalScrollbarVisibilityOff();

  vtkKWMultiColumnList *list = this->ContourDetails->GetWidget();
  list->ColumnSeparatorsVisibilityOn();
  list->SetHeight(7);

  int col;

  col = list->AddColumn(ks_("Contour Details|Property"));
  list->SetColumnWidth(col, 20);
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 0);
  list->SetColumnEditable(col, 0);

  col = list->AddColumn(ks_("Contour Details|Volume"));
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 1);
  list->SetColumnEditable(col, 0);

  this->Script(
    "pack %s -side top -anchor nw -fill x -expand n -padx 2 -pady 2",
    this->ContourDetails->GetWidgetName());
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::SetToolbarPresetButtonsHelpStrings(vtkKWToolbar *toolbar)
{
  this->Superclass::SetToolbarPresetButtonsHelpStrings(toolbar);

  if (!toolbar)
    {
    return;
    }

  vtkKWPushButton *toolbar_pb;
  toolbar_pb = vtkKWPushButton::SafeDownCast(
    toolbar->GetWidget(this->GetRemoveButtonLabel()));
  if (toolbar_pb)
    {
    toolbar_pb->SetBalloonHelpString(
      k_("Remove the selected contour(s)"));
    }
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::CreateColumns()
{
  this->Superclass::CreateColumns();

  vtkKWMultiColumnList *list = this->PresetList->GetWidget();

  // Needed since we are using special user-defined window for the
  // visibility and color cells

  list->SetPotentialCellColorsChangedCommand(
    list, "ScheduleRefreshColorsOfAllCellsWithWindowCommand");
  list->SetColumnSortedCommand(
    list, "ScheduleRefreshColorsOfAllCellsWithWindowCommand");
  
  int col;

  // Value

  col = list->InsertColumn( 0,
    ks_("Measurement Preset Selector|Column|Volume"));
  list->SetColumnName(
    col, this->Internals->ContourValueColumnName.c_str());
  list->SetColumnWidth(col, 12);
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 0);
  list->SetColumnEditable(col, 0);

  // Visibility

  col = list->InsertColumn(col + 1, NULL);
  list->SetColumnName(
    col, this->Internals->VisibilityColumnName.c_str());
  list->SetColumnLabelImageToPredefinedIcon(
    col, vtkKWIcon::IconSilkEye);
  list->SetColumnResizable(col, 0);
  list->SetColumnStretchable(col, 0);
  list->SetColumnFormatCommandToEmptyOutput(col);
  list->SetColumnWidth(col, -20);

  // Color

  col = list->InsertColumn(col + 1, NULL);
  list->SetColumnName(
    col, this->Internals->ColorColumnName.c_str());
  list->SetColumnResizable(col, 0);
  list->SetColumnStretchable(col, 0);
  list->SetColumnEditable(col, 1);
  list->SetColumnFormatCommandToEmptyOutput(col);
  list->SetColumnLabelImageToPredefinedIcon(col, vtkKWIcon::IconSilkColorSwatch);
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::GetContourValueColumnIndex()
{
  return this->PresetList ? 
    this->PresetList->GetWidget()->GetColumnIndexWithName(
      this->Internals->ContourValueColumnName.c_str()) : -1;
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::GetVisibilityColumnIndex()
{
  return this->PresetList ? 
    this->PresetList->GetWidget()->GetColumnIndexWithName(
      this->Internals->VisibilityColumnName.c_str()) : -1;
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::GetColorColumnIndex()
{
  return this->PresetList ?
    this->PresetList->GetWidget()->GetColumnIndexWithName(
      this->Internals->ColorColumnName.c_str()) : -1;
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::UpdatePresetRow(int id)
{
  if (!this->Superclass::UpdatePresetRow(id))
    {
    return 0;
    }

  int row = this->GetPresetRow(id);
  if (row < 0)
    {
    return 0;
    }

  vtkKWMultiColumnList *list = this->PresetList->GetWidget();

  vtkVVDataItemVolumeContour *c = this->GetPresetContour(id);

  if (c)
    {
    // Visibility

    list->SetCellTextAsInt(
      row, this->GetVisibilityColumnIndex(), c->GetVisibility());
    list->SetCellWindowCommandToCheckButton(
      row, this->GetVisibilityColumnIndex());

    // Color

    double r = 1.0, g = 1.0, b = 1.0;
    c->GetColor(r, g, b);
    char rgb[256];
    sprintf(rgb, "%g %g %g", r, g, b);
    list->SetCellText(row, this->GetColorColumnIndex(), rgb);
    list->SetCellWindowCommandToColorButton(row, this->GetColorColumnIndex());
    }

  this->UpdatePresetRowValueColumn(id);

  return 1;
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::UpdatePresetRowValueColumn(
  int id)
{
  int row = this->GetPresetRow(id);
  if (row < 0)
    {
    return 0;
    }

  vtkKWMultiColumnList *list = this->PresetList->GetWidget();
  if (vtkVVDataItemVolumeContour *contour = this->GetPresetContour(id))
    {
    vtkVVDataItemVolume *data = contour->GetDataItemVolume();

    std::ostringstream s;
    s << contour->GetVolume() << " "
      << (data->GetDistanceUnits() ? data->GetDistanceUnits() : "mm") 
      << "^3" << std::ends;
    list->SetCellText(
      row, this->GetContourValueColumnIndex(), s.str().c_str());

    return 1;
    }

  return 0;
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::UpdateSelectedContourDetails()
{
  if (this->PresetList)
    {
    this->UpdateContourDetails(this->GetIdOfPresetAtRow(
          this->PresetList->GetWidget()->GetIndexOfFirstSelectedRow()));
    }
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::UpdateContourDetails(int id)
{
  if (this->ContourDetails)
    {
    vtkKWMultiColumnList *list = this->ContourDetails->GetWidget();
    vtkVVDataItemVolumeContour *contour = this->GetPresetContour(id);
    if (contour)
      {
      int row = 0;
      int precision = 5;

      list->InsertCellText(row, 0, ks_("Measurement Details|Volume"));
      list->SetCellTextAsFormattedDouble(
        row, 1, contour->GetVolume(), precision);
      row++;

      list->InsertCellText(row, 0, ks_("Measurement Details|Surface Area"));
      list->SetCellTextAsFormattedDouble(
        row, 1, contour->GetSurfaceArea(), precision);
      row++;
      
      list->InsertCellText(row, 0, ks_("Measurement Details|Number of surfaces"));
      list->SetCellTextAsInt( row, 1, contour->GetNumberOfSurfaces() );
      row++;
      }
    else
      {
      list->DeleteAllRows();
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVContourSelector::PresetSelectionChangedCallback()
{
  this->Superclass::PresetSelectionChangedCallback();
  this->UpdateSelectedContourDetails();
}

//---------------------------------------------------------------------------
void vtkVVContourSelector::PresetCellUpdatedCallback(
  int row, int col, const char *text)
{
  this->Superclass::PresetCellUpdatedCallback(row, col, text);

  int id = this->GetIdOfPresetAtRow(row);
  if (!this->HasPreset(id))
    {
    return;
    }

  vtkVVDataItemVolumeContour *contour = this->GetPresetContour(id);
  if (!contour)
    {
    return;
    }

  vtkKWMultiColumnList *list = this->PresetList->GetWidget();

  // Visibility

  if (col == this->GetVisibilityColumnIndex())
    {
    contour->SetVisibility(list->GetCellTextAsInt(row, col));
    contour->Render();
    this->InvokePresetHasChangedCommand(id);
    return;
    }

  // Color

  if (col == this->GetColorColumnIndex())
    {
    double r, g, b;
    if (sscanf(list->GetCellText(row, col), "%lg %lg %lg", &r, &g, &b) != 3)
      {
      return;
      }
    contour->SetColor( r, g, b );
    contour->Render();
    this->InvokePresetHasChangedCommand(id);
    return;
    }
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::HasPresetWithGroupWithContour(
  const char *group, vtkVVDataItemVolumeContour *contour)
{
  int i, nb_presets = this->GetNumberOfPresetsWithGroup(group);
  for (i = 0; i < nb_presets; i++)
    {
    int id = this->GetIdOfNthPresetWithGroup(i, group);
    if (this->GetPresetContour(id) == contour)
      {
      return 1;
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::GetIdOfContour(vtkVVDataItemVolumeContour *w)
{
  int nb_presets = this->GetNumberOfPresets();
  for (int i = 0; i < nb_presets; i++)
    {
    int id = this->GetIdOfNthPreset(i);
    if (this->GetPresetContour(id) == w)
      {
      return id;
      }
    }

  return -1;
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::ProcessCallbackCommandEvents(
  vtkObject *caller,
  unsigned long event,
  void *calldata)
{
  vtkVVDataItemVolumeContour *contour = vtkVVDataItemVolumeContour::SafeDownCast(caller);

  int sel_id = this->GetIdOfPresetAtRow(
    this->PresetList->GetWidget()->GetIndexOfFirstSelectedRow());

  if (contour)
    {
    int i, nb_presets = this->GetNumberOfPresets();
    for (i = 0; i < nb_presets; i++)
      {
      int id = this->GetIdOfNthPreset(i);
      if (this->GetPresetContour(id) == contour)
        {
        this->UpdatePresetRowValueColumn(id);
        if (sel_id != id)
          {
          this->PresetList->GetWidget()->SelectSingleRow(
            this->GetPresetRow(id));
          }
        }
      }
    }

  this->Superclass::ProcessCallbackCommandEvents(caller, event, calldata);
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::UpdateEnableState()
{
  this->Superclass::UpdateEnableState();
  this->PropagateEnableState(this->ContourDetails);
}

//----------------------------------------------------------------------------
int vtkVVContourSelector::RemovePreset( int id )
{
  // Get the contour and remove it from our collection.
  if (vtkVVDataItemVolumeContour *contour = this->GetPresetContour(id))
    {
    contour->SetVisibility(0);
    contour->Render();
    contour->GetDataItemVolume()->GetContours()->RemoveItem( contour );
    }

  return this->Superclass::RemovePreset(id);
}

//----------------------------------------------------------------------------
void vtkVVContourSelector::PopulatePresets( vtkVVDataItemVolume *data )
{
  if (data)
    {
    // Remove the old ones.

    vtksys_stl::vector<int> ids_to_remove;
    const int nb_presets = this->GetNumberOfPresets();
    for (int i = 0; i < nb_presets; i++)
      {
      int id = this->GetIdOfNthPreset(i);
      vtkVVDataItemVolumeContour *contour = this->GetPresetContour(id);
      if (contour && contour->GetDataItemVolume() == data && 
          !data->GetContours()->IsItemPresent(contour))
        {
        ids_to_remove.push_back(id);
        }
      }
    for (unsigned int rank = 0; rank < ids_to_remove.size(); rank++)
      {
      this->RemovePreset(ids_to_remove[rank]);
      }

    // Add the new ones.
      
    const char *group = data->GetName(); // Unique hopefully for a dataitem ?
    this->SetPresetFilterGroupConstraint( group );

    const int nb_contours = data->GetContours()->GetNumberOfItems();

    for (int i = 0; i < nb_contours; i++)
      {
      vtkVVDataItemVolumeContour *contour = data->GetContours()->GetNthItem(i);
      if (contour && !this->HasPresetWithGroupWithContour(group, contour))
        {
        int id = this->InsertPreset(this->GetIdOfNthPreset(0));
        this->SetPresetGroup(id, group);
        this->SetPresetContour(id, contour);
        }
      }
    }
}


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