/*=auto=========================================================================

  Portions (c) Copyright 2005 Brigham and Women's Hospital (BWH) All Rights Reserved.

  See COPYRIGHT.txt
  or http://www.slicer.org/copyright/copyright.txt for details.

  Program:   3D Slicer
  Module:    $RCSfile: vtkImageNeighborhoodFilter.cxx,v $
  Date:      $Date$
  Version:   $Revision$

=========================================================================auto=*/
#include "vtkImageNeighborhoodFilter.h"

#include "vtkObjectFactory.h"
#include <iostream>

//------------------------------------------------------------------------------
vtkStandardNewMacro(vtkImageNeighborhoodFilter);

//----------------------------------------------------------------------------
// Description:
// Constructor sets default values
vtkImageNeighborhoodFilter::vtkImageNeighborhoodFilter()
{
  this->Mask = nullptr;
  this->HandleBoundaries = 1;
  this->SetNeighborTo4();
}

//----------------------------------------------------------------------------
vtkImageNeighborhoodFilter::~vtkImageNeighborhoodFilter()
{
  if (this->Mask != nullptr)
  {
    delete[] this->Mask;
  }
}

//----------------------------------------------------------------------------
// This method sets the size of the neighborhood and the default middle of the
// neighborhood.  Also sets the size of (allocates) the matching mask.
void vtkImageNeighborhoodFilter::SetKernelSize(int size0, int size1, int size2)
{
  int modified = 0;

  if (this->KernelSize[0] != size0)
  {
    modified = 1;
    this->KernelSize[0] = size0;
    this->KernelMiddle[0] = size0 / 2;
  }
  if (this->KernelSize[1] != size1)
  {
    modified = 1;
    this->KernelSize[1] = size1;
    this->KernelMiddle[1] = size1 / 2;
  }
  if (this->KernelSize[2] != size2)
  {
    modified = 1;
    this->KernelSize[2] = size2;
    this->KernelMiddle[2] = size2 / 2;
  }

  if (modified)
  {
    if (this->Mask != nullptr)
    {
      delete[] this->Mask;
    }
    this->Mask = new unsigned char[this->KernelSize[0] * this->KernelSize[1] * this->KernelSize[2]];
    this->Modified();
  }
  // cout << "kernel middle: " <<KernelMiddle[0]<<" "<<KernelMiddle[1]<<" "<<KernelMiddle[2]<<endl;
}

//----------------------------------------------------------------------------
void vtkImageNeighborhoodFilter::SetNeighborTo4()
{
  this->SetKernelSize(3, 3, 3);

  this->Neighbor = 4;

  // clear
  memset(this->Mask, 0, this->KernelSize[0] * this->KernelSize[1] * this->KernelSize[2]);

  // set 4 neighbors in center slice
  int z = 0;
  for (int y = -1; y <= 1; y++)
  {
    for (int x = -1; x <= 1; x++)
    {
      if (x * y == 0)
      {
        this->Mask[(1 + z) * 9 + (1 + y) * 3 + (1 + x)] = 1;
      }
    }
  }

  // unset center (current) pixel
  this->Mask[1 * 9 + 1 * 3 + 1] = 0;
  // set center pix in slice before/after (3D 4-connectivity)
  this->Mask[0 * 9 + 1 * 3 + 1] = 1;
  this->Mask[2 * 9 + 1 * 3 + 1] = 1;

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkImageNeighborhoodFilter::SetNeighborTo8()
{
  this->SetKernelSize(3, 3, 3);

  this->Neighbor = 8;

  // set
  memset(this->Mask, 1, this->KernelSize[0] * this->KernelSize[1] * this->KernelSize[2]);

  // only unset current (center) pixel
  this->Mask[1 * 9 + 1 * 3 + 1] = 0;

  this->Modified();
}

//----------------------------------------------------------------------------
// Description:
// increments to loop through mask.
void vtkImageNeighborhoodFilter::GetMaskIncrements(vtkIdType& maskInc0, vtkIdType& maskInc1, vtkIdType& maskInc2)
{
  maskInc0 = 1;
  maskInc1 = this->KernelSize[0];
  maskInc2 = this->KernelSize[0] * this->KernelSize[1];
}

//----------------------------------------------------------------------------
// Description:
// This is like the extent of the neighborhood, but relative to the
// current voxel
void vtkImageNeighborhoodFilter::GetRelativeHoodExtent(int& hoodMin0, int& hoodMax0, int& hoodMin1, int& hoodMax1, int& hoodMin2, int& hoodMax2)
{
  // Neighborhood around current pixel (kernel has radius 1)
  hoodMin0 = -this->KernelMiddle[0];
  hoodMin1 = -this->KernelMiddle[1];
  hoodMin2 = -this->KernelMiddle[2];

  hoodMax0 = hoodMin0 + this->KernelSize[0] - 1;
  hoodMax1 = hoodMin1 + this->KernelSize[1] - 1;
  hoodMax2 = hoodMin2 + this->KernelSize[2] - 1;

  //    cout << "mins: " << hoodMin0 << " " << hoodMin1
  //         << " " << hoodMin2 << endl;
  //    cout << "max: " << hoodMax0 << " " << hoodMax1
  //         << " " << hoodMax2 << endl;
}
