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

  Program:   Visualization Toolkit
  Module:    vtkMPIMoveData.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 "vtkMPIMoveData.h"

#include "vtkAppendCompositeDataLeaves.h"
#include "vtkAppendFilter.h"
#include "vtkAppendPolyData.h"
#include "vtkCellData.h"
#include "vtkCharArray.h"
#include "vtkCompositeDataSet.h"
#include "vtkDataSetReader.h"
#include "vtkDirectedGraph.h"
#include "vtkGenericDataObjectReader.h"
#include "vtkGenericDataObjectWriter.h"
#include "vtkGraphReader.h"
#include "vtkGraphWriter.h"
#include "vtkImageAppend.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMergeGraphs.h"
#include "vtkMPIMToNSocketConnection.h"
#include "vtkMultiBlockDataSet.h"
#include "vtkMultiProcessController.h"
#include "vtkObjectFactory.h"
#include "vtkOutlineFilter.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkProcessModule.h"
#include "vtkPVSession.h"
#include "vtkSmartPointer.h"
#include "vtkSocketCommunicator.h"
#include "vtkSocketController.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkTimerLog.h"
#include "vtkToolkits.h"
#include "vtkUndirectedGraph.h"
#include "vtkUnstructuredGrid.h"
#include "vtk_zlib.h"
#include <vtksys/ios/sstream>

#ifdef VTK_USE_MPI
#include "vtkMPICommunicator.h"
#include "vtkAllToNRedistributeCompositePolyData.h"
#endif

#include <vector>

bool vtkMPIMoveData::UseZLibCompression = false;

namespace
{
  static bool vtkMPIMoveDataMerge(std::vector<vtkSmartPointer<vtkDataObject> >& pieces,
    vtkDataObject* result)
    {
    if (pieces.size() == 0)
      {
      return false;
      }

    if (pieces.size() == 1)
      {
      result->ShallowCopy(pieces[0]);
      vtkImageData* id = vtkImageData::SafeDownCast(pieces[0]);
      if (id)
        {
        result->SetWholeExtent(
          static_cast<vtkImageData*>(pieces[0].GetPointer())->GetExtent());
        }
      return true;
      }

    // PolyData and Unstructured grid need different append filters.
    vtkAlgorithm* appender = NULL;
    if (vtkPolyData::SafeDownCast(result))
      {
      appender = vtkAppendPolyData::New();
      }
    else if (vtkUnstructuredGrid::SafeDownCast(result))
      {
      appender = vtkAppendFilter::New();
      }
    else if (vtkImageData::SafeDownCast(result))
      {
      vtkImageAppend* ia = vtkImageAppend::New();
      ia->PreserveExtentsOn();
      appender = ia;
      }
    else if (vtkGraph::SafeDownCast(result))
      {
      // graph has to be handled separately because it doesn't have the standard
      // append-filter API.
      vtkMergeGraphs* mergegraphs = vtkMergeGraphs::New();
      mergegraphs->SetInput(0, pieces[0]);
      std::vector<vtkSmartPointer<vtkDataObject> >::iterator iter =
        pieces.begin();
      iter++;
      for ( ; iter != pieces.end(); ++iter)
        {
        mergegraphs->SetInput(1, iter->GetPointer());
        mergegraphs->Update();

        vtkGraph* mergeResult = mergegraphs->GetOutput();
        vtkGraph* clone = mergeResult->NewInstance();
        clone->ShallowCopy(mergeResult);
        mergegraphs->SetInput(0, clone);
        clone->FastDelete();
        }
      vtkDataObject* mergeResult = mergegraphs->GetInputDataObject(0, 0);
      result->ShallowCopy(mergeResult);
      mergegraphs->Delete();
      return true;
      }
    else if (vtkCompositeDataSet::SafeDownCast(result))
      {
      // this only supports composite datasets of polydata and unstructured
      // grids.
      appender = vtkAppendCompositeDataLeaves::New();
      }
    else
      {
      vtkGenericWarningMacro(<< result->GetClassName() << " cannot be merged");
      result->ShallowCopy(pieces[0]);
      return false;
      }

    std::vector<vtkSmartPointer<vtkDataObject> >::iterator iter;
    for (iter = pieces.begin(); iter != pieces.end(); ++iter)
      {
      vtkDataSet* ds = vtkDataSet::SafeDownCast(iter->GetPointer());
      if (ds && ds->GetNumberOfPoints() == 0)
        {
        // skip empty pieces.
        continue;
        }
      appender->AddInputConnection(0, iter->GetPointer()->GetProducerPort());
      }
    appender->Update();
    result->ShallowCopy(appender->GetOutputDataObject(0));
    appender->Delete();
    return true;
    }
};


vtkStandardNewMacro(vtkMPIMoveData);

vtkCxxSetObjectMacro(vtkMPIMoveData,Controller, vtkMultiProcessController);
vtkCxxSetObjectMacro(vtkMPIMoveData,ClientDataServerSocketController, vtkMultiProcessController);
vtkCxxSetObjectMacro(vtkMPIMoveData,MPIMToNSocketConnection, vtkMPIMToNSocketConnection);
//-----------------------------------------------------------------------------
vtkMPIMoveData::vtkMPIMoveData()
{
  this->Controller = 0;
  this->ClientDataServerSocketController = 0;
  this->MPIMToNSocketConnection = 0;

  this->SetController(vtkMultiProcessController::GetGlobalController());

  this->MoveMode = vtkMPIMoveData::PASS_THROUGH;
  // This tells which server/client this object is on.
  this->Server = -1;

  // This is set on the data server and render server when we are running
  // with a render server.
  this->MPIMToNSocketConnection = 0;

  this->NumberOfBuffers = 0;
  this->BufferLengths = 0;
  this->BufferOffsets = 0;
  this->Buffers = 0;
  this->BufferTotalLength = 9;

  this->OutputDataType = VTK_POLY_DATA;

  this->UpdateNumberOfPieces = 0;
  this->UpdatePiece = 0;

  this->DeliverOutlineToClient = 0;
}

//-----------------------------------------------------------------------------
vtkMPIMoveData::~vtkMPIMoveData()
{
  this->SetController(0);
  this->SetClientDataServerSocketController(0);
  this->SetMPIMToNSocketConnection(0);
  this->ClearBuffer();
}

//----------------------------------------------------------------------------
void vtkMPIMoveData::InitializeForCommunicationForParaView()
{
  vtkProcessModule* pm = vtkProcessModule::GetProcessModule();
  if (pm == NULL)
    {
    vtkWarningMacro("No process module found.");
    return;
    }

  vtkPVSession* session = vtkPVSession::SafeDownCast(
    pm->GetActiveSession());
  if (!session)
    {
    vtkWarningMacro("No active vtkPVSession found.");
    return;
    }

  int processRoles = session->GetProcessRoles();
  if (processRoles & vtkPVSession::RENDER_SERVER)
    {
    this->SetServerToRenderServer();
    }

  if (processRoles & vtkPVSession::DATA_SERVER)
    {
    this->SetServerToDataServer();
    this->SetClientDataServerSocketController(
      session->GetController(vtkPVSession::CLIENT));
    }

  if (processRoles & vtkPVSession::CLIENT)
    {
    this->SetServerToClient();
    this->SetClientDataServerSocketController(
      session->GetController(vtkPVSession::DATA_SERVER));
    }

  this->SetController(pm->GetGlobalController());
  this->SetMPIMToNSocketConnection(
    session->GetMPIMToNSocketConnection());
}

//----------------------------------------------------------------------------
void vtkMPIMoveData::SetUseZLibCompression(bool b)
{
  vtkMPIMoveData::UseZLibCompression = b;
}

//----------------------------------------------------------------------------
bool vtkMPIMoveData::GetUseZLibCompression()
{
  return vtkMPIMoveData::UseZLibCompression;
}

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

//-----------------------------------------------------------------------------
int vtkMPIMoveData::RequestDataObject(vtkInformation*,
                                      vtkInformationVector**,
                                      vtkInformationVector* outputVector)
{
  vtkDataObject* output =
    outputVector->GetInformationObject(0)->Get(vtkDataObject::DATA_OBJECT());

  vtkDataObject* outputCopy = 0;
  if (this->OutputDataType == VTK_POLY_DATA)
    {
    if (output && output->IsA("vtkPolyData"))
      {
      return 1;
      }
    outputCopy = vtkPolyData::New();
    }
  else if (this->OutputDataType == VTK_UNSTRUCTURED_GRID)
    {
    if (output && output->IsA("vtkUnstructuredGrid"))
      {
      return 1;
      }
    outputCopy = vtkUnstructuredGrid::New();
    }
  else if (this->OutputDataType == VTK_IMAGE_DATA)
    {
    if (output && output->IsA("vtkImageData"))
      {
      return 1;
      }
    outputCopy = vtkImageData::New();
    }
  else if (this->OutputDataType == VTK_DIRECTED_GRAPH)
    {
    if (output && output->IsA("vtkDirectedGraph"))
      {
      return 1;
      }
    outputCopy = vtkDirectedGraph::New();
    }
  else if (this->OutputDataType == VTK_UNDIRECTED_GRAPH)
    {
    if (output && output->IsA("vtkUndirectedGraph"))
      {
      return 1;
      }
    outputCopy = vtkUndirectedGraph::New();
    }
  else if (this->OutputDataType == VTK_MULTIBLOCK_DATA_SET)
    {
    if (output && output->IsA("vtkMultiBlockDataSet"))
      {
      return 1;
      }
    outputCopy = vtkMultiBlockDataSet::New();
    }
  else
    {
    vtkErrorMacro("Unrecognized output type: " << this->OutputDataType
                  << ". Cannot create output.");
    return 0;
    }

  outputCopy->SetPipelineInformation(outputVector->GetInformationObject(0));
  outputCopy->Delete();
  return 1;
}

//-----------------------------------------------------------------------------
int vtkMPIMoveData::RequestInformation(vtkInformation*,
                                       vtkInformationVector** inputVector,
                                       vtkInformationVector* outputVector)
{
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  if (inputVector[0]->GetNumberOfInformationObjects() > 0)
    {
    outInfo->Set(
      vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES(),
      inputVector[0]->GetInformationObject(0)->Get(
        vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES()));
    return 1;
    }

  outInfo->Set(
    vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES(), -1);

  return 1;
}

//-----------------------------------------------------------------------------
// This filter  is going to replace the many variations of collection fitlers.
// It handles collection and duplication.
// It handles poly data and unstructured grid.
// It handles rendering on the data server and render server.


// Pass through, No render server. (Distributed rendering on data server).
// Data server copy input to output.

// Passthrough, Yes RenderServer (Distributed rendering on render server).
// Data server MtoN
// Move data from N data server processes to N render server processes.

// Duplicate, No render server. (Tile rendering on data server and client).
// GatherAll on data server.
// Data server process 0 sends data to client.

// Duplicate, Yes RenderServer (Tile rendering on rendering server and client).
// GatherToZero on data server.
// Data server process 0 sends to client
// Data server process 0 sends to render server 0
// Render server process 0 broad casts to all render server processes.

// Collect, render server: yes or no. (client rendering).
// GatherToZero on data server.
// Data server process 0 sends data to client.

//-----------------------------------------------------------------------------
// We should avoid marshalling more than once.
int vtkMPIMoveData::RequestData(vtkInformation*,
                                vtkInformationVector** inputVector,
                                vtkInformationVector* outputVector)
{
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  vtkDataObject* input = 0;
  vtkDataObject* output = outInfo->Get(vtkDataObject::DATA_OBJECT());

  if (inputVector[0]->GetNumberOfInformationObjects() > 0)
    {
    input = inputVector[0]->GetInformationObject(0)->Get(
        vtkDataObject::DATA_OBJECT());
    }


  if (this->OutputDataType == VTK_IMAGE_DATA)
    {
    if (this->MoveMode == vtkMPIMoveData::PASS_THROUGH && this->MPIMToNSocketConnection)
      {
      vtkErrorMacro("Image data delivery to render server not supported.");
      return 0;
      }
    }

  this->UpdatePiece = outInfo->Get(
    vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
  this->UpdateNumberOfPieces = outInfo->Get(
    vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());

  // This case deals with everything running as one MPI group
  // Client, Data and render server are all the same program.
  // This covers single process mode too, although this filter
  // is unnecessary in that mode and really should not be put in.
  if (this->MPIMToNSocketConnection == 0 &&
      this->ClientDataServerSocketController == 0)
    {
    // Clone in this mode is used for plots and picking.
    if (this->MoveMode == vtkMPIMoveData::CLONE)
      {
      this->DataServerGatherAll(input, output);
      return 1;
      }
    // Collect mode for rendering on node 0.
    if (this->MoveMode == vtkMPIMoveData::COLLECT)
      {
      this->DataServerGatherToZero(input, output);
      return 1;
      }
    // PassThrough mode for compositing.
    if (this->MoveMode == vtkMPIMoveData::PASS_THROUGH)
      {
      output->ShallowCopy(input);
      return 1;
      }
    // Collect and PassThrough.
    if (this->MoveMode == vtkMPIMoveData::COLLECT_AND_PASS_THROUGH)
      {
      // Collect
      this->DataServerGatherToZero(input, output);
      // PassThrough
      output->ShallowCopy(input);
      return 1;
      }
    vtkErrorMacro("MoveMode not set.");
    return 0;
    }

  // PassThrough with no RenderServer. (Distributed rendering on data server).
  // Data server copy input to output.
  // Render server and client will not have an input.
  if (this->MoveMode == vtkMPIMoveData::PASS_THROUGH &&
      this->MPIMToNSocketConnection == 0)
    {
    if (input)
      {
      output->ShallowCopy(input);
      }
    return 1;
    }

  // Passthrough and RenderServer (Distributed rendering on render server).
  // Data server MtoN
  // Move data from N data server processes to N render server processes.
  if (this->MoveMode == vtkMPIMoveData::PASS_THROUGH &&
      this->MPIMToNSocketConnection)
    {
    if (this->Server == vtkMPIMoveData::DATA_SERVER)
      {
      this->DataServerAllToN(input,output,
                      this->MPIMToNSocketConnection->GetNumberOfConnections());
      this->DataServerSendToRenderServer(output);
      output->Initialize();
      return 1;
      }
    if (this->Server == vtkMPIMoveData::RENDER_SERVER)
      {
      this->RenderServerReceiveFromDataServer(output);
      return 1;
      }
    // Client does nothing.
    return 1;
    }

  // Duplicate with no RenderServer.(Tile rendering on data server and client).
  // GatherAll on data server.
  // Data server process 0 sends data to client.
  if (this->MoveMode == vtkMPIMoveData::CLONE &&
      this->MPIMToNSocketConnection ==0)
    {
    if (this->Server == vtkMPIMoveData::DATA_SERVER)
      {
      this->DataServerGatherAll(input, output);
      this->DataServerSendToClient(output);
      return 1;
      }
    if (this->Server == vtkMPIMoveData::CLIENT)
      {
      this->ClientReceiveFromDataServer(output);
      return 1;
      }
    }

  // Duplicate and RenderServer(Tile rendering on rendering server and client).
  // GatherToZero on data server.
  // Data server process 0 sends to client
  // Data server process 0 sends to render server 0
  // Render server process 0 broad casts to all render server processes.
  if (this->MoveMode == vtkMPIMoveData::CLONE &&
      this->MPIMToNSocketConnection)
    {
    if (this->Server == vtkMPIMoveData::DATA_SERVER)
      {
      this->DataServerGatherToZero(input, output);
      this->DataServerSendToClient(output);
      this->DataServerZeroSendToRenderServerZero(output);
      return 1;
      }
    if (this->Server == vtkMPIMoveData::CLIENT)
      {
      this->ClientReceiveFromDataServer(output);
      return 1;
      }
    if (this->Server == vtkMPIMoveData::RENDER_SERVER)
      {
      this->RenderServerZeroReceiveFromDataServerZero(output);
      this->RenderServerZeroBroadcast(output);
      }
    }

  // Collect and data server or render server (client rendering).
  // GatherToZero on data server.
  // Data server process 0 sends data to client.
  if (this->MoveMode == vtkMPIMoveData::COLLECT)
    {
    if (this->Server == vtkMPIMoveData::DATA_SERVER)
      {
      this->DataServerGatherToZero(input, output);
      this->DataServerSendToClient(output);
      return 1;
      }
    if (this->Server == vtkMPIMoveData::CLIENT)
      {
      this->ClientReceiveFromDataServer(output);
      return 1;
      }
    // Render server does nothing
    return 1;
    }

  if (this->MoveMode == vtkMPIMoveData::COLLECT_AND_PASS_THROUGH)
    {
    if (this->MPIMToNSocketConnection == 0)
      {
      // In client-server mode without render server.
      if (this->Server == vtkMPIMoveData::DATA_SERVER)
        {
        this->DataServerGatherToZero(input, output);
        this->DataServerSendToClient(output);
        output->Initialize();
        output->ShallowCopy(input);
        return 1;
        }
      if (this->Server == vtkMPIMoveData::CLIENT)
        {
        this->ClientReceiveFromDataServer(output);
        return 1;
        }
      }
    else
      {
      // in client-dataserver-renderserver mode.
      if (this->Server == vtkMPIMoveData::DATA_SERVER)
        {
        // Pass Through
        this->DataServerAllToN(input,output,
          this->MPIMToNSocketConnection->GetNumberOfConnections());
        this->DataServerSendToRenderServer(output);
        output->Initialize();

        // Collect to client.
        this->DataServerGatherToZero(input, output);
        this->DataServerSendToClient(output);
        output->Initialize();
        return 1;
        }
      if (this->Server == vtkMPIMoveData::RENDER_SERVER)
        {
        this->RenderServerReceiveFromDataServer(output);
        return 1;
        }
      if (this->Server == vtkMPIMoveData::CLIENT)
        {
        this->ClientReceiveFromDataServer(output);
        return 1;
        }
      }
    }

  return 1;
}

//-----------------------------------------------------------------------------
// Use LANL filter to redistribute the data.
// We will marshal more than once, but that is OK.
void vtkMPIMoveData::DataServerAllToN(vtkDataObject* input,
                                      vtkDataObject* output, int n)
{
  vtkMultiProcessController* controller = this->Controller;
  int m;

  if (controller == 0)
    {
    vtkErrorMacro("Missing controller.");
    return;
    }

  m = this->Controller->GetNumberOfProcesses();
  if (n > m)
    {
    vtkWarningMacro("Too many render servers.");
    n = m;
    }
  if (input == 0 || output == 0)
    {
    vtkErrorMacro("All to N only works for poly data currently.");
    return;
    }

  if (n == m)
    {
    output->ShallowCopy(input);
    }

  // Perform the M to N operation.
#ifdef VTK_USE_MPI
   vtkAllToNRedistributeCompositePolyData* AllToN = NULL;
   vtkDataObject* inputCopy = input->NewInstance();
   inputCopy->ShallowCopy(input);
   AllToN = vtkAllToNRedistributeCompositePolyData::New();
   AllToN->SetController(controller);
   AllToN->SetNumberOfProcesses(n);
   AllToN->SetInput(inputCopy);
   inputCopy->Delete();
   AllToN->Update();
   output->ShallowCopy(AllToN->GetOutputDataObject(0));
   AllToN->Delete();
   AllToN= 0;
#endif
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::DataServerGatherAll(vtkDataObject* input,
                                         vtkDataObject* output)
{
  int numProcs= this->Controller->GetNumberOfProcesses();

  if (numProcs <= 1)
    {
    if (input)
      {
      output->ShallowCopy(input);
      }
    return;
    }

#ifdef VTK_USE_MPI
  int idx;
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast(
                                         this->Controller->GetCommunicator());

  if (com == 0)
    {
    vtkErrorMacro("MPICommunicator neededfor this operation.");
    return;
    }
  this->ClearBuffer();
  this->MarshalDataToBuffer(input);

  // Save a copy of the buffer so we can receive into the buffer.
  // We will be responsiblefor deleting the buffer.
  // This assumes one buffer. MashalData will produce only one buffer
  // One data set, one buffer.
  vtkIdType inBufferLength = this->BufferTotalLength;
  char *inBuffer = this->Buffers;
  this->Buffers = NULL;
  this->ClearBuffer();

  // Allocate arrays used by the AllGatherV call.
  this->BufferLengths = new vtkIdType[numProcs];
  this->BufferOffsets = new vtkIdType[numProcs];

  // Compute the degenerate input offsets and lengths.
  // Broadcast our size to all other processes.
  com->AllGather(&inBufferLength, this->BufferLengths, 1);

  // Compute the displacements.
  this->BufferTotalLength = 0;
  for (idx = 0; idx < numProcs; ++idx)
    {
    this->BufferOffsets[idx] = this->BufferTotalLength;
    this->BufferTotalLength += this->BufferLengths[idx];
    }
  // Gather the marshaled data sets from all procs.
  this->NumberOfBuffers = numProcs;
  this->Buffers = new char[this->BufferTotalLength];
  com->AllGatherV(inBuffer, this->Buffers, inBufferLength,
                  this->BufferLengths, this->BufferOffsets);

  this->ReconstructDataFromBuffer(output);

  //int fixme; // Do not clear buffers here
  this->ClearBuffer();
#endif
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::DataServerGatherToZero(vtkDataObject* input,
                                            vtkDataObject* output)
{
  int numProcs= this->Controller->GetNumberOfProcesses();
  if (numProcs == 1)
    {
    if (input)
      {
      output->ShallowCopy(input);
      }
    return;
    }

    vtkTimerLog::MarkStartEvent("Dataserver gathering to 0");

#ifdef VTK_USE_MPI
  int idx;
  int myId= this->Controller->GetLocalProcessId();
  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast(
                                         this->Controller->GetCommunicator());

  if (com == 0)
    {
    vtkErrorMacro("MPICommunicator neededfor this operation.");
    return;
    }
  this->ClearBuffer();
  this->MarshalDataToBuffer(input);

  // Save a copy of the buffer so we can receive into the buffer.
  // We will be responsiblefor deleting the buffer.
  // This assumes one buffer. MashalData will produce only one buffer
  // One data set, one buffer.
  vtkIdType inBufferLength = this->BufferTotalLength;
  char *inBuffer = this->Buffers;
  this->Buffers = NULL;
  this->ClearBuffer();

  if (myId == 0)
    {
    // Allocate arrays used by the AllGatherV call.
    this->BufferLengths = new vtkIdType[numProcs];
    this->BufferOffsets = new vtkIdType[numProcs];
    }

  // Compute the degenerate input offsets and lengths.
  // Broadcast our size to process 0.
  com->Gather(&inBufferLength, this->BufferLengths, 1, 0);

  // Compute the displacements.
  this->BufferTotalLength = 0;
  if (myId == 0)
    {
    for (idx = 0; idx < numProcs; ++idx)
      {
      this->BufferOffsets[idx] = this->BufferTotalLength;
      this->BufferTotalLength += this->BufferLengths[idx];
      }
    // Gather the marshaled data sets to 0.
    this->Buffers = new char[this->BufferTotalLength];
    }
  com->GatherV(inBuffer, this->Buffers, inBufferLength,
                  this->BufferLengths, this->BufferOffsets, 0);
  this->NumberOfBuffers = numProcs;

  if (myId == 0)
    {
    this->ReconstructDataFromBuffer(output);
    }

  //int fixme; // Do not clear buffers here
  this->ClearBuffer();

  delete [] inBuffer;
  inBuffer = NULL;
#endif

  vtkTimerLog::MarkEndEvent("Dataserver gathering to 0");
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::DataServerSendToRenderServer(vtkDataObject* output)
{
  vtkSocketCommunicator* com =
      this->MPIMToNSocketConnection->GetSocketCommunicator();

  if (com == 0)
    {
    // Some data server may not have sockets because there are more data
    // processes than render server processes.
    return;
    }

  //int fixme;
  // We might be able to eliminate this marshal.
  this->ClearBuffer();
  this->MarshalDataToBuffer(output);

  com->Send(&(this->NumberOfBuffers), 1, 1, 23480);
  com->Send(this->BufferLengths, this->NumberOfBuffers, 1, 23481);
  com->Send(this->Buffers, this->BufferTotalLength, 1, 23482);
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::RenderServerReceiveFromDataServer(vtkDataObject* output)
{
  vtkSocketCommunicator* com =
      this->MPIMToNSocketConnection->GetSocketCommunicator();

  if (com == 0)
    {
    vtkErrorMacro("All render server processes should have sockets.");
    return;
    }

  this->ClearBuffer();
  com->Receive(&(this->NumberOfBuffers), 1, 1, 23480);
  this->BufferLengths = new vtkIdType[this->NumberOfBuffers];
  com->Receive(this->BufferLengths, this->NumberOfBuffers, 1, 23481);
  // Compute additional buffer information.
  this->BufferOffsets = new vtkIdType[this->NumberOfBuffers];
  this->BufferTotalLength = 0;
  for (int idx = 0; idx < this->NumberOfBuffers; ++idx)
    {
    this->BufferOffsets[idx] = this->BufferTotalLength;
    this->BufferTotalLength += this->BufferLengths[idx];
    }
  this->Buffers = new char[this->BufferTotalLength];
  com->Receive(this->Buffers, this->BufferTotalLength, 1, 23482);

  //int fixme;  // Can we avoid this?
  this->ReconstructDataFromBuffer(output);
  this->ClearBuffer();
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::DataServerZeroSendToRenderServerZero(vtkDataObject* data)
{
  int myId = this->Controller->GetLocalProcessId();

  if (myId == 0)
    {
    vtkSocketCommunicator* com =
        this->MPIMToNSocketConnection->GetSocketCommunicator();

    if (com == 0)
      {
      // Proc 0 (at least) should have a communicator.
      vtkErrorMacro("Missing socket connection.");
      return;
      }

    //int fixme;
    // We might be able to eliminate this marshal.
    this->ClearBuffer();
    this->MarshalDataToBuffer(data);
    com->Send(&(this->NumberOfBuffers), 1, 1, 23480);
    com->Send(this->BufferLengths, this->NumberOfBuffers, 1, 23481);
    com->Send(this->Buffers, this->BufferTotalLength, 1, 23482);
    this->ClearBuffer();
    }
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::RenderServerZeroReceiveFromDataServerZero(vtkDataObject* data)
{
  int myId = this->Controller->GetLocalProcessId();

  if (myId == 0)
    {
    vtkSocketCommunicator* com =
        this->MPIMToNSocketConnection->GetSocketCommunicator();

    if (com == 0)
      {
      vtkErrorMacro("All render server processes should have sockets.");
      return;
      }

    this->ClearBuffer();
    com->Receive(&(this->NumberOfBuffers), 1, 1, 23480);
    this->BufferLengths = new vtkIdType[this->NumberOfBuffers];
    com->Receive(this->BufferLengths, this->NumberOfBuffers, 1, 23481);
    // Compute additional buffer information.
    this->BufferOffsets = new vtkIdType[this->NumberOfBuffers];
    this->BufferTotalLength = 0;
    for (int idx = 0; idx < this->NumberOfBuffers; ++idx)
      {
      this->BufferOffsets[idx] = this->BufferTotalLength;
      this->BufferTotalLength += this->BufferLengths[idx];
      }
    this->Buffers = new char[this->BufferTotalLength];
    com->Receive(this->Buffers, this->BufferTotalLength, 1, 23482);

    //int fixme;  // Can we avoid this?
    this->ReconstructDataFromBuffer(data);
    this->ClearBuffer();
    }
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::DataServerSendToClient(vtkDataObject* output)
{
  if (this->ClientDataServerSocketController == NULL)
    {
    return;
    }

  int myId = this->Controller->GetLocalProcessId();

  if (myId == 0)
    {
    vtkTimerLog::MarkStartEvent("Dataserver sending to client");

    vtkSmartPointer<vtkDataObject> tosend = output;
    if (this->DeliverOutlineToClient)
      {
      // reduce data using outline filter.
      if (output->IsA("vtkPolyData") || output->IsA("vtkMultiBlockDataSet"))
        {
        vtkDataObject* clone = output->NewInstance();
        clone->ShallowCopy(output);

        vtkOutlineFilter* filter = vtkOutlineFilter::New();
        filter->SetInput(clone);
        filter->Update();
        tosend = filter->GetOutputDataObject(0);
        filter->Delete();
        clone->Delete();
        }
      else
        {
        vtkErrorMacro("DeliverOutlineToClient can only be used for vtkPolyData.");
        }
      }

    this->ClearBuffer();
    this->MarshalDataToBuffer(tosend);
    this->ClientDataServerSocketController->Send(
                                     &(this->NumberOfBuffers), 1, 1, 23490);
    this->ClientDataServerSocketController->Send(this->BufferLengths,
                                     this->NumberOfBuffers, 1, 23491);
    this->ClientDataServerSocketController->Send(this->Buffers,
                                     this->BufferTotalLength, 1, 23492);
    this->ClearBuffer();
    vtkTimerLog::MarkEndEvent("Dataserver sending to client");
    }
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::ClientReceiveFromDataServer(vtkDataObject* output)
{
  vtkCommunicator* com = 0;
  com = this->ClientDataServerSocketController->GetCommunicator();
  if (com == 0)
    {
    vtkErrorMacro("Missing socket controler on cleint.");
    return;
    }

  this->ClearBuffer();
  com->Receive(&(this->NumberOfBuffers), 1, 1, 23490);
  this->BufferLengths = new vtkIdType[this->NumberOfBuffers];
  com->Receive(this->BufferLengths, this->NumberOfBuffers,
                                  1, 23491);
  // Compute additional buffer information.
  this->BufferOffsets = new vtkIdType[this->NumberOfBuffers];
  this->BufferTotalLength = 0;
  for (int idx = 0; idx < this->NumberOfBuffers; ++idx)
    {
    this->BufferOffsets[idx] = this->BufferTotalLength;
    this->BufferTotalLength += this->BufferLengths[idx];
    }
  this->Buffers = new char[this->BufferTotalLength];
  com->Receive(this->Buffers, this->BufferTotalLength,
                                  1, 23492);
  this->ReconstructDataFromBuffer(output);
  this->ClearBuffer();
}


//-----------------------------------------------------------------------------
void vtkMPIMoveData::RenderServerZeroBroadcast(vtkDataObject* data)
{
  (void)data; // shut up warning
  int numProcs= this->Controller->GetNumberOfProcesses();
  if (numProcs <= 1)
    {
    return;
    }

#ifdef VTK_USE_MPI
  int myId= this->Controller->GetLocalProcessId();

  vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast(
                                         this->Controller->GetCommunicator());

  if (com == 0)
    {
    vtkErrorMacro("MPICommunicator neededfor this operation.");
    return;
    }

  int bufferLength = 0;
  if (myId == 0)
    {
    this->ClearBuffer();
    this->MarshalDataToBuffer(data);
    bufferLength = this->BufferLengths[0];
    }

  // Broadcast the size of the buffer.
  com->Broadcast(&bufferLength, 1, 0);

  // Allocate buffers for all receiving nodes.
  if (myId != 0)
    {
    this->NumberOfBuffers = 1;
    this->BufferLengths = new vtkIdType[1];
    this->BufferLengths[0] = bufferLength;
    this->BufferOffsets = new vtkIdType[1];
    this->BufferOffsets[0] = 0;
    this->BufferTotalLength = this->BufferLengths[0];
    this->Buffers = new char[bufferLength];
    }

  // Broadcast the buffer.
  com->Broadcast(this->Buffers, bufferLength, 0);

  // Reconstruct the output on nodes other than 0.
  if (myId != 0)
    {
    this->ReconstructDataFromBuffer(data);
    }

  this->ClearBuffer();
#endif
}




//-----------------------------------------------------------------------------
void vtkMPIMoveData::ClearBuffer()
{
  this->NumberOfBuffers = 0;
  if (this->BufferLengths)
    {
    delete [] this->BufferLengths;
    this->BufferLengths = 0;
    }
  if (this->BufferOffsets)
    {
    delete [] this->BufferOffsets;
    this->BufferOffsets = 0;
    }
  if (this->Buffers)
    {
    delete [] this->Buffers;
    this->Buffers = 0;
    }
  this->BufferTotalLength = 0;
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::MarshalDataToBuffer(vtkDataObject* data)
{
  vtkDataSet* dataSet = vtkDataSet::SafeDownCast(data);
  vtkImageData* imageData = vtkImageData::SafeDownCast(data);
  vtkGraph* graph = vtkGraph::SafeDownCast(data);

  // Protect from empty data.
  if ((dataSet && dataSet->GetNumberOfPoints() == 0) ||
    (graph && graph->GetNumberOfVertices() == 0))
    {
    this->NumberOfBuffers = 0;
    }

  // Copy input to isolate reader from the pipeline.
  vtkDataWriter* writer = vtkGenericDataObjectWriter::New();
  vtkDataObject* d = data->NewInstance();
  d->ShallowCopy(data);
  writer->SetInput(d);
  d->Delete();
  if (imageData)
    {
    // We add the image extents to the header, since the writer doesn't preserve
    // the extents.
    int *extent = imageData->GetExtent();
    double* origin = imageData->GetOrigin();
    vtksys_ios::ostringstream stream;
    stream << "EXTENT " << extent[0] << " " <<
      extent[1] << " " <<
      extent[2] << " " <<
      extent[3] << " " <<
      extent[4] << " " <<
      extent[5];
    stream << " ORIGIN: " << origin[0] << " " << origin[1] << " " << origin[2];
    writer->SetHeader(stream.str().c_str());
    }
  writer->SetFileTypeToBinary();
  writer->WriteToOutputStringOn();
  writer->Write();

  char* buffer =NULL;
  vtkIdType buffer_length = 0;

  if (vtkMPIMoveData::UseZLibCompression)
    {
    vtkTimerLog::MarkStartEvent("Zlib compress");
    // Use z-lib compression.
    uLongf out_size =compressBound(writer->GetOutputStringLength());
    buffer = new char[out_size + 8]; 
    memcpy(buffer, "zlib0000", 8);

    compress2(reinterpret_cast<Bytef*>(buffer + 8), 
      &out_size,
      reinterpret_cast<const Bytef*>(writer->GetOutputString()),
      writer->GetOutputStringLength(), /* compression_level */ Z_DEFAULT_COMPRESSION);
    vtkTimerLog::MarkEndEvent("Zlib compress");
    int in_size = static_cast<int>(writer->GetOutputStringLength());
    for (int cc=0; cc < 4; cc++)
      {
      // the first 4 bytes in the header are "zlib" which helps the receiver
      // identify that zlib compression has been used.
      // the next 4 bytes are the original length since zlib doesn't provide
      // that to the receiver.
      buffer[4+cc] = (in_size & 0x0ff);
      in_size = in_size >> 8;
      }
    buffer_length = out_size + 8;
    }
  else
    {
    buffer_length = writer->GetOutputStringLength();
    buffer = writer->RegisterAndGetOutputString();
    }

  // Get string.
  this->NumberOfBuffers = 1;
  this->BufferLengths = new vtkIdType[1];
  this->BufferLengths[0] = buffer_length;
  this->BufferOffsets = new vtkIdType[1];
  this->BufferOffsets[0] = 0;
  this->Buffers = buffer;
  this->BufferTotalLength = this->BufferLengths[0];

  writer->Delete();
  writer = 0;
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::ReconstructDataFromBuffer(vtkDataObject* data)
{
  if (this->NumberOfBuffers == 0 || this->Buffers == 0)
    {
    data->Initialize();
    return;
    }

  bool is_image_data = data->IsA("vtkImageData") != 0;
  std::vector<vtkSmartPointer<vtkDataObject> > pieces;

  for (int idx = 0; idx < this->NumberOfBuffers; ++idx)
    {
    char* bufferArray = this->Buffers+this->BufferOffsets[idx];
    vtkIdType bufferLength = this->BufferLengths[idx];

    char* realBuffer = 0;
    if (bufferLength > 4 && strncmp(bufferArray, "zlib", 4) == 0)
      {
      // sender used zlib compression. Decompress it.
      vtkIdType compressed_length = bufferLength - 8; // remove the zlib header.
      vtkIdType uncompressed_length = 0;
      for (int cc=0; cc < 4; cc++)
        {
        uncompressed_length = uncompressed_length | 
          ((0xff & (bufferArray[4+cc])) <<8 *cc);
        }

      // using zlib compression.
      realBuffer = new char[uncompressed_length];
      uLongf destLen = uncompressed_length;
      vtkTimerLog::MarkStartEvent("Zlib uncompress");
      uncompress(reinterpret_cast<Bytef*>(realBuffer), &destLen,
        reinterpret_cast<const Bytef*>(bufferArray+8), compressed_length);
      vtkTimerLog::MarkEndEvent("Zlib uncompress");

      bufferArray = realBuffer;
      bufferLength = uncompressed_length;
      }

    // Setup a reader.
    vtkDataReader *reader = vtkGenericDataObjectReader::New();
    reader->ReadFromInputStringOn();

    vtkCharArray* mystring = vtkCharArray::New();
    mystring->SetArray(bufferArray, bufferLength, 1);
    reader->SetInputArray(mystring);
    reader->Modified(); // For append loop
    reader->Update();

    if (is_image_data)
      {
      // FIXME: EXTENT and ORIGIN in vtkImageData are lost by reader/writer.
      // The header hack we used isn't going to work for composite datasets. We
      // need a more intrusive fix in the reader/writer itself.
      int extent[6]= {0, 0, 0, 0, 0, 0};
      float origin[3] = {0, 0, 0};
      sscanf(reader->GetHeader(),
        "EXTENT %d %d %d %d %d %d ORIGIN %f %f %f", &extent[0], &extent[1],
        &extent[2], &extent[3], &extent[4], &extent[5],
        &origin[0], &origin[1], &origin[2]);
      vtkImageData* clone = vtkImageData::SafeDownCast(
        reader->GetOutputDataObject(0)->NewInstance());
      clone->ShallowCopy(reader->GetOutputDataObject(0));
      clone->SetOrigin(origin[0], origin[1], origin[2]);
      clone->SetExtent(extent);
      pieces.push_back(clone);
      clone->Delete();
      }
    else
      {
      pieces.push_back(reader->GetOutputDataObject(0));
      }

    mystring->Delete();
    mystring = 0;
    reader->Delete();
    reader = NULL;
    delete [] realBuffer;
    realBuffer = 0;
    }

  vtkMPIMoveDataMerge(pieces, data);
}

//-----------------------------------------------------------------------------
void vtkMPIMoveData::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "NumberOfBuffers: " << this->NumberOfBuffers << endl;
  os << indent << "Server: " << this->Server << endl;
  os << indent << "MoveMode: " << this->MoveMode << endl;
  os << indent << "DeliverOutlineToClient : "
    << this->DeliverOutlineToClient << endl;
  os << indent << "OutputDataType: ";
  if (this->OutputDataType == VTK_POLY_DATA)
    {
    os << "VTK_POLY_DATA";
    }
  else if (this->OutputDataType == VTK_UNSTRUCTURED_GRID)
    {
    os << "VTK_UNSTRUCTURED_GRID";
    }
  else if (this->OutputDataType == VTK_IMAGE_DATA)
    {
    os << "VTK_IMAGE_DATA";
    }
  else if (this->OutputDataType == VTK_DIRECTED_GRAPH)
    {
    os << "VTK_DIRECTED_GRAPH";
    }
  else if (this->OutputDataType == VTK_UNDIRECTED_GRAPH)
    {
    os << "VTK_UNDIRECTED_GRAPH";
    }
  else
    {
    os << "Unrecognized output type " << this->OutputDataType;
    }
  os << endl;
  //os << indent << "MToN
}

