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

  Program:   Visualization Toolkit
  Module:    vtkScatterPlotMatrix.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.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 "vtkScatterPlotMatrix.h"

#include "vtkTable.h"
#include "vtkFloatArray.h"
#include "vtkIntArray.h"
#include "vtkChartXY.h"
#include "vtkPlot.h"
#include "vtkAxis.h"
#include "vtkContextMouseEvent.h"
#include "vtkStdString.h"
#include "vtkStringArray.h"
#include "vtkNew.h"
#include "vtkPen.h"
#include "vtkMathUtilities.h"
#include "vtkAnnotationLink.h"
#include "vtkObjectFactory.h"
#include "vtkBrush.h"
#include "vtkPlotPoints.h"
#include "vtkPlotPoints3D.h"
#include "vtkCommand.h"
#include "vtkTextProperty.h"
#include "vtkContextScene.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkCallbackCommand.h"
#include "vtkVectorOperators.h"
#include "vtkTooltipItem.h"

#include "vtkChartXYZ.h"

// STL includes
#include <map>
#include <cassert>
#include <vector>

class vtkScatterPlotMatrix::PIMPL
{
public:
  PIMPL() : VisibleColumnsModified(true), BigChart(NULL),
    AnimationCallbackInitialized(false), TimerId(0),
    TimerCallbackInitialized(false)
  {
    pimplChartSetting* scatterplotSettings = new pimplChartSetting();
    scatterplotSettings->BackgroundBrush->SetColor(255, 255, 255, 255);
    this->ChartSettings[vtkScatterPlotMatrix::SCATTERPLOT] =
        scatterplotSettings;
    pimplChartSetting* histogramSettings = new pimplChartSetting();
    histogramSettings->BackgroundBrush->SetColor(127, 127, 127, 102);
    histogramSettings->PlotPen->SetColor(255, 255, 255, 255);
    histogramSettings->ShowAxisLabels = true;
    this->ChartSettings[vtkScatterPlotMatrix::HISTOGRAM] = histogramSettings;
    pimplChartSetting* activeplotSettings = new pimplChartSetting();
    activeplotSettings->BackgroundBrush->SetColor(255, 255, 255, 255);
    activeplotSettings->ShowAxisLabels = true;
    this->ChartSettings[vtkScatterPlotMatrix::ACTIVEPLOT] = activeplotSettings;
    activeplotSettings->MarkerSize = 8.0;
    this->SelectedChartBGBrush->SetColor(0, 204, 0, 102);
    this->SelectedRowColumnBGBrush->SetColor(204, 0, 0, 102);
    this->TooltipItem = vtkSmartPointer<vtkTooltipItem>::New();
  }

  ~PIMPL()
  {
    delete this->ChartSettings[vtkScatterPlotMatrix::SCATTERPLOT];
    delete this->ChartSettings[vtkScatterPlotMatrix::HISTOGRAM];
    delete this->ChartSettings[vtkScatterPlotMatrix::ACTIVEPLOT];
  }

  // Store columns settings such as axis range, title, number of tick marks.
  class ColumnSetting
  {
  public:
    ColumnSetting() : min(0), max(0), nTicks(0), title("?!?")
    {
    }

    double min;
    double max;
    int    nTicks;
    std::string title;
  };

  class pimplChartSetting
  {
  public:
    pimplChartSetting()
    {
      this->PlotPen->SetColor(0, 0, 0, 255);
      this->MarkerStyle = vtkPlotPoints::CIRCLE;
      this->MarkerSize = 3.0;
      this->AxisColor.Set(0, 0, 0, 255);
      this->GridColor.Set(242, 242, 242, 255);
      this->LabelNotation = vtkAxis::STANDARD_NOTATION;
      this->LabelPrecision = 2;
      this->TooltipNotation = vtkAxis::STANDARD_NOTATION;
      this->TooltipPrecision = 2;
      this->ShowGrid = true;
      this->ShowAxisLabels = false;
      this->LabelFont = vtkSmartPointer<vtkTextProperty>::New();
      this->LabelFont->SetFontFamilyToArial();
      this->LabelFont->SetFontSize(12);
      this->LabelFont->SetColor(0.0, 0.0, 0.0);
      this->LabelFont->SetOpacity(1.0);
    }
    ~pimplChartSetting() {}

    int MarkerStyle;
    float MarkerSize;
    vtkColor4ub AxisColor;
    vtkColor4ub GridColor;
    int LabelNotation;
    int LabelPrecision;
    int TooltipNotation;
    int TooltipPrecision;
    bool ShowGrid;
    bool ShowAxisLabels;
    vtkSmartPointer<vtkTextProperty> LabelFont;
    vtkNew<vtkBrush> BackgroundBrush;
    vtkNew<vtkPen> PlotPen;
    vtkNew<vtkBrush> PlotBrush;
  };

  void UpdateAxis(vtkAxis* axis, pimplChartSetting* setting,
                  bool updateLabel = true)
    {
    if(axis && setting)
      {
      axis->GetPen()->SetColor(setting->AxisColor);
      axis->GetGridPen()->SetColor(setting->GridColor);
      axis->SetGridVisible(setting->ShowGrid);
      if (updateLabel)
        {
        vtkTextProperty *prop = setting->LabelFont.GetPointer();
        axis->SetNotation(setting->LabelNotation);
        axis->SetPrecision(setting->LabelPrecision);
        axis->SetLabelsVisible(setting->ShowAxisLabels);
        axis->GetLabelProperties()->SetFontSize(prop->GetFontSize());
        axis->GetLabelProperties()->SetColor(prop->GetColor());
        axis->GetLabelProperties()->SetOpacity(prop->GetOpacity());
        axis->GetLabelProperties()->SetFontFamilyAsString(
          prop->GetFontFamilyAsString());
        axis->GetLabelProperties()->SetBold(prop->GetBold());
        axis->GetLabelProperties()->SetItalic(prop->GetItalic());
        }
      }
    }

  void UpdateChart(vtkChart* chart, pimplChartSetting* setting)
    {
    if(chart && setting)
      {
      vtkPlot *plot = chart->GetPlot(0);
      if (plot)
        {
        plot->SetTooltipNotation(setting->TooltipNotation);
        plot->SetTooltipPrecision(setting->TooltipPrecision);
        }
      }
    }

  vtkNew<vtkTable> Histogram;
  bool VisibleColumnsModified;
  vtkWeakPointer<vtkChart> BigChart;
  vtkNew<vtkAnnotationLink> Link;

  // Settings for the charts in the scatter plot matrix.
  std::map<int, pimplChartSetting*> ChartSettings;
  typedef std::map<int, pimplChartSetting*>::iterator chartIterator;

  // Axis ranges for the columns in the scatter plot matrix.
  std::map<std::string, ColumnSetting> ColumnSettings;

  vtkNew<vtkBrush> SelectedRowColumnBGBrush;
  vtkNew<vtkBrush> SelectedChartBGBrush;
  std::vector<vtkVector2i>           AnimationPath;
  std::vector<vtkVector2i>::iterator AnimationIter;
  vtkRenderWindowInteractor* Interactor;
  vtkNew<vtkCallbackCommand> AnimationCallback;
  bool                       AnimationCallbackInitialized;
  unsigned long int          TimerId;
  bool                       TimerCallbackInitialized;
  int                        AnimationPhase;
  float                      CurrentAngle;
  float                      IncAngle;
  float                      FinalAngle;
  vtkVector2i                NextActivePlot;

  vtkNew<vtkChartXYZ> BigChart3D;
  vtkNew<vtkAxis>     TestAxis;   // Used to get ranges/numer of ticks
  vtkSmartPointer<vtkTooltipItem> TooltipItem;
  vtkSmartPointer<vtkStringArray> IndexedLabelsArray;
};

namespace
{

// This is just here for now - quick and dirty historgram calculations...
bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s,
                        int NumberOfBins)
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
  for (vtkIdType i = 0; i < s->GetNumberOfTuples(); ++i)
    {
    double minmax[2] = { 0.0, 0.0 };
    vtkStdString name(s->GetValue(i));
    vtkDataArray *in =
        vtkDataArray::SafeDownCast(input->GetColumnByName(name.c_str()));
    if (in)
      {
      // The bin values are the centers, extending +/- half an inc either side
      in->GetRange(minmax);
      if (minmax[0] == minmax[1])
        {
        minmax[1] = minmax[0] + 1.0;
        }
      double inc = (minmax[1] - minmax[0]) / (NumberOfBins) * 1.001;
      double halfInc = inc / 2.0;
      vtkSmartPointer<vtkFloatArray> extents =
          vtkFloatArray::SafeDownCast(
            output->GetColumnByName(vtkStdString(name + "_extents").c_str()));
      if (!extents)
        {
        extents = vtkSmartPointer<vtkFloatArray>::New();
        extents->SetName(vtkStdString(name + "_extents").c_str());
        }
      extents->SetNumberOfTuples(NumberOfBins);
      float *centers = static_cast<float *>(extents->GetVoidPointer(0));
      double min = minmax[0] - 0.0005 * inc + halfInc;
      for (int j = 0; j < NumberOfBins; ++j)
        {
        extents->SetValue(j, min + j * inc);
        }
      vtkSmartPointer<vtkIntArray> populations =
          vtkIntArray::SafeDownCast(
            output->GetColumnByName(vtkStdString(name + "_pops").c_str()));
      if (!populations)
        {
        populations = vtkSmartPointer<vtkIntArray>::New();
        populations->SetName(vtkStdString(name + "_pops").c_str());
        }
      populations->SetNumberOfTuples(NumberOfBins);
      int *pops = static_cast<int *>(populations->GetVoidPointer(0));
      for (int k = 0; k < NumberOfBins; ++k)
        {
        pops[k] = 0;
        }
      for (vtkIdType j = 0; j < in->GetNumberOfTuples(); ++j)
        {
        double v(0.0);
        in->GetTuple(j, &v);
        for (int k = 0; k < NumberOfBins; ++k)
          {
          if (vtkMathUtilities::FuzzyCompare(v, double(centers[k]), halfInc))
            {
            ++pops[k];
            break;
            }
          }
        }
      output->AddColumn(extents.GetPointer());
      output->AddColumn(populations.GetPointer());
      }
    }
  return true;
}

bool MoveColumn(vtkStringArray* visCols, int fromCol, int toCol)
{
  if(!visCols || visCols->GetNumberOfTuples() == 0
    || fromCol == toCol || fromCol == (toCol-1) || fromCol < 0 || toCol < 0)
    {
    return false;
    }
  int numCols = visCols->GetNumberOfTuples();
  if( fromCol >= numCols || toCol > numCols)
    {
    return false;
    }

  std::vector<vtkStdString> newVisCols;
  vtkIdType c;
  if(toCol == numCols)
    {
    for(c=0; c<numCols; c++)
      {
      if(c!=fromCol)
        {
        newVisCols.push_back(visCols->GetValue(c));
        }
      }
    // move the fromCol to the end
    newVisCols.push_back(visCols->GetValue(fromCol));
    }
  // insert the fromCol before toCol
  else if(fromCol < toCol)
    {
    // move Cols in the middle up
    for(c=0; c<fromCol; c++)
      {
      newVisCols.push_back(visCols->GetValue(c));
      }
    for(c=fromCol+1; c<numCols; c++)
      {
      if(c == toCol)
        {
        newVisCols.push_back(visCols->GetValue(fromCol));
        }
      newVisCols.push_back(visCols->GetValue(c));
      }
    }
  else
    {
    for(c=0; c<toCol; c++)
      {
      newVisCols.push_back(visCols->GetValue(c));
      }
    newVisCols.push_back(visCols->GetValue(fromCol));
    for(c=toCol; c<numCols; c++)
      {
      if(c != fromCol)
        {
        newVisCols.push_back(visCols->GetValue(c));
        }
      }
    }

  // repopulate the visCols
  vtkIdType visId=0;
  std::vector<vtkStdString>::iterator arrayIt;
  for(arrayIt=newVisCols.begin(); arrayIt!=newVisCols.end(); ++arrayIt)
    {
    visCols->SetValue(visId++, *arrayIt);
    }
  return true;
}
} // End of anonymous namespace

vtkStandardNewMacro(vtkScatterPlotMatrix)

vtkScatterPlotMatrix::vtkScatterPlotMatrix()
  : NumberOfBins(10), NumberOfFrames(25)
{
  this->Private = new PIMPL;
  this->TitleProperties = vtkSmartPointer<vtkTextProperty>::New();
  this->TitleProperties->SetFontSize(12);
  this->SelectionMode = vtkContextScene::SELECTION_NONE;
}

vtkScatterPlotMatrix::~vtkScatterPlotMatrix()
{
  delete this->Private;
}

void vtkScatterPlotMatrix::Update()
{
  if (this->Private->VisibleColumnsModified)
    {
    // We need to handle layout changes due to modified visibility.
    // Build up our histograms data before updating the layout.
    PopulateHistograms(this->Input.GetPointer(),
                       this->Private->Histogram.GetPointer(),
                       this->VisibleColumns.GetPointer(),
                       this->NumberOfBins);
    this->UpdateLayout();
    this->Private->VisibleColumnsModified = false;
    }
}

bool vtkScatterPlotMatrix::Paint(vtkContext2D *painter)
{
  this->Update();
  return Superclass::Paint(painter);
}

void vtkScatterPlotMatrix::SetScene(vtkContextScene *scene)
{
  // The internal axis shouldn't be a child as it isn't rendered with the
  // chart, but it does need access to the scene.
  this->Private->TestAxis->SetScene(scene);

  this->Superclass::SetScene(scene);
}

bool vtkScatterPlotMatrix::SetActivePlot(const vtkVector2i &pos)
{
  if (pos.GetX() + pos.GetY() + 1 < this->Size.GetX() && pos.GetX() < this->Size.GetX() &&
      pos.GetY() < this->Size.GetY())
    {
    // The supplied index is valid (in the lower quadrant).
    this->ActivePlot = pos;

    // Invoke an interaction event, to let observers know something changed.
    this->InvokeEvent(vtkCommand::AnnotationChangedEvent);

    // set background colors for plots
    if (this->GetChart(this->ActivePlot)->GetPlot(0))
      {
      int plotCount = this->GetSize().GetX();
      for (int i = 0; i < plotCount; ++i)
        {
        for (int j = 0; j < plotCount; ++j)
          {
          if (this->GetPlotType(i, j) == SCATTERPLOT)
            {
            vtkChartXY *chart =
                vtkChartXY::SafeDownCast(this->GetChart(vtkVector2i(i, j)));

            if (pos[0] == i && pos[1] == j)
              {
              // set the new active chart background color to light green
              chart->SetBackgroundBrush(
                    this->Private->SelectedChartBGBrush.GetPointer());
              }
            else if (pos[0] == i || pos[1] == j)
              {
              // set background color for all other charts in the selected
              // chart's row and column to light red
              chart->SetBackgroundBrush(
                    this->Private->SelectedRowColumnBGBrush.GetPointer());
              }
            else
              {
              // set all else to white
              chart->SetBackgroundBrush(
                    this->Private->ChartSettings[SCATTERPLOT]
                    ->BackgroundBrush.GetPointer());
              }
            }
          }
        }
      }
    if (this->Private->BigChart)
      {
      vtkPlot *plot = this->Private->BigChart->GetPlot(0);
      vtkStdString column = this->GetColumnName(pos.GetX());
      vtkStdString row = this->GetRowName(pos.GetY());
      if (!plot)
        {
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
        vtkChart *active = this->GetChart(this->ActivePlot);
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
          {
          // Set plot corner, and axis visibility
          xy->SetPlotCorner(plot, 2);
          xy->SetAutoAxes(false);
          xy->GetAxis(vtkAxis::TOP)->SetVisible(true);
          xy->GetAxis(vtkAxis::RIGHT)->SetVisible(true);
          xy->GetAxis(vtkAxis::BOTTOM)->SetLabelsVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetGridVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetTicksVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetVisible(true);
          xy->GetAxis(vtkAxis::LEFT)->SetLabelsVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetGridVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetTicksVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetVisible(true);

          // set labels array
          if(this->Private->IndexedLabelsArray)
            {
              plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
              plot->SetTooltipLabelFormat("%i");
            }
          }
        if (xy && active)
          {
          vtkAxis *a = active->GetAxis(vtkAxis::BOTTOM);
          xy->GetAxis(vtkAxis::TOP)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
          a = active->GetAxis(vtkAxis::LEFT);
          xy->GetAxis(vtkAxis::RIGHT)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
          }
        }
      else
        {
        this->Private->BigChart->ClearPlots();
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
          {
          xy->SetPlotCorner(plot, 2);
          }

        // set labels array
        if(this->Private->IndexedLabelsArray)
          {
          plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
          plot->SetTooltipLabelFormat("%i");
          }
        }
      plot->SetInputData(this->Input.GetPointer(), column, row);
      plot->SetPen(this->Private->ChartSettings[ACTIVEPLOT]
                   ->PlotPen.GetPointer());
      this->ApplyAxisSetting(this->Private->BigChart.GetPointer(), column, row);

      // Set marker size and style.
      vtkPlotPoints *plotPoints = vtkPlotPoints::SafeDownCast(plot);
      plotPoints->SetMarkerSize(this->Private->ChartSettings[ACTIVEPLOT]
                                ->MarkerSize);
      plotPoints->SetMarkerStyle(this->Private->ChartSettings[ACTIVEPLOT]
                                 ->MarkerStyle);
      // Set background color.
      this->Private->BigChart->SetBackgroundBrush(
            this->Private->ChartSettings[ACTIVEPLOT]
            ->BackgroundBrush.GetPointer());
      this->Private->BigChart->GetAxis(vtkAxis::TOP)->SetTitle(
            this->VisibleColumns->GetValue(pos.GetX()));
      this->Private->BigChart->GetAxis(vtkAxis::RIGHT)->SetTitle(
            this->VisibleColumns->GetValue(this->GetSize().GetX() - pos.GetY() - 1));
      // Calculate the ideal range.
      //this->Private->BigChart->RecalculateBounds();
      }
    return true;
    }
  else
    {
    return false;
    }
}

vtkVector2i vtkScatterPlotMatrix::GetActivePlot()
{
  return this->ActivePlot;
}

void vtkScatterPlotMatrix::UpdateAnimationPath(const vtkVector2i& newActivePos)
{
  this->Private->AnimationPath.clear();
  if (newActivePos[0] != this->ActivePlot[0] ||
      newActivePos[1] != this->ActivePlot[1])
    {
    if (newActivePos[1] >= this->ActivePlot[1])
      {
      // x direction first
      if (this->ActivePlot[0]>newActivePos[0])
        {
        for(int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
          }
        }
      else
        {
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
          }
        }
      // then y direction
      for (int c = this->ActivePlot[1] + 1; c <= newActivePos[1]; c++)
        {
        this->Private->AnimationPath.push_back(vtkVector2i(newActivePos[0], c));
        }
      }
    else
      {
      // y direction first
      for (int c = this->ActivePlot[1] - 1; c >= newActivePos[1]; c--)
        {
        this->Private->AnimationPath.push_back(vtkVector2i(this->ActivePlot[0],
                                                           c));
        }
      // then x direction
      if (this->ActivePlot[0]>newActivePos[0])
        {
        for (int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             newActivePos[1]));
          }
        }
      else
        {
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r, newActivePos[1]));
          }
        }
      }
    }
}

void vtkScatterPlotMatrix::StartAnimation(vtkRenderWindowInteractor* interactor)
{
  // Start a simple repeating timer to advance along the path until completion.
  if (!this->Private->TimerCallbackInitialized && interactor)
    {
    if (!this->Private->AnimationCallbackInitialized)
      {
      this->Private->AnimationCallback->SetClientData(this);
      this->Private->AnimationCallback->SetCallback(
            vtkScatterPlotMatrix::ProcessEvents);
      interactor->AddObserver(vtkCommand::TimerEvent,
                              this->Private->AnimationCallback.GetPointer(),
                              0);
      this->Private->Interactor = interactor;
      this->Private->AnimationCallbackInitialized = true;
      }
    this->Private->TimerCallbackInitialized = true;
    // This defines the interval at which the animation will proceed. 25Hz?
    this->Private->TimerId = interactor->CreateRepeatingTimer(1000 / 50);
    this->Private->AnimationIter = this->Private->AnimationPath.begin();
    this->Private->AnimationPhase = 0;
    }
}

void vtkScatterPlotMatrix::AdvanceAnimation()
{
  // The animation has several phases, and we must track where we are.

  // 1: Remove decoration from the big chart.
  // 2: Set three dimensions to plot in the BigChart3D.
  // 3: Make BigChart inivisible, and BigChart3D visible.
  // 4: Rotate between the two dimensions we are transitioning between.
  //    -> Loop from start to end angle to complete the effect.
  // 5: Make the new dimensionality active, update BigChart.
  // 5: Make BigChart3D invisible and BigChart visible.
  // 6: Stop the timer.
  this->InvokeEvent(vtkCommand::AnimationCueTickEvent);
  switch (this->Private->AnimationPhase)
    {
  case 0: // Remove decoration from the big chart, load up the 3D chart
    {
    this->Private->NextActivePlot = *this->Private->AnimationIter;
    vtkChartXYZ *chart = this->Private->BigChart3D.GetPointer();
    chart->SetVisible(false);
    chart->SetAutoRotate(true);
    chart->SetDecorateAxes(false);
    chart->SetFitToScene(false);

    int yColumn = this->GetSize().GetY() - this->ActivePlot.GetY() - 1;
    bool isX = false;
    int zColumn = 0;

    vtkRectf size = this->Private->BigChart->GetSize();
    float zSize;
    this->Private->FinalAngle = 90.0;
    this->Private->IncAngle = this->Private->FinalAngle / this->NumberOfFrames;

    if (this->Private->NextActivePlot.GetY() == this->ActivePlot.GetY())
      {
      // Horizontal move.
      zColumn = this->Private->NextActivePlot.GetX();
      isX = false;
      if (this->ActivePlot.GetX() < zColumn)
        {
        this->Private->IncAngle *= 1.0;
        zSize = size.GetWidth();
        }
      else
        {
        this->Private->IncAngle *= -1.0;
        zSize = -size.GetWidth();
        }
      }
    else
      {
      // Vertical move.
      zColumn = this->GetSize().GetY() - this->Private->NextActivePlot.GetY() - 1;
      isX = true;
      if (this->GetSize().GetY() - this->ActivePlot.GetY() - 1 < zColumn)
        {
        this->Private->IncAngle *= -1.0;
        zSize = size.GetHeight();
        }
      else
        {
        this->Private->IncAngle *= 1.0;
        zSize = -size.GetHeight();
        }
      }
    chart->SetAroundX(isX);
    chart->SetGeometry(size);

    vtkStdString names[3];
    names[0] = this->VisibleColumns->GetValue(this->ActivePlot.GetX());
    names[1] = this->VisibleColumns->GetValue(yColumn);
    names[2] = this->VisibleColumns->GetValue(zColumn);

    // Setup the 3D chart
    this->Private->BigChart3D->ClearPlots();
    vtkNew<vtkPlotPoints3D> scatterPlot3D;
    scatterPlot3D->SetInputData(
      this->Input.GetPointer(), names[0], names[1], names[2]);
    this->Private->BigChart3D->AddPlot(scatterPlot3D.GetPointer());

    // Set the z axis up so that it ends in the right orientation.
    chart->GetAxis(2)->SetPoint2(0, zSize);
    // Now set the ranges for the three axes.
    for (int i = 0; i < 3; ++i)
      {
      PIMPL::ColumnSetting &settings = this->Private->ColumnSettings[names[i]];
      chart->GetAxis(i)->SetUnscaledRange(settings.min, settings.max);
      }
    chart->RecalculateTransform();
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    return;
    }
  case 1: // Make BigChart invisible, and BigChart3D visible.
    this->Private->BigChart->SetVisible(false);
    this->AddItem(this->Private->BigChart3D.GetPointer());
    this->Private->BigChart3D->SetVisible(true);
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    this->Private->CurrentAngle = 0.0;
    return;
  case 2: // Rotation of the 3D chart from start to end angle.
    if (fabs(this->Private->CurrentAngle) < (this->Private->FinalAngle - 0.001))
      {
      this->Private->CurrentAngle += this->Private->IncAngle;
      this->Private->BigChart3D->SetAngle(this->Private->CurrentAngle);
      }
    else
      {
      ++this->Private->AnimationPhase;
      }
    this->GetScene()->SetDirty(true);
    return;
  case 3: // Transition to new dimensionality, update the big chart.
    this->SetActivePlot(this->Private->NextActivePlot);
    this->Private->BigChart->Update();
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    break;
  case 4:
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationIter;
    // Clean up - we are done.
    this->Private->AnimationPhase = 0;
    if (this->Private->AnimationIter == this->Private->AnimationPath.end())
      {
      this->Private->BigChart->SetVisible(true);
      this->RemoveItem(this->Private->BigChart3D.GetPointer());
      this->Private->BigChart3D->SetVisible(false);
      this->Private->Interactor->DestroyTimer(this->Private->TimerId);
      this->Private->TimerId = 0;
      this->Private->TimerCallbackInitialized = false;
      }
    }
}

void vtkScatterPlotMatrix::ProcessEvents(vtkObject *, unsigned long event,
                                         void *clientData, void *callerData)
{
  vtkScatterPlotMatrix *self =
      reinterpret_cast<vtkScatterPlotMatrix *>(clientData);
  switch (event)
    {
    case vtkCommand::TimerEvent:
      {
      // We must filter the events to ensure we actually get the timer event we
      // created. I would love signals and slots...
      int timerId = *reinterpret_cast<int *>(callerData);   // Seems to work.
      if (self->Private->TimerCallbackInitialized &&
          timerId == static_cast<int>(self->Private->TimerId))
        {
        self->AdvanceAnimation();
        }
      break;
      }
    default:
      break;
    }
}

vtkAnnotationLink* vtkScatterPlotMatrix::GetAnnotationLink()
{
  return this->Private->Link.GetPointer();
}

void vtkScatterPlotMatrix::SetInput(vtkTable *table)
{
  if(table && table->GetNumberOfRows() == 0)
    {
    // do nothing if the table is emtpy
    return;
    }

  if (this->Input != table)
    {
    // Set the input, then update the size of the scatter plot matrix, set
    // their inputs and all the other stuff needed.
    this->Input = table;
    this->SetSize(vtkVector2i(0, 0));
    this->Modified();

    if (table == NULL)
      {
      this->SetColumnVisibilityAll(true);
      return;
      }
    int n = static_cast<int>(this->Input->GetNumberOfColumns());
    this->SetColumnVisibilityAll(true);
    this->SetSize(vtkVector2i(n, n));
    }
}

void vtkScatterPlotMatrix::SetColumnVisibility(const vtkStdString &name,
                                               bool visible)
{
  if (visible)
    {
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
      {
      if (this->VisibleColumns->GetValue(i) == name)
        {
        // Already there, nothing more needs to be done
        return;
        }
      }
    // Add the column to the end of the list if it is a numeric column
    if (this->Input && this->Input->GetColumnByName(name.c_str()) &&
        vtkDataArray::SafeDownCast(this->Input->GetColumnByName(name.c_str())))
      {
      this->VisibleColumns->InsertNextValue(name);
      this->Private->VisibleColumnsModified = true;
      this->SetSize(vtkVector2i(0, 0));
      this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                this->VisibleColumns->GetNumberOfTuples()));
      this->Modified();
      }
    }
  else
    {
    // Remove the value if present
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
      {
      if (this->VisibleColumns->GetValue(i) == name)
        {
        // Move all the later elements down by one, and reduce the size
        while (i < this->VisibleColumns->GetNumberOfTuples() - 1)
          {
          this->VisibleColumns->SetValue(i,
                                         this->VisibleColumns->GetValue(i + 1));
          ++i;
          }
        this->VisibleColumns->SetNumberOfTuples(
            this->VisibleColumns->GetNumberOfTuples() - 1);
        this->SetSize(vtkVector2i(0, 0));
        this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                  this->VisibleColumns->GetNumberOfTuples()));
        if (this->ActivePlot.GetX() + this->ActivePlot.GetY() + 1 >=
            this->VisibleColumns->GetNumberOfTuples())
          {
          this->ActivePlot.Set(0, this->VisibleColumns->GetNumberOfTuples() - 1);
          }
        this->Private->VisibleColumnsModified = true;
        this->Modified();
        }
      }
    }
}

void vtkScatterPlotMatrix::InsertVisibleColumn(const vtkStdString &name,
                                               int index)
{
  if(!this->Input || !this->Input->GetColumnByName(name.c_str()))
    {
    return;
    }

  // Check if the column is already in the list. If yes,
  // we may need to rearrange the order of the columns.
  vtkIdType currIdx = -1;
  vtkIdType numCols = this->VisibleColumns->GetNumberOfTuples();
  for (vtkIdType i = 0; i < numCols; ++i)
    {
    if (this->VisibleColumns->GetValue(i) == name)
      {
      currIdx = i;
      break;
      }
    }

  if(currIdx > 0 && currIdx == index)
    {
    //This column is already there.
    return;
    }

  if(currIdx < 0)
    {
    this->VisibleColumns->SetNumberOfTuples(numCols+1);
    if(index >= numCols)
      {
      this->VisibleColumns->SetValue(numCols, name);
      }
    else // move all the values after index down 1
      {
      vtkIdType startidx = numCols;
      vtkIdType idx = (index < 0) ? 0 : index;
      while (startidx > idx)
        {
        this->VisibleColumns->SetValue(startidx,
          this->VisibleColumns->GetValue(startidx-1));
        startidx--;
        }
      this->VisibleColumns->SetValue(idx, name);
      }
    this->Private->VisibleColumnsModified = true;
    }
  else // need to rearrange table columns
    {
    vtkIdType toIdx = (index < 0) ? 0 : index;
    toIdx = toIdx>numCols ? numCols : toIdx;
    this->Private->VisibleColumnsModified =
      MoveColumn(this->VisibleColumns.GetPointer(), currIdx, toIdx);
    }

  this->Update();
}

bool vtkScatterPlotMatrix::GetColumnVisibility(const vtkStdString &name)
{
  for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
    {
    if (this->VisibleColumns->GetValue(i) == name)
      {
      return true;
      }
    }
  return false;
}

void vtkScatterPlotMatrix::SetColumnVisibilityAll(bool visible)
{
  if (visible && this->Input)
    {
    vtkIdType n = this->Input->GetNumberOfColumns();
    this->VisibleColumns->SetNumberOfTuples(n);
    for (vtkIdType i = 0; i < n; ++i)
      {
      this->VisibleColumns->SetValue(i, this->Input->GetColumnName(i));
      }
    }
  else
    {
    this->SetSize(vtkVector2i(0, 0));
    this->VisibleColumns->SetNumberOfTuples(0);
    }

  this->Private->VisibleColumnsModified = true;
}

vtkStringArray* vtkScatterPlotMatrix::GetVisibleColumns()
{
  return this->VisibleColumns.GetPointer();
}

void vtkScatterPlotMatrix::SetVisibleColumns(vtkStringArray* visColumns)
{
  if(!visColumns || visColumns->GetNumberOfTuples() == 0)
    {
    this->SetSize(vtkVector2i(0, 0));
    this->VisibleColumns->SetNumberOfTuples(0);
    }
  else
    {
    this->VisibleColumns->SetNumberOfTuples(
      visColumns->GetNumberOfTuples());
    this->VisibleColumns->DeepCopy(visColumns);
    }
  this->Private->VisibleColumnsModified = true;
  this->Update();
}

void vtkScatterPlotMatrix::SetNumberOfBins(int numberOfBins)
{
  if (this->NumberOfBins != numberOfBins)
    {
    this->NumberOfBins = numberOfBins;
    if (this->Input)
      {
      PopulateHistograms(this->Input.GetPointer(),
                         this->Private->Histogram.GetPointer(),
                         this->VisibleColumns.GetPointer(),
                         this->NumberOfBins);
      }
    this->Modified();
    }
}

void vtkScatterPlotMatrix::SetPlotColor(int plotType, const vtkColor4ub& color)
{
  if(plotType >= 0 && plotType < NOPLOT)
    {
    if (plotType == ACTIVEPLOT || plotType == SCATTERPLOT)
      {
      this->Private->ChartSettings[plotType]->PlotPen->SetColor(color);
      }
    else
      {
      this->Private->ChartSettings[HISTOGRAM]->PlotBrush->SetColor(color);
      }
    this->Modified();
    }
}

void vtkScatterPlotMatrix::SetPlotMarkerStyle(int plotType, int style)
{
  if(plotType >= 0 && plotType < NOPLOT &&
     style != this->Private->ChartSettings[plotType]->MarkerStyle)
    {
    this->Private->ChartSettings[plotType]->MarkerStyle = style;

    if (plotType == ACTIVEPLOT)
      {
      vtkChart *chart = this->Private->BigChart;
      if (chart)
        {
        vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart->GetPlot(0));
        if (plot)
          {
          plot->SetMarkerStyle(style);
          }
        }
      this->Modified();
      }
    else if (plotType == SCATTERPLOT)
      {
      int plotCount = this->GetSize().GetX();
      for (int i = 0; i < plotCount - 1; ++i)
        {
        for(int j = 0; j < plotCount - 1; ++j)
          {
          if(this->GetPlotType(i, j) == SCATTERPLOT &&
             this->GetChart(vtkVector2i(i, j)))
            {
            vtkChart *chart = this->GetChart(vtkVector2i(i, j));
            vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart->GetPlot(0));
            if (plot)
              {
              plot->SetMarkerStyle(style);
              }
            }
          }
        }
      this->Modified();
      }
    }
}

void vtkScatterPlotMatrix::SetPlotMarkerSize(int plotType, float size)
{
  if(plotType >= 0 && plotType < NOPLOT &&
     size != this->Private->ChartSettings[plotType]->MarkerSize)
    {
    this->Private->ChartSettings[plotType]->MarkerSize = size;

    if (plotType == ACTIVEPLOT)
      {
      // update marker size on current active plot
      vtkChart *chart = this->Private->BigChart;
      if(chart)
        {
        vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart->GetPlot(0));
        if (plot)
          {
          plot->SetMarkerSize(size);
          }
        }
      this->Modified();
      }
    else if (plotType == SCATTERPLOT)
      {
      int plotCount = this->GetSize().GetX();

      for(int i = 0; i < plotCount - 1; i++)
        {
        for(int j = 0; j < plotCount - 1; j++)
          {
          if(this->GetPlotType(i, j) == SCATTERPLOT &&
             this->GetChart(vtkVector2i(i, j)))
            {
            vtkChart *chart = this->GetChart(vtkVector2i(i, j));
            vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart
                                                              ->GetPlot(0));
            if (plot)
              {
              plot->SetMarkerSize(size);
              }
            }
          }
        }
      this->Modified();
      }
    }
}

bool vtkScatterPlotMatrix::Hit(const vtkContextMouseEvent &)
{
  return true;
}

bool vtkScatterPlotMatrix::MouseMoveEvent(const vtkContextMouseEvent &)
{
  // Eat the event, don't do anything for now...
  return true;
}

bool vtkScatterPlotMatrix::MouseButtonPressEvent(const vtkContextMouseEvent &)
{
  return true;
}

bool vtkScatterPlotMatrix::MouseButtonReleaseEvent(
    const vtkContextMouseEvent &mouse)
{
  // Work out which scatter plot was clicked - make that one the active plot.
  vtkVector2i pos = this->GetChartIndex(mouse.GetPos());

  if(pos.GetX() == -1 || pos.GetX() + pos.GetY() + 1 >= this->Size.GetX())
    {
    // We didn't click a chart in the bottom-left triangle of the matrix.
    return true;
    }

  // If the left button was used, hyperjump, if the right was used full path.
  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
    if (this->NumberOfFrames == 0)
      {
      this->SetActivePlot(pos);
      return true;
      }
    this->Private->AnimationPath.clear();
    bool horizontalFirst = pos[0] > this->ActivePlot[0] ? false : true;
    if (horizontalFirst)
      {
      if (pos[0] != this->ActivePlot[0])
        {
        this->Private->AnimationPath.push_back(vtkVector2i(pos[0],
                                                           this->ActivePlot[1]));
        }
      }
    else
      {
      if (pos[1] != this->ActivePlot[1])
        {
        this->Private->AnimationPath.push_back(vtkVector2i(this->ActivePlot[0],
                                                           pos[1]));
        }
      }
    if ((this->Private->AnimationPath.size() == 1 &&
         this->Private->AnimationPath.back() != pos) ||
        (this->Private->AnimationPath.size() == 0 && this->ActivePlot != pos))
      {
      this->Private->AnimationPath.push_back(pos);
      }
    if (this->Private->AnimationPath.size() > 0)
      {
      this->InvokeEvent(vtkCommand::CreateTimerEvent);
      this->StartAnimation(mouse.GetInteractor());
      }
    }
  else if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON)
    {
    if (this->NumberOfFrames == 0)
      {
      this->SetActivePlot(pos);
      return true;
      }
    this->UpdateAnimationPath(pos);
    if (this->Private->AnimationPath.size() > 0)
      {
      this->InvokeEvent(vtkCommand::CreateTimerEvent);
      this->StartAnimation(mouse.GetInteractor());
      }
    else
      {
      this->SetActivePlot(pos);
      }
    }

  return true;
}

void vtkScatterPlotMatrix::SetNumberOfFrames(int frames)
{
  this->NumberOfFrames = frames;
}

int vtkScatterPlotMatrix::GetNumberOfFrames()
{
  return this->NumberOfFrames;
}

void vtkScatterPlotMatrix::ClearAnimationPath()
{
  this->Private->AnimationPath.clear();
}

vtkIdType vtkScatterPlotMatrix::GetNumberOfAnimationPathElements()
{
  return static_cast<vtkIdType>(this->Private->AnimationPath.size());
}

vtkVector2i vtkScatterPlotMatrix::GetAnimationPathElement(vtkIdType i)
{
  return this->Private->AnimationPath.at(i);
}

bool vtkScatterPlotMatrix::AddAnimationPath(const vtkVector2i &move)
{
  vtkVector2i pos = this->ActivePlot;
  if (this->Private->AnimationPath.size())
    {
    pos = this->Private->AnimationPath.back();
    }
  if (move.GetX() != pos.GetX() && move.GetY() != pos.GetY())
    {
    // Can only move in x or y, not both. Do not append the element.
    return false;
    }
  else
    {
    this->Private->AnimationPath.push_back(move);
    return true;
    }
}

bool vtkScatterPlotMatrix::BeginAnimationPath(vtkRenderWindowInteractor* interactor)
{
  if (interactor && this->Private->AnimationPath.size())
    {
    this->StartAnimation(interactor);
    return true;
    }
  else
    {
    return false;
    }
}

int vtkScatterPlotMatrix::GetPlotType(const vtkVector2i &pos)
{
  int plotCount = this->GetSize().GetX();

  if(pos.GetX() + pos.GetY() + 1 < plotCount)
    {
    return SCATTERPLOT;
    }
  else if(pos.GetX() + pos.GetY() + 1 == plotCount)
    {
    return HISTOGRAM;
    }
  else if(pos.GetX() == pos.GetY() &&
          pos.GetX() == static_cast<int>(plotCount / 2.0) + plotCount % 2)
    {
    return ACTIVEPLOT;
    }
  else
    {
      return NOPLOT;
    }
}

int vtkScatterPlotMatrix::GetPlotType(int row, int column)
{
  return this->GetPlotType(vtkVector2i(row, column));
}

void vtkScatterPlotMatrix::UpdateAxes()
{
  if (!this->Input)
    {
    return;
    }
  // We need to iterate through all visible columns and set up the axis ranges.
  vtkAxis *axis(this->Private->TestAxis.GetPointer());
  axis->SetPoint1(0, 0);
  axis->SetPoint2(0, 200);
  for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
    {
    double range[2] = { 0, 0 };
    std::string name(this->VisibleColumns->GetValue(i));
    vtkDataArray *arr =
        vtkDataArray::SafeDownCast(this->Input->GetColumnByName(name.c_str()));
    if (arr)
      {
      PIMPL::ColumnSetting settings;
      arr->GetRange(range);
      // Apply a little padding either side of the ranges.
      range[0] = range[0] - (0.01 * range[0]);
      range[1] = range[1] + (0.01 * range[1]);
      axis->SetUnscaledRange(range);
      axis->AutoScale();
      settings.min = axis->GetUnscaledMinimum();
      settings.max = axis->GetUnscaledMaximum();
      settings.nTicks = axis->GetNumberOfTicks();
      settings.title = name;
      this->Private->ColumnSettings[name] = settings;
      }
    else
      {
      vtkDebugMacro(<< "No valid data array available. " << name);
      }
    }
}

vtkStdString vtkScatterPlotMatrix::GetColumnName(int column)
{
  assert(column < this->VisibleColumns->GetNumberOfTuples());
  return this->VisibleColumns->GetValue(column);
}

vtkStdString vtkScatterPlotMatrix::GetRowName(int row)
{
  assert(row < this->VisibleColumns->GetNumberOfTuples());
  return this->VisibleColumns->GetValue(this->Size.GetY() - row - 1);
}

void vtkScatterPlotMatrix::ApplyAxisSetting(vtkChart *chart,
                                            const vtkStdString &x,
                                            const vtkStdString &y)
{
  PIMPL::ColumnSetting &xSettings = this->Private->ColumnSettings[x];
  PIMPL::ColumnSetting &ySettings = this->Private->ColumnSettings[y];
  vtkAxis *axis = chart->GetAxis(vtkAxis::BOTTOM);
  axis->SetUnscaledRange(xSettings.min, xSettings.max);
  axis->SetBehavior(vtkAxis::FIXED);
  axis = chart->GetAxis(vtkAxis::TOP);
  axis->SetUnscaledRange(xSettings.min, xSettings.max);
  axis->SetBehavior(vtkAxis::FIXED);
  axis = chart->GetAxis(vtkAxis::LEFT);
  axis->SetUnscaledRange(ySettings.min, ySettings.max);
  axis->SetBehavior(vtkAxis::FIXED);
  axis = chart->GetAxis(vtkAxis::RIGHT);
  axis->SetUnscaledRange(ySettings.min, ySettings.max);
  axis->SetBehavior(vtkAxis::FIXED);
}

void vtkScatterPlotMatrix::UpdateLayout()
{
  // We want scatter plots on the lower-left triangle, then histograms along
  // the diagonal and a big plot in the top-right. The basic layout is,
  //
  // 3 H   +++
  // 2 S H +++
  // 1 S S H
  // 0 S S S H
  //   0 1 2 3
  //
  // Where the indices are those of the columns. The indices of the charts
  // originate in the bottom-left. S = scatter plot, H = histogram and + is the
  // big chart.
  int n = this->Size.GetX();
  this->UpdateAxes();
  this->Private->BigChart3D->SetAnnotationLink(this->Private->Link.GetPointer());
  for (int i = 0; i < n; ++i)
    {
    vtkStdString column = this->GetColumnName(i);
    for (int j = 0; j < n; ++j)
      {
      vtkStdString row = this->GetRowName(j);
      vtkVector2i pos(i, j);
      if (this->GetPlotType(pos) == SCATTERPLOT)
        {
        vtkChart* chart = this->GetChart(pos);
        this->ApplyAxisSetting(chart, column, row);
        chart->ClearPlots();
        chart->SetInteractive(false);
        chart->SetAnnotationLink(this->Private->Link.GetPointer());
        // Lower-left triangle - scatter plots.
        chart->SetActionToButton(vtkChart::PAN, -1);
        chart->SetActionToButton(vtkChart::ZOOM, -1);
        chart->SetActionToButton(vtkChart::SELECT, -1);
        vtkPlot *plot = chart->AddPlot(vtkChart::POINTS);
        plot->SetInputData(this->Input.GetPointer(), column, row);
        plot->SetPen(this->Private->ChartSettings[SCATTERPLOT]
                     ->PlotPen.GetPointer());
        // set plot marker size and style
        vtkPlotPoints *plotPoints = vtkPlotPoints::SafeDownCast(plot);
        plotPoints->SetMarkerSize(this->Private->ChartSettings[SCATTERPLOT]
                                  ->MarkerSize);
        plotPoints->SetMarkerStyle(this->Private->ChartSettings[SCATTERPLOT]
                                   ->MarkerStyle);
        }
      else if (this->GetPlotType(pos) == HISTOGRAM)
        {
        // We are on the diagonal - need a histogram plot.
        vtkChart* chart = this->GetChart(pos);
        chart->SetInteractive(false);
        this->ApplyAxisSetting(chart, column, row);
        chart->ClearPlots();
        vtkPlot *plot = chart->AddPlot(vtkChart::BAR);
        plot->SetPen(this->Private->ChartSettings[HISTOGRAM]
                     ->PlotPen.GetPointer());
        plot->SetBrush(this->Private->ChartSettings[HISTOGRAM]
                       ->PlotBrush.GetPointer());
        vtkStdString name(this->VisibleColumns->GetValue(i));
        plot->SetInputData(this->Private->Histogram.GetPointer(),
                           name + "_extents", name + "_pops");
        vtkAxis *axis = chart->GetAxis(vtkAxis::TOP);
        axis->SetTitle(name);
        axis->SetLabelsVisible(false);
        // Show the labels on the right for populations of bins.
        axis = chart->GetAxis(vtkAxis::RIGHT);
        axis->SetLabelsVisible(true);
        axis->SetBehavior(vtkAxis::AUTO);
        axis->AutoScale();
        // Set the plot corner to the top-right
        vtkChartXY *xy = vtkChartXY::SafeDownCast(chart);
        if (xy)
          {
          xy->SetBarWidthFraction(1.0);
          xy->SetPlotCorner(plot, 2);
          }

        // set background color to light gray
        xy->SetBackgroundBrush(this->Private->ChartSettings[HISTOGRAM]
                               ->BackgroundBrush.GetPointer());
        }
      else if (this->GetPlotType(pos) == ACTIVEPLOT)
        {
        // This big plot in the top-right
        this->Private->BigChart = this->GetChart(pos);
        this->Private->BigChart->SetAnnotationLink(
              this->Private->Link.GetPointer());
        this->Private->BigChart->AddObserver(
          vtkCommand::SelectionChangedEvent, this,
          &vtkScatterPlotMatrix::BigChartSelectionCallback);

        // set tooltip item
        vtkChartXY *chartXY =
          vtkChartXY::SafeDownCast(this->Private->BigChart.GetPointer());
        if(chartXY)
          {
          chartXY->SetTooltip(this->Private->TooltipItem);
          }

        this->SetChartSpan(pos, vtkVector2i(n - i, n - j));
        this->SetActivePlot(vtkVector2i(0, n - 2));
        }
      // Only show bottom axis label for bottom plots
      if (j > 0)
        {
        vtkAxis *axis = this->GetChart(pos)->GetAxis(vtkAxis::BOTTOM);
        axis->SetTitle("");
        axis->SetLabelsVisible(false);
        axis->SetBehavior(vtkAxis::FIXED);
        }
      else
        {
        vtkAxis *axis = this->GetChart(pos)->GetAxis(vtkAxis::BOTTOM);
        axis->SetTitle(this->VisibleColumns->GetValue(i));
        axis->SetLabelsVisible(false);
        this->AttachAxisRangeListener(axis);
        }
      // Only show the left axis labels for left-most plots
      if (i > 0)
        {
        vtkAxis *axis = this->GetChart(pos)->GetAxis(vtkAxis::LEFT);
        axis->SetTitle("");
        axis->SetLabelsVisible(false);
        axis->SetBehavior(vtkAxis::FIXED);
        }
      else
        {
        vtkAxis *axis = this->GetChart(pos)->GetAxis(vtkAxis::LEFT);
        axis->SetTitle(this->VisibleColumns->GetValue(n - j - 1));
        axis->SetLabelsVisible(false);
        this->AttachAxisRangeListener(axis);
        }
      }
    }
}

void vtkScatterPlotMatrix::AttachAxisRangeListener(vtkAxis* axis)
{
  axis->AddObserver(vtkChart::UpdateRange, this,
                    &vtkScatterPlotMatrix::AxisRangeForwarderCallback);
}

void vtkScatterPlotMatrix::AxisRangeForwarderCallback(vtkObject*,
                                                      unsigned long, void*)
{
  // Only set on the end axes, and propagated to all other matching axes.
  double r[2];
  int n = this->GetSize().GetX() - 1;
  for (int i = 0; i < n; ++i)
    {
    this->GetChart(vtkVector2i(i, 0))
      ->GetAxis(vtkAxis::BOTTOM)
      ->GetUnscaledRange(r);
    for (int j = 1; j < n - i; ++j)
      {
      this->GetChart(vtkVector2i(i, j))
        ->GetAxis(vtkAxis::BOTTOM)
        ->SetUnscaledRange(r);
      }
    this->GetChart(vtkVector2i(i, n-i))
      ->GetAxis(vtkAxis::TOP)
      ->SetUnscaledRange(r);
    this->GetChart(vtkVector2i(0, i))
      ->GetAxis(vtkAxis::LEFT)
      ->GetUnscaledRange(r);
    for (int j = 1; j < n - i; ++j)
      {
      this->GetChart(vtkVector2i(j, i))
        ->GetAxis(vtkAxis::LEFT)
        ->SetUnscaledRange(r);
      }
    }
}

void vtkScatterPlotMatrix::BigChartSelectionCallback(vtkObject*,
  unsigned long event, void*)
{
  // forward the SelectionChangedEvent from the Big Chart plot
  this->InvokeEvent(event);
}

void vtkScatterPlotMatrix::SetTitle(const vtkStdString& title)
{
  if (this->Title != title)
    {
    this->Title = title;
    this->Modified();
    }
}

vtkStdString vtkScatterPlotMatrix::GetTitle()
{
  return this->Title;
}

void vtkScatterPlotMatrix::SetTitleProperties(vtkTextProperty *prop)
{
  if (this->TitleProperties != prop)
    {
    this->TitleProperties = prop;
    this->Modified();
    }
}

vtkTextProperty* vtkScatterPlotMatrix::GetTitleProperties()
{
  return this->TitleProperties.GetPointer();
}

void vtkScatterPlotMatrix::SetAxisLabelProperties(int plotType,
                                                  vtkTextProperty *prop)
{
  if (plotType >= 0 && plotType < vtkScatterPlotMatrix::NOPLOT &&
      this->Private->ChartSettings[plotType]->LabelFont != prop)
    {
    this->Private->ChartSettings[plotType]->LabelFont = prop;
    this->Modified();
    }
}

vtkTextProperty* vtkScatterPlotMatrix::GetAxisLabelProperties(int plotType)
{
  if (plotType >= 0 && plotType < vtkScatterPlotMatrix::NOPLOT)
    {
    return this->Private->ChartSettings[plotType]->LabelFont.GetPointer();
    }
  return NULL;
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetBackgroundColor(int plotType,
                                              const vtkColor4ub& color)
{
  if (plotType >= 0 && plotType < vtkScatterPlotMatrix::NOPLOT)
    {
    this->Private->ChartSettings[plotType]->BackgroundBrush->SetColor(color);
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetAxisColor(int plotType,
                                        const vtkColor4ub& color)
{
  if (plotType >= 0 && plotType < vtkScatterPlotMatrix::NOPLOT)
    {
    this->Private->ChartSettings[plotType]->AxisColor = color;
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetGridVisibility(int plotType, bool visible)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->ShowGrid = visible;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetGridColor(int plotType,
                                        const vtkColor4ub& color)
{
  if (plotType >= 0 && plotType < vtkScatterPlotMatrix::NOPLOT)
    {
    this->Private->ChartSettings[plotType]->GridColor = color;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetAxisLabelVisibility(int plotType, bool visible)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->ShowAxisLabels = visible;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetAxisLabelNotation(int plotType, int notation)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->LabelNotation = notation;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetAxisLabelPrecision(int plotType, int precision)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->LabelPrecision = precision;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetTooltipNotation(int plotType, int notation)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->TooltipNotation = notation;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetTooltipPrecision(int plotType, int precision)
{
  if(plotType!= NOPLOT)
    {
    this->Private->ChartSettings[plotType]->TooltipPrecision = precision;
    // How to update
    this->Modified();
    }
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetScatterPlotSelectedRowColumnColor(
    const vtkColor4ub& color)
{
  this->Private->SelectedRowColumnBGBrush->SetColor(color);
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetScatterPlotSelectedActiveColor(
    const vtkColor4ub& color)
{
  this->Private->SelectedChartBGBrush->SetColor(color);
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::UpdateChartSettings(int plotType)
{
  if (plotType == HISTOGRAM)
    {
    int plotCount = this->GetSize().GetX();

    for(int i = 0; i < plotCount; i++)
      {
      vtkChart *chart = this->GetChart(vtkVector2i(i, plotCount - i - 1));
      this->Private->UpdateAxis(chart->GetAxis(vtkAxis::TOP),
                                this->Private->ChartSettings[HISTOGRAM]);
      this->Private->UpdateAxis(chart->GetAxis(vtkAxis::RIGHT),
                                this->Private->ChartSettings[HISTOGRAM]);
      this->Private->UpdateChart(chart, this->Private->ChartSettings[HISTOGRAM]);
      }
    }
  else if (plotType == SCATTERPLOT)
    {
    int plotCount = this->GetSize().GetX();

    for (int i = 0; i < plotCount - 1; i++)
      {
      for (int j = 0; j < plotCount - 1; j++)
        {
        if (this->GetPlotType(i, j) == SCATTERPLOT)
          {
          vtkChart *chart = this->GetChart(vtkVector2i(i, j));
          bool updateleft = i==0 ? true : false;
          bool updatebottom = j==0 ? true : false;
          this->Private->UpdateAxis(chart->GetAxis(vtkAxis::LEFT),
            this->Private->ChartSettings[SCATTERPLOT], updateleft);
          this->Private->UpdateAxis(chart->GetAxis(vtkAxis::BOTTOM),
            this->Private->ChartSettings[SCATTERPLOT], updatebottom);
          }
        }
      }
    }
  else if (plotType == ACTIVEPLOT && this->Private->BigChart)
    {
    this->Private->UpdateAxis(this->Private->BigChart->GetAxis(vtkAxis::TOP),
                              this->Private->ChartSettings[ACTIVEPLOT]);
    this->Private->UpdateAxis(this->Private->BigChart->GetAxis(vtkAxis::RIGHT),
                              this->Private->ChartSettings[ACTIVEPLOT]);
    this->Private->UpdateChart(this->Private->BigChart,
                               this->Private->ChartSettings[ACTIVEPLOT]);
    this->Private->BigChart->SetSelectionMode(this->SelectionMode);
    }

}
//-----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetSelectionMode(int selMode)
  {
  if (this->SelectionMode == selMode ||
      selMode < vtkContextScene::SELECTION_NONE ||
      selMode > vtkContextScene::SELECTION_TOGGLE)
    {
    return;
    }
  this->SelectionMode = selMode;
  if(this->Private->BigChart)
    {
    this->Private->BigChart->SetSelectionMode(selMode);
    }

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::UpdateSettings()
{

// TODO: Should update the Scatter plot title

  this->UpdateChartSettings(ACTIVEPLOT);
  this->UpdateChartSettings(HISTOGRAM);
  this->UpdateChartSettings(SCATTERPLOT);
}

//----------------------------------------------------------------------------
bool vtkScatterPlotMatrix::GetGridVisibility(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->ShowGrid;
}

//----------------------------------------------------------------------------
vtkColor4ub vtkScatterPlotMatrix::GetBackgroundColor(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->BackgroundBrush
      ->GetColorObject();
}

//----------------------------------------------------------------------------
vtkColor4ub vtkScatterPlotMatrix::GetAxisColor(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->AxisColor;
}

//----------------------------------------------------------------------------
vtkColor4ub vtkScatterPlotMatrix::GetGridColor(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->GridColor;
}

//----------------------------------------------------------------------------
bool vtkScatterPlotMatrix::GetAxisLabelVisibility(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->ShowAxisLabels;
}

//----------------------------------------------------------------------------
int vtkScatterPlotMatrix::GetAxisLabelNotation(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->LabelNotation;
}

//----------------------------------------------------------------------------
int vtkScatterPlotMatrix::GetAxisLabelPrecision(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->LabelPrecision;
}

//----------------------------------------------------------------------------
int vtkScatterPlotMatrix::GetTooltipNotation(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->TooltipNotation;
}

int vtkScatterPlotMatrix::GetTooltipPrecision(int plotType)
{
  assert(plotType != NOPLOT);
  return this->Private->ChartSettings[plotType]->TooltipPrecision;
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetTooltip(vtkTooltipItem *tooltip)
{
  if(tooltip != this->Private->TooltipItem)
    {
    this->Private->TooltipItem = tooltip;
    this->Modified();

    vtkChartXY *chartXY =
      vtkChartXY::SafeDownCast(this->Private->BigChart.GetPointer());

    if(chartXY)
      {
      chartXY->SetTooltip(tooltip);
      }
    }
}

//----------------------------------------------------------------------------
vtkTooltipItem* vtkScatterPlotMatrix::GetTooltip() const
{
  return this->Private->TooltipItem;
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::SetIndexedLabels(vtkStringArray *labels)
{
  if(labels != this->Private->IndexedLabelsArray)
    {
    this->Private->IndexedLabelsArray = labels;
    this->Modified();

    if(this->Private->BigChart)
      {
      vtkPlot *plot = this->Private->BigChart->GetPlot(0);

      if(plot)
        {
        plot->SetIndexedLabels(labels);
        }
      }
    }
}

//----------------------------------------------------------------------------
vtkStringArray* vtkScatterPlotMatrix::GetIndexedLabels() const
{
  return this->Private->IndexedLabelsArray;
}

//----------------------------------------------------------------------------
vtkColor4ub vtkScatterPlotMatrix::GetScatterPlotSelectedRowColumnColor()
{
  return this->Private->SelectedRowColumnBGBrush->GetColorObject();
}

//----------------------------------------------------------------------------
vtkColor4ub vtkScatterPlotMatrix::GetScatterPlotSelectedActiveColor()
{
  return this->Private->SelectedChartBGBrush->GetColorObject();
}

//----------------------------------------------------------------------------
vtkChart * vtkScatterPlotMatrix::GetMainChart()
{
  return this->Private->BigChart.GetPointer();
}

//----------------------------------------------------------------------------
void vtkScatterPlotMatrix::PrintSelf(ostream &os, vtkIndent indent)
{
  Superclass::PrintSelf(os, indent);

  os << indent << "NumberOfBins: " << this->NumberOfBins << endl;
  os << indent << "Title: " << this->Title << endl;
  os << indent << "SelectionMode: " << this->SelectionMode << endl;
}
