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

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

#include "vtkObjectFactory.h"
#include "vtkByteSwap.h"
#include "vtkMultiProcessController.h"
#include "vtkRenderWindow.h"
vtkStandardNewMacro(vtkMPICompositeManager);

//-----------------------------------------------------------------------------
static void vtkMPICompositeManagerGatherZBufferValueRMI(void *local, void *pArg, 
                                                    int pLength, int)
{
  vtkMPICompositeManager* self = (vtkMPICompositeManager*)local;
  int *p;
  int x, y;

  if (pLength != sizeof(int)*3)
    {
    vtkGenericWarningMacro("Integer sizes differ.");
    }

  p = (int*)pArg;
  if (p[0] != 1)
    { // Need to swap
    vtkByteSwap::SwapVoidRange(pArg, 3, sizeof(int));
    if (p[0] != 1)
      { // Swapping did not work.
      vtkGenericWarningMacro("Swapping failed.");
      }
    }
  x = p[1];
  y = p[2];
  self->GatherZBufferValueRMI(x, y);
}

//-----------------------------------------------------------------------------
vtkMPICompositeManager::vtkMPICompositeManager()
{
  this->WriteBackImages = 0;
  this->UseRGBA = 0;
}

//-----------------------------------------------------------------------------
vtkMPICompositeManager::~vtkMPICompositeManager()
{

}

//-----------------------------------------------------------------------------
// Called only on Root node.
float vtkMPICompositeManager::GetZBufferValue(int x, int y)
{
  float z;
  int pArg[3];
  float *pz;
  pz = this->RenderWindow->GetZbufferData(x, y, x, y);
  z = *pz;
  delete [] pz;
  if (this->UseCompositing == 0 || !this->Controller)
    {
    // This could cause a problem between setting this ivar and rendering.
    // We could always composite, and always consider client z.
    return z; // no need to collect from other processes.
    }
  
  int myId = this->Controller->GetLocalProcessId();
  if (myId != 0)
    {
    vtkErrorMacro("GetZBufferValue must be called only on Root Node.");
    return 0;
    }
  
  float otherZ;
  int numProcs = this->Controller->GetNumberOfProcesses();
  int idx;
  pArg[0] = 1;
  pArg[1] = x;
  pArg[2] = y;

  for (idx = 1; idx < numProcs; ++idx)
    {
    // Request the Z from all other processes.
    this->Controller->TriggerRMI(1, (void*)pArg, sizeof(int)*3, 
      vtkMPICompositeManager::GATHER_Z_RMI_TAG);
    }
  for (idx = 1; idx < numProcs; ++idx)
    {
    // Receive the Z from all other processes and find the minimum.
    this->Controller->Receive(&otherZ, 1, idx, vtkMPICompositeManager::Z_TAG);
    if (otherZ < z)
      {
      z = otherZ;
      }
    }
  return z;
}
//----------------------------------------------------------------------------
// Get called on every process other than Root Node.
void vtkMPICompositeManager::GatherZBufferValueRMI(int x, int y)
{
  float z;

  // Get the z value.
  int *size = this->RenderWindow->GetActualSize();
  if (x < 0 || x >= size[0] || y < 0 || y >= size[1])
    {
    vtkErrorMacro("Point not contained in window.");
    z = 0;
    }
  else
    {
    float *tmp;
    tmp = this->RenderWindow->GetZbufferData(x, y, x, y);
    z = *tmp;
    delete [] tmp;
    }

  int myId = this->Controller->GetLocalProcessId();
  if (myId == 0)
    {
    vtkErrorMacro("This method should not have gotten called on node 0!!!");
    return;
    }
  else
    {
    // Send z to the root server node (ie. node 0)..
    this->Controller->Send(&z, 1, 1, vtkMPICompositeManager::Z_TAG);
    }
}

//-----------------------------------------------------------------------------
void vtkMPICompositeManager::InitializeRMIs()
{
  if (!this->Controller)
    {
    vtkErrorMacro("Missing Controller!");
    return;
    }
  if (this->Controller->GetLocalProcessId() == 0)
    {
    // Root node, does not need to listen to any RMI triggers.
    return;
    }
  this->Superclass::InitializeRMIs();
  
  this->Controller->AddRMI(::vtkMPICompositeManagerGatherZBufferValueRMI, this,
    vtkMPICompositeManager::GATHER_Z_RMI_TAG);
}

//-----------------------------------------------------------------------------
void vtkMPICompositeManager::StartRender()
{
  if (!this->ParallelRendering)
    {
    // Make adjustments for window size.
    int *sizeptr = this->RenderWindow->GetActualSize();
    int size[2];
    size[0] = sizeptr[0];  
    size[1] = sizeptr[1];
    if ((size[0] == 0) || (size[1] == 0))
      {
      // It helps to have a real window size.
      vtkDebugMacro("Resetting window size to 300x300");
      size[0] = size[1] = 300;
      this->RenderWindow->SetSize(size[0], size[1]);
      }
    this->FullImageSize[0] = size[0];
    this->FullImageSize[1] = size[1];
    
    //Round up.
    this->ReducedImageSize[0] =
      (int)((size[0]+this->ImageReductionFactor-1)/this->ImageReductionFactor);
    this->ReducedImageSize[1] =
      (int)((size[1]+this->ImageReductionFactor-1)/this->ImageReductionFactor);
    }
  this->Superclass::StartRender();
}

//-----------------------------------------------------------------------------
int vtkMPICompositeManager::ChooseBuffer()
{
  // Choose the back buffer if double buffering is on.
  return (this->RenderWindow->GetDoubleBuffer() == 0 ||
    this->LastRenderInFrontBuffer());
  // If LastRenderInFrontBuffer then front buffer is the buffer to work on.
}

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