// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkImageConnector.h"

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

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkImageConnector);

//------------------------------------------------------------------------------
vtkImageConnector::vtkImageConnector()
{
  this->Seeds = nullptr;
  this->LastSeed = nullptr;
  this->ConnectedValue = 255;
  this->UnconnectedValue = 128;
}

//------------------------------------------------------------------------------
vtkImageConnector::~vtkImageConnector()
{
  this->RemoveAllSeeds();
}

//------------------------------------------------------------------------------
void vtkImageConnector::RemoveAllSeeds()
{
  vtkImageConnectorSeed* temp;

  while (this->Seeds)
  {
    temp = this->Seeds;
    this->Seeds = temp->Next;
    delete temp;
  }
  this->LastSeed = nullptr;
}

//------------------------------------------------------------------------------
vtkImageConnectorSeed* vtkImageConnector::NewSeed(int index[3], void* ptr)
{
  vtkImageConnectorSeed* seed = vtkImageConnectorSeed::New();
  int idx;

  for (idx = 0; idx < 3; ++idx)
  {
    seed->Index[idx] = index[idx];
  }
  seed->Pointer = ptr;
  seed->Next = nullptr;

  return seed;
}

//------------------------------------------------------------------------------
// Add a new seed to the end of the seed list.
void vtkImageConnector::AddSeedToEnd(vtkImageConnectorSeed* seed)
{
  // Add the seed to the end of the list
  if (this->LastSeed == nullptr)
  { // no seeds yet
    this->LastSeed = this->Seeds = seed;
  }
  else
  {
    this->LastSeed->Next = seed;
    this->LastSeed = seed;
  }
}

//------------------------------------------------------------------------------
// Add a new seed to the start of the seed list.
void vtkImageConnector::AddSeed(vtkImageConnectorSeed* seed)
{
  seed->Next = this->Seeds;
  this->Seeds = seed;
  if (!this->LastSeed)
  {
    this->LastSeed = seed;
  }
}

//------------------------------------------------------------------------------
// Removes a seed from the start of the seed list, and returns the seed.
vtkImageConnectorSeed* vtkImageConnector::PopSeed()
{
  vtkImageConnectorSeed* seed;

  seed = this->Seeds;
  this->Seeds = seed->Next;
  if (this->Seeds == nullptr)
  {
    this->LastSeed = nullptr;
  }
  return seed;
}

//------------------------------------------------------------------------------
// Input a data of 0's and "UnconnectedValue"s. Seeds of this object are
// used to find connected pixels.
// All pixels connected to seeds are set to ConnectedValue.
// The data has to be unsigned char.
void vtkImageConnector::MarkData(vtkImageData* data, int numberOfAxes, int extent[6])
{
  vtkIdType incs[3], *pIncs;
  int* pExtent;
  vtkImageConnectorSeed* seed;
  unsigned char* ptr;
  int newIndex[3], *pIndex, idx;
  long count = 0;

  data->GetIncrements(incs);
  while (this->Seeds)
  {
    ++count;
    seed = this->PopSeed();
    // just in case the seed has not been marked visited.
    *(static_cast<unsigned char*>(seed->Pointer)) = this->ConnectedValue;
    // Add neighbors
    newIndex[0] = seed->Index[0];
    newIndex[1] = seed->Index[1];
    newIndex[2] = seed->Index[2];
    pExtent = extent;
    pIncs = incs;
    pIndex = newIndex;
    for (idx = 0; idx < numberOfAxes; ++idx)
    {
      // check pixel below
      if (*pExtent < *pIndex)
      {
        ptr = static_cast<unsigned char*>(seed->Pointer) - *pIncs;
        if (*ptr == this->UnconnectedValue)
        { // add a new seed
          --(*pIndex);
          *ptr = this->ConnectedValue;
          this->AddSeedToEnd(this->NewSeed(newIndex, ptr));
          ++(*pIndex);
        }
      }
      ++pExtent;
      // check above pixel
      if (*pExtent > *pIndex)
      {
        ptr = static_cast<unsigned char*>(seed->Pointer) + *pIncs;
        if (*ptr == this->UnconnectedValue)
        { // add a new seed
          ++(*pIndex);
          *ptr = this->ConnectedValue;
          this->AddSeedToEnd(this->NewSeed(newIndex, ptr));
          --(*pIndex);
        }
      }
      ++pExtent;
      // move to next axis
      ++pIncs;
      ++pIndex;
    }

    // Delete seed
    delete seed;
  }
  vtkDebugMacro("Marked " << count << " pixels");
  (void)count;
}

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

  os << indent << "ConnectedValue: " << this->ConnectedValue << "\n";
  os << indent << "UnconnectedValue: " << this->UnconnectedValue << "\n";
}
VTK_ABI_NAMESPACE_END
