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

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

#include "vtkImageData.h"
#include "vtkMultiThreader.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkImageMultipleInputOutputFilter, "1.13");

//----------------------------------------------------------------------------
vtkImageMultipleInputOutputFilter::vtkImageMultipleInputOutputFilter()
{
}

//----------------------------------------------------------------------------
vtkImageMultipleInputOutputFilter::~vtkImageMultipleInputOutputFilter()
{      
}

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

//----------------------------------------------------------------------------
vtkImageData *vtkImageMultipleInputOutputFilter::GetOutput()
{
  return this->GetOutput(0);
}

//----------------------------------------------------------------------------
vtkImageData *vtkImageMultipleInputOutputFilter::GetOutput(int idx)
{
  if (this->NumberOfOutputs <= idx)
    {
    return NULL;
    }
  
  return (vtkImageData*)(this->Outputs[idx]);
}


//----------------------------------------------------------------------------
void vtkImageMultipleInputOutputFilter::ExecuteInformation()
{
  vtkImageData *output;
  vtkImageData *input = this->GetInput(0);
  
  if ( input == NULL)
    {
    return;
    }
  
  // Set the defaults from input1 to all outputs
  for (int i = 0; i < this->NumberOfOutputs; i++)
    {
    output = this->GetOutput(i);
    if (output)
      {
      output->CopyTypeSpecificInformation(input);
      }
    }

  // Let the subclass modify the default.
  this->ExecuteInformation((vtkImageData**)(this->Inputs), 
                           (vtkImageData**)(this->Outputs));
}

// Call the alternate version of this method, and use the returned input
// update extent for all inputs
void vtkImageMultipleInputOutputFilter::
ComputeInputUpdateExtents( vtkDataObject *output )
{
  int outExt[6], inExt[6];
  int idx;
  
  output->GetUpdateExtent( outExt );
  
  for (idx = 0; idx < this->NumberOfInputs; idx++)
    {
    if (this->Inputs[idx] != NULL)
      {
      this->ComputeInputUpdateExtent( inExt, outExt, idx );
      this->Inputs[idx]->SetUpdateExtent( inExt );
      }
    }  

  // by default set other output's UpdateExtent to the same if they are unset
  for (idx = 0; idx < this->NumberOfOutputs; idx++)
    {
    if (this->Outputs[idx] && this->Outputs[idx] != output)
      {
      int *uExtent = this->Outputs[idx]->GetUpdateExtent();
      if (uExtent[0] > uExtent[1])
        {
        this->Outputs[idx]->SetUpdateExtent(outExt);
        }
      }
    }
}

// By default, simply set the input update extent to match the given output
// extent
void vtkImageMultipleInputOutputFilter::ComputeInputUpdateExtent( 
  int inExt[6],
  int outExt[6],
  int vtkNotUsed(whichInput) )
{
  memcpy(inExt,outExt,sizeof(int)*6);
}


struct vtkImageMultiThreadStruct
{
  vtkImageMultipleInputOutputFilter *Filter;
  vtkImageData   **Inputs;
  vtkImageData   **Outputs;
};
  
// this mess is really a simple function. All it does is call
// the ThreadedExecute method after setting the correct
// extent for this thread. Its just a pain to calculate
// the correct extent.
VTK_THREAD_RETURN_TYPE vtkImageMultiInOutThreadedExecute( void *arg )
{
  vtkImageMultiThreadStruct *str;
  int ext[6], splitExt[6], total;
  int threadId, threadCount;
  
  threadId = ((vtkMultiThreader::ThreadInfo *)(arg))->ThreadID;
  threadCount = ((vtkMultiThreader::ThreadInfo *)(arg))->NumberOfThreads;
  
  str = (vtkImageMultiThreadStruct *)(((vtkMultiThreader::ThreadInfo *)(arg))->UserData);
  
  memcpy(ext,str->Filter->GetOutput()->GetUpdateExtent(),
         sizeof(int)*6);

  // execute the actual method with appropriate extent
  // first find out how many pieces extent can be split into.
  total = str->Filter->SplitExtent(splitExt, ext, threadId, threadCount);
    
  if (threadId < total)
    {
    str->Filter->ThreadedExecute(str->Inputs, str->Outputs, splitExt, threadId);
    }
  // else
  //   {
  //   otherwise don't use this thread. Sometimes the threads dont
  //   break up very well and it is just as efficient to leave a 
  //   few threads idle.
  //   }

  return VTK_THREAD_RETURN_VALUE;
}


//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkImageMultipleInputOutputFilter::ExecuteData(vtkDataObject *out)
{ 
  vtkImageData *output = vtkImageData::SafeDownCast(out);
  if (!output)
    {
    vtkWarningMacro("ExecuteData called without ImageData output");
    return;
    }

  // Too many filters have floating point exceptions to execute
  // with empty input/ no request.
  if (this->UpdateExtentIsEmpty(output))
    {
    return;
    }

  output->SetExtent(output->GetUpdateExtent());
  output->AllocateScalars();

  vtkImageMultiThreadStruct str;
  
  str.Filter = this;
  str.Inputs = (vtkImageData **)this->Inputs;
  str.Outputs = (vtkImageData **)this->Outputs;
  
  this->Threader->SetNumberOfThreads(this->NumberOfThreads);
  
  // setup threading and the invoke threadedExecute
  this->Threader->SetSingleMethod(vtkImageMultiInOutThreadedExecute, &str);
  this->Threader->SingleMethodExecute();
}

//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkImageMultipleInputOutputFilter::
ThreadedExecute(vtkImageData **vtkNotUsed(inData), 
                vtkImageData **vtkNotUsed(outData),
                int extent[6], int vtkNotUsed(threadId))
{
  extent = extent;
  vtkErrorMacro("subclase should override this method!!!");
}

//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkImageMultipleInputOutputFilter::
ThreadedExecute(vtkImageData **vtkNotUsed(inData), 
                vtkImageData *vtkNotUsed(outData),
                int extent[6], int vtkNotUsed(threadId))
{
  extent = extent;
  vtkErrorMacro("This method should not be called!");
}
