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

  Program:   ParaView
  Module:    vtkStructuredGridVolumeRepresentation.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 "vtkStructuredGridVolumeRepresentation.h"

#include "vtkAlgorithmOutput.h"
#include "vtkCommunicator.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMath.h"
#include "vtkMultiProcessController.h"
#include "vtkObjectFactory.h"
#include "vtkPVRenderView.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStructuredGrid.h"
#include "vtkTableExtentTranslator.h"

#include <assert.h>

vtkStandardNewMacro(vtkStructuredGridVolumeRepresentation);
//----------------------------------------------------------------------------
vtkStructuredGridVolumeRepresentation::vtkStructuredGridVolumeRepresentation()
{
  this->UseDataParititions = false;
  this->TableExtentTranslator = vtkTableExtentTranslator::New();
}

//----------------------------------------------------------------------------
vtkStructuredGridVolumeRepresentation::~vtkStructuredGridVolumeRepresentation()
{
  this->TableExtentTranslator->Delete();
}

//----------------------------------------------------------------------------
void vtkStructuredGridVolumeRepresentation::SetUseDataParititions(bool val)
{
  if (this->UseDataParititions != val)
    {
    this->UseDataParititions = val;
    this->MarkModified();
    }
}

//----------------------------------------------------------------------------
int vtkStructuredGridVolumeRepresentation::FillInputPortInformation(
  int, vtkInformation* info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkStructuredGrid");
  info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
  return 1;
}

//----------------------------------------------------------------------------
int vtkStructuredGridVolumeRepresentation::RequestData(vtkInformation* request,
    vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  if (!this->Superclass::RequestData(request, inputVector, outputVector))
    {
    return 0;
    }

  this->TableExtentTranslator->SetNumberOfPieces(0);
  this->TableExtentTranslator->SetNumberOfPiecesInTable(0);

  if (inputVector[0]->GetNumberOfInformationObjects()==1 &&
    this->UseDataParititions)
    {
    // reduce bounds across processes in parallel.
    vtkMultiProcessController* controller =
      vtkMultiProcessController::GetGlobalController();
    int numProcs = controller->GetNumberOfProcesses();
    if (numProcs > 1)
      {
      vtkStructuredGrid* grid = vtkStructuredGrid::GetData(inputVector[0], 0);
      assert(grid != NULL);

      // AllGather the local extents on each process and then build up the
      // vtkTableExtentTranslator. vtkTableExtentTranslator is merely used as
      // the datastructure to pass process->extent mapping to the rendering
      // code.
      this->TableExtentTranslator->SetNumberOfPieces(numProcs);
      this->TableExtentTranslator->SetNumberOfPiecesInTable(numProcs);

      int *gatheredExtents = new int[numProcs * 6];
      int myExtents[6];
      grid->GetExtent(myExtents);
      controller->AllGather(myExtents, gatheredExtents, 6);
      for (int cc=0; cc < numProcs; cc++)
        {
        this->TableExtentTranslator->SetExtentForPiece(
          cc, gatheredExtents + 6*cc);
        }
      delete[] gatheredExtents;

      //if (controller->GetLocalProcessId() == 0)
      //  {
      //  this->TableExtentTranslator->Print(cout);
      //  }

      // Reduce bounds globally.
      double bounds_max[3] = {VTK_DOUBLE_MIN, VTK_DOUBLE_MIN};
      double bounds_min[3] = {VTK_DOUBLE_MAX, VTK_DOUBLE_MAX};
      if (vtkMath::AreBoundsInitialized(this->DataBounds))
        {
        bounds_min[0] = this->DataBounds[0];
        bounds_min[1] = this->DataBounds[2];
        bounds_min[2] = this->DataBounds[4];
        bounds_max[0] = this->DataBounds[1];
        bounds_max[1] = this->DataBounds[3];
        bounds_max[2] = this->DataBounds[5];
        }

      double reduced_bounds_max[3], reduced_bounds_min[3];
      controller->AllReduce(bounds_max, reduced_bounds_max, 3,
        vtkCommunicator::MAX_OP);
      controller->AllReduce(bounds_min, reduced_bounds_min, 3,
        vtkCommunicator::MIN_OP);

      this->DataBounds[0] = reduced_bounds_min[0];
      this->DataBounds[2] = reduced_bounds_min[1];
      this->DataBounds[4] = reduced_bounds_min[2];
      this->DataBounds[1] = reduced_bounds_max[0];
      this->DataBounds[3] = reduced_bounds_max[1];
      this->DataBounds[5] = reduced_bounds_max[2];
      }
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkStructuredGridVolumeRepresentation::ProcessViewRequest(
  vtkInformationRequestKey* request_type,
  vtkInformation* inInfo, vtkInformation* outInfo)
{
  if (!this->Superclass::ProcessViewRequest(request_type, inInfo, outInfo))
    {
    return 0;
    }

  if (request_type == vtkPVView::REQUEST_UPDATE())
    {
    if (this->GetNumberOfInputConnections(0) == 1 &&
        this->UseDataParititions &&
        this->TableExtentTranslator->GetNumberOfPiecesInTable() > 0)
      {
      vtkAlgorithmOutput* connection = this->GetInputConnection(0, 0);
      vtkAlgorithm* inputAlgo = connection->GetProducer();
      vtkStreamingDemandDrivenPipeline* sddp =
        vtkStreamingDemandDrivenPipeline::SafeDownCast(inputAlgo->GetExecutive());

      int whole_extent[6] = {1, -1, 1, -1, 1, -1};
      sddp->GetWholeExtent(sddp->GetOutputInformation(connection->GetIndex()),
        whole_extent);

      double origin[3] = { this->DataBounds[0], this->DataBounds[2],
        this->DataBounds[4] };

      double spacing[3] = {
        (this->DataBounds[1] - this->DataBounds[0]) / (whole_extent[1] - whole_extent[0] + 1),
        (this->DataBounds[3] - this->DataBounds[2]) / (whole_extent[3] - whole_extent[2] + 1),
        (this->DataBounds[5] - this->DataBounds[4]) / (whole_extent[5] - whole_extent[4] + 1)
      };

      vtkPVRenderView::SetOrderedCompositingInformation(inInfo, this,
        this->TableExtentTranslator, whole_extent, origin, spacing);
      }
    else
      {
      double origin[3] = {0, 0, 0};
      double spacing[3] = {1, 1, 1};
      int whole_extent[6] = {1, -1, 1, -1, 1, -1};

      // Unset the ordered compositing info, so that vtkPVRenderView will
      // redistribute the unstructured grid as needed to volume render it.
      vtkPVRenderView::SetOrderedCompositingInformation(inInfo, this,
        NULL, whole_extent, origin, spacing);
      }

    // this is essential since this->Superclass::ProcessViewRequest(..) marks
    // the data as redistributable, which it isn't in our case. We don't want
    // the unstructured-grid we created from the structured-grid to be
    // redistributed.
    vtkPVRenderView::MarkAsRedistributable(inInfo, this, false);
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkStructuredGridVolumeRepresentation::PrintSelf(
  ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "UseDataParititions: " << this->UseDataParititions << endl;
}
