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

  Program:   ParaView
  Module:    vtkPVImageChartRepresentation.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkPVImageChartRepresentation.h"

#include "vtkChartHistogram2D.h"
#include "vtkChartXY.h"
#include "vtkColorLegend.h"
#include "vtkColorTransferFunction.h"
#include "vtkCommand.h"
#include "vtkContextView.h"
#include "vtkDataSetAttributes.h"
#include "vtkImageData.h"
#include "vtkImageMapToColors.h"
#include "vtkInformation.h"
#include "vtkLookupTable.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkObjectFactory.h"
#include "vtkPVContextView.h"
#include "vtkPVXYChartView.h"
#include "vtkPlotHistogram2D.h"
#include "vtkPointData.h"
#include "vtkReductionFilter.h"
#include "vtkSMPVRepresentationProxy.h"

vtkStandardNewMacro(vtkPVImageChartRepresentation);
vtkCxxSetObjectMacro(vtkPVImageChartRepresentation, LookupTable, vtkScalarsToColors);

//----------------------------------------------------------------------------
vtkPVImageChartRepresentation::vtkPVImageChartRepresentation()
  : LookupTable(nullptr)
{
  vtkNew<vtkColorTransferFunction> lut;
  lut->SetColorSpaceToDiverging();
  lut->AddRGBPoint(0, 59. / 255., 76. / 255., 192. / 255.);
  lut->AddRGBPoint(1, 221. / 255., 221. / 255., 221. / 255.);
  lut->AddRGBPoint(2, 180. / 255., 4. / 255., 38. / 255.);
  SetLookupTable(lut.Get());
}

//----------------------------------------------------------------------------
vtkPVImageChartRepresentation::~vtkPVImageChartRepresentation()
{
  SetLookupTable(nullptr);
}

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

//----------------------------------------------------------------------------
bool vtkPVImageChartRepresentation::AddToView(vtkView* view)
{
  if (!this->Superclass::AddToView(view))
  {
    return false;
  }

  if (this->GetChart())
  {
    this->GetChart()->SetVisible(this->GetVisibility());
  }

  return true;
}

//----------------------------------------------------------------------------
bool vtkPVImageChartRepresentation::RemoveFromView(vtkView* view)
{
  if (this->GetChart())
  {
    this->GetChart()->GetPlot(0)->SetInputData(nullptr);
    this->GetChart()->SetVisible(false);
  }

  return this->Superclass::RemoveFromView(view);
}

//----------------------------------------------------------------------------
void vtkPVImageChartRepresentation::SetVisibility(bool visible)
{
  this->Superclass::SetVisibility(visible);

  vtkChartHistogram2D* chart = this->GetChart();
  if (chart && !visible)
  {
    chart->SetVisible(false);
  }

  this->Modified();
}

//----------------------------------------------------------------------------
vtkChartHistogram2D* vtkPVImageChartRepresentation::GetChart()
{
  if (this->ContextView)
  {
    return vtkChartHistogram2D::SafeDownCast(this->ContextView->GetContextItem());
  }

  return nullptr;
}

//----------------------------------------------------------------------------
void vtkPVImageChartRepresentation::SetInputArrayToProcess(
  int idx, int port, int connection, int fieldAssociation, const char* name)
{
  this->Superclass::SetInputArrayToProcess(idx, port, connection, fieldAssociation, name);
}

//----------------------------------------------------------------------------
void vtkPVImageChartRepresentation::PrepareForRendering()
{
  this->Superclass::PrepareForRendering();

  if (this->LocalOutput)
  {
    for (unsigned int idx = 0; idx < this->LocalOutput->GetNumberOfBlocks(); idx++)
    {
      this->LocalGrid = vtkImageData::SafeDownCast(this->LocalOutput->GetBlock(idx));
      if (this->LocalGrid)
      {
        break;
      }
    }
  }

  vtkChartHistogram2D* chart = this->GetChart();
  dynamic_cast<vtkColorLegend*>(chart->GetLegend())->DrawBorderOn();

  vtkPlotHistogram2D* gridPlot = nullptr;
  for (int i = 0; i < chart->GetNumberOfPlots(); i++)
  {
    if (vtkPlotHistogram2D::SafeDownCast(chart->GetPlot(i)))
    {
      gridPlot = vtkPlotHistogram2D::SafeDownCast(chart->GetPlot(i));
      break;
    }
  }

  if (!gridPlot)
  {
    gridPlot = vtkPlotHistogram2D::New();
    chart->AddPlot(gridPlot);
    chart->SetInputData(this->LocalGrid);
    gridPlot->Delete();
  }

  gridPlot->SetInputData(this->LocalGrid);
  gridPlot->SetVisible(this->LocalGrid != nullptr);

  if (this->LocalGrid)
  {
    double range[2];
    this->LocalGrid->GetScalarRange(range);

    gridPlot->SetTransferFunction(this->LookupTable);
    if (gridPlot->GetTransferFunction())
    {
      vtkLookupTable* lut = vtkLookupTable::SafeDownCast(gridPlot->GetTransferFunction());
      vtkColorTransferFunction* tf =
        vtkColorTransferFunction::SafeDownCast(gridPlot->GetTransferFunction());
      if (lut)
      {
        lut->SetTableRange(range);
        lut->Build();
      }
      else if (tf)
      {
        double oldRange[2];
        tf->GetRange(oldRange);
        int numberOfNodes = tf->GetSize();
        double* newNodes = new double[6 * numberOfNodes];
        for (int i = 0; i < numberOfNodes; ++i)
        {
          double* newNode = &newNodes[6 * i];
          tf->GetNodeValue(i, newNode);
          newNode[0] =
            (range[1] - range[0]) * (newNode[0] - oldRange[0]) / (oldRange[1] - oldRange[0]) +
            range[0];
        }
        tf->RemoveAllPoints();
        for (int i = 0; i < numberOfNodes; ++i)
        {
          double* newNode = &newNodes[6 * i];
          tf->AddRGBPoint(newNode[0], newNode[1], newNode[2], newNode[3], newNode[4], newNode[5]);
        }
        chart->SetTransferFunction(tf);
        delete[] newNodes;
      }
    }
  }
}

//----------------------------------------------------------------------------
vtkSmartPointer<vtkDataObject> vtkPVImageChartRepresentation::ReduceDataToRoot(vtkDataObject* data)
{
  vtkNew<vtkReductionFilter> reductionFilter;
  reductionFilter->SetInputData(data);
  reductionFilter->Update();
  return reductionFilter->GetOutputDataObject(0);
}
