/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
/*=========================================================================
 *
 *  Portions of this file are subject to the VTK Toolkit Version 3 copyright.
 *
 *  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
 *
 *  For complete copyright, license and disclaimer of warranty information
 *  please refer to the NOTICE file at the top of the ITK source tree.
 *
 *=========================================================================*/
#ifndef itkChangeRegionLabelMapFilter_hxx
#define itkChangeRegionLabelMapFilter_hxx
#include "itkChangeRegionLabelMapFilter.h"
#include "itkProgressReporter.h"
/*
 *
 * This code was contributed in the Insight Journal paper:
 * "Label object representation and manipulation with ITK"
 * by Lehmann G.
 * https://hdl.handle.net/1926/584
 * http://www.insight-journal.org/browse/publication/176
 *
 */

namespace itk
{
template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::GenerateInputRequestedRegion()
{
  // call the superclass' implementation of this method
  Superclass::GenerateInputRequestedRegion();

  // We need all the input.
  InputImagePointer input = const_cast<InputImageType *>(this->GetInput());
  if (!input)
  {
    return;
  }
  input->SetRequestedRegion(input->GetLargestPossibleRegion());
}

template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::GenerateOutputInformation()
{
  Superclass::GenerateOutputInformation();
  this->GetOutput()->SetLargestPossibleRegion(m_Region);
}

template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::EnlargeOutputRequestedRegion(DataObject *)
{
  this->GetOutput()->SetRequestedRegion(this->GetOutput()->GetLargestPossibleRegion());
}

template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::GenerateData()
{
  if (m_Region.IsInside(this->GetInput()->GetLargestPossibleRegion()))
  {
    // only copy the image, report progress anyway
    ProgressReporter progress(this, 0, 1);
    this->AllocateOutputs();
  }
  else
  {
    // call the superclass implementation so it will take care to create the
    // threads
    Superclass::GenerateData();
  }
}

template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::ThreadedProcessLabelObject(LabelObjectType * labelObject)
{
  typename LabelObjectType::Pointer tmp = LabelObjectType::New();
  tmp->template CopyAllFrom<LabelObjectType>(labelObject);
  labelObject->Clear();

  const IndexType idxMin = m_Region.GetIndex();
  IndexType       idxMax;
  for (unsigned int i = 0; i < ImageDimension; i++)
  {
    idxMax[i] = idxMin[i] + m_Region.GetSize()[i] - 1;
  }

  typename LabelObjectType::ConstLineIterator lit(tmp);
  while (!lit.IsAtEnd())
  {
    const IndexType      idx = lit.GetLine().GetIndex();
    const IndexValueType length = lit.GetLine().GetLength();

    bool outside = false;
    for (unsigned int i = 1; i < ImageDimension; i++)
    {
      if (idx[i] < idxMin[i] || idx[i] > idxMax[i])
      {
        outside = true;
      }
    }
    // check the axis 0
    if (!outside)
    {
      const IndexValueType lastIdx0 = idx[0] + length - 1;
      if (!((idx[0] < idxMin[0] && lastIdx0 < idxMin[0]) || (idx[0] > idxMax[0] && lastIdx0 > idxMax[0])))
      {
        IndexType      newIdx = idx;
        IndexValueType newLength = length;
        if (idx[0] < idxMin[0])
        {
          newLength -= idxMin[0] - idx[0];
          newIdx[0] = idxMin[0];
        }
        if (lastIdx0 > idxMax[0])
        {
          newLength -= lastIdx0 - idxMax[0];
        }
        labelObject->AddLine(newIdx, newLength);
      }
    }
    ++lit;
  }

  // remove the object if it is empty
  if (labelObject->Empty())
  {
    std::lock_guard<std::mutex> mutexHolder(this->m_LabelObjectContainerLock);
    this->GetOutput()->RemoveLabelObject(labelObject);
  }
}

template <typename TInputImage>
void
ChangeRegionLabelMapFilter<TInputImage>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "Region: " << m_Region << std::endl;
}
} // end namespace itk

#endif
