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

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

#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"

#include <cmath>

vtkStandardNewMacro(vtkImageSpatialAlgorithm);

//------------------------------------------------------------------------------
// Construct an instance of vtkImageSpatialAlgorithm filter.
vtkImageSpatialAlgorithm::vtkImageSpatialAlgorithm()
{
  this->KernelSize[0] = this->KernelSize[1] = this->KernelSize[2] = 1;
  this->KernelMiddle[0] = this->KernelMiddle[1] = this->KernelMiddle[2] = 0;
  this->HandleBoundaries = 1;
}

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

  int idx;

  os << indent << "KernelSize: (" << this->KernelSize[0];
  for (idx = 1; idx < 3; ++idx)
  {
    os << ", " << this->KernelSize[idx];
  }
  os << ").\n";

  os << indent << "KernelMiddle: (" << this->KernelMiddle[0];
  for (idx = 1; idx < 3; ++idx)
  {
    os << ", " << this->KernelMiddle[idx];
  }
  os << ").\n";
}

//------------------------------------------------------------------------------
int vtkImageSpatialAlgorithm::RequestInformation(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  // Take this opportunity to override the defaults.
  int extent[6];
  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
  this->ComputeOutputWholeExtent(extent, this->HandleBoundaries);
  outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent, 6);

  return 1;
}

//------------------------------------------------------------------------------
// A helper method to compute output image extent
void vtkImageSpatialAlgorithm::ComputeOutputWholeExtent(int extent[6], int handleBoundaries)
{
  int idx;

  if (!handleBoundaries)
  {
    // Make extent a little smaller because of the kernel size.
    for (idx = 0; idx < 3; ++idx)
    {
      extent[idx * 2] += this->KernelMiddle[idx];
      extent[idx * 2 + 1] -= (this->KernelSize[idx] - 1) - this->KernelMiddle[idx];
    }
  }
}

//------------------------------------------------------------------------------
// This method computes the extent of the input region necessary to generate
// an output region.  Before this method is called "region" should have the
// extent of the output region.  After this method finishes, "region" should
// have the extent of the required input region.
int vtkImageSpatialAlgorithm::RequestUpdateExtent(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  int wholeExtent[6], extent[6], inExtent[6];

  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* outInfo = outputVector->GetInformationObject(0);

  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent);
  outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExtent);

  this->InternalRequestUpdateExtent(extent, inExtent, wholeExtent);

  inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent, 6);

  return 1;
}

//------------------------------------------------------------------------------
void vtkImageSpatialAlgorithm::InternalRequestUpdateExtent(
  int* extent, int* inExtent, int* wholeExtent)
{
  int idx;
  for (idx = 0; idx < 3; ++idx)
  {
    // Magnify by strides
    extent[idx * 2] = inExtent[idx * 2];
    extent[idx * 2 + 1] = inExtent[idx * 2 + 1];

    // Expand to get inRegion Extent
    extent[idx * 2] -= this->KernelMiddle[idx];
    extent[idx * 2 + 1] += (this->KernelSize[idx] - 1) - this->KernelMiddle[idx];

    // If the expanded region is out of the IMAGE Extent (grow min)
    if (extent[idx * 2] < wholeExtent[idx * 2])
    {
      if (this->HandleBoundaries)
      {
        // shrink the required region extent
        extent[idx * 2] = wholeExtent[idx * 2];
      }
      else
      {
        vtkWarningMacro(<< "Required region is out of the image extent.");
      }
    }
    // If the expanded region is out of the IMAGE Extent (shrink max)
    if (extent[idx * 2 + 1] > wholeExtent[idx * 2 + 1])
    {
      if (this->HandleBoundaries)
      {
        // shrink the required region extent
        extent[idx * 2 + 1] = wholeExtent[idx * 2 + 1];
      }
      else
      {
        vtkWarningMacro(<< "Required region is out of the image extent.");
      }
    }
  }
}
