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

  Program:   Visualization Toolkit
  Module:    vtkImagePointDataIterator.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 "vtkImagePointDataIterator.h"
#include "vtkImageData.h"
#include "vtkImageStencilData.h"
#include "vtkDataArray.h"
#include "vtkPointData.h"
#include "vtkAlgorithm.h"

//----------------------------------------------------------------------------
class vtkImageStencilIteratorFriendship
{
public:

  static int *GetExtentListLengths(vtkImageStencilData *stencil)
    {
    return stencil->ExtentListLengths;
    }

  static int **GetExtentLists(vtkImageStencilData *stencil)
    {
    return stencil->ExtentLists;
    }
};

//----------------------------------------------------------------------------
vtkImagePointDataIterator::vtkImagePointDataIterator()
{
  this->Id = 0;
  this->SpanEnd = 0;
  this->RowEnd = 0;
  this->SliceEnd = 0;
  this->End = 0;

  this->RowEndIncrement = 0;
  this->RowIncrement = 0;
  this->SliceEndIncrement = 0;
  this->SliceIncrement = 0;

  this->Extent[0] = 0;
  this->Extent[1] = 0;
  this->Extent[2] = 0;
  this->Extent[3] = 0;
  this->Extent[4] = 0;
  this->Extent[5] = 0;

  this->Index[0] = 0;
  this->Index[1] = 0;
  this->Index[2] = 0;
  this->StartY = 0;

  this->HasStencil = false;
  this->InStencil = false;
  this->SpanSliceEndIncrement = 0;
  this->SpanSliceIncrement = 0;
  this->SpanIndex = 0;
  this->SpanCountPointer = 0;
  this->SpanListPointer = 0;

  this->Algorithm = 0;
  this->Count = 0;
  this->Target = 0;
  this->ThreadId = 0;
}

//----------------------------------------------------------------------------
void vtkImagePointDataIterator::Initialize(
  vtkImageData *image, const int extent[6], vtkImageStencilData *stencil,
  vtkAlgorithm *algorithm, int threadId)
{
  const int *dataExtent = image->GetExtent();
  if (extent == 0)
    {
    extent = dataExtent;
    }

  // Compute the increments for marching through the data.
  this->RowIncrement = dataExtent[1] - dataExtent[0] + 1;
  this->SliceIncrement =
    this->RowIncrement*(dataExtent[3] - dataExtent[2] + 1);

  // Compute the span of the image region to be covered.
  int rowSpan = 0;
  int sliceSpan = 0;
  int volumeSpan = 0;
  this->Id = 0;

  if (extent[1] >= extent[0] &&
      extent[3] >= extent[2] &&
      extent[5] >= extent[4])
    {
    rowSpan = extent[1] - extent[0] + 1;
    sliceSpan = extent[3] - extent[2] + 1;
    volumeSpan = extent[5] - extent[4] + 1;
    this->Id = (extent[0] - dataExtent[0]) +
      (extent[2] - dataExtent[2])*this->RowIncrement +
      (extent[4] - dataExtent[4])*this->SliceIncrement;
    }
  // Compute the end increments (continous increments).
  this->RowEndIncrement = this->RowIncrement - rowSpan;
  this->SliceEndIncrement = this->RowEndIncrement +
    this->SliceIncrement - this->RowIncrement*sliceSpan;

  // Get the end pointers for row, slice, and volume.
  this->SpanEnd = this->Id + rowSpan;
  this->RowEnd = this->Id + rowSpan;
  this->SliceEnd = this->Id +
    (this->RowIncrement*sliceSpan - this->RowEndIncrement);
  this->End = this->Id +
    (this->SliceIncrement*volumeSpan - this->SliceEndIncrement);

  // Save the extent (will be adjusted if there is a stencil).
  this->Extent[0] = extent[0];
  this->Extent[1] = extent[1];
  this->Extent[2] = extent[2];
  this->Extent[3] = extent[3];
  this->Extent[4] = extent[4];
  this->Extent[5] = extent[5];

  // For keeping track of the current x,y,z index.
  this->Index[0] = this->Extent[0];
  this->Index[1] = this->Extent[2];
  this->Index[2] = this->Extent[4];

  // For resetting the Y index after each slice.
  this->StartY = this->Index[1];

  // Code for when a stencil is provided.
  if (stencil)
    {
    this->HasStencil = true;
    this->InStencil = true;

    this->SpanIndex = 0;
    int stencilExtent[6];
    stencil->GetExtent(stencilExtent);

    // The stencil has a YZ array of span lists, we need increments
    // to get to the next Z position in the YZ array.
    this->SpanSliceIncrement = 0;
    this->SpanSliceEndIncrement = 0;

    if (stencilExtent[3] >= stencilExtent[2] &&
        stencilExtent[5] >= stencilExtent[4])
      {
      this->SpanSliceIncrement = stencilExtent[3] - stencilExtent[2] + 1;
      int botOffset = extent[2] - stencilExtent[2];
      if (botOffset >= 0)
        {
        this->SpanSliceEndIncrement += botOffset;
        }
      int topOffset = stencilExtent[3] - extent[3];
      if (topOffset >= 0)
        {
        this->SpanSliceEndIncrement += topOffset;
        }
      }

    // Find the offset to the start position within the YZ array.
    vtkIdType startOffset = 0;

    int yOffset = extent[2] - stencilExtent[2];
    if (yOffset < 0)
      {
      this->Extent[2] = stencilExtent[2];
      }
    else
      {
      startOffset += yOffset;
      }

    if (stencilExtent[3] <= extent[3])
      {
      this->Extent[3] = stencilExtent[3];
      }

    int zOffset = extent[4] - stencilExtent[4];
    if (zOffset < 0)
      {
      this->Extent[4] = stencilExtent[4];
      }
    else
      {
      startOffset += zOffset*this->SpanSliceIncrement;
      }

    if (stencilExtent[5] <= extent[5])
      {
      this->Extent[5] = stencilExtent[5];
      }

    if (this->Extent[2] <= this->Extent[3] &&
        this->Extent[4] <= this->Extent[5])
      {
      this->SpanCountPointer =
        vtkImageStencilIteratorFriendship::GetExtentListLengths(stencil) +
        startOffset;

      this->SpanListPointer =
        vtkImageStencilIteratorFriendship::GetExtentLists(stencil) +
        startOffset;

      // Holds the current position within the span list for the current row
      this->SetSpanState(this->Extent[0]);
      }
    else
      {
      this->SpanCountPointer = 0;
      this->SpanListPointer = 0;
      this->InStencil = false;
      }
    }
  else
    {
    this->HasStencil = false;
    this->InStencil = true;
    this->SpanSliceEndIncrement = 0;
    this->SpanSliceIncrement = 0;
    this->SpanIndex = 0;
    this->SpanCountPointer = 0;
    this->SpanListPointer = 0;
    }

  if (algorithm)
    {
    this->Algorithm = algorithm;
    vtkIdType maxCount = extent[3] - extent[2] + 1;
    maxCount *= extent[5] - extent[4] + 1;
    this->Target = maxCount/50 + 1;
    this->Count = this->Target*50 - (maxCount/this->Target)*this->Target + 1;
    this->ThreadId = threadId;
    }
  else
    {
    this->Algorithm = 0;
    this->Target = 0;
    this->Count = 0;
    this->ThreadId = 0;
    }
}

//----------------------------------------------------------------------------
void vtkImagePointDataIterator::SetSpanState(int idX)
{
  // Find the span that includes idX
  bool inStencil = false;
  int *spans = *this->SpanListPointer;
  int n = *this->SpanCountPointer;
  int i;
  for (i = 0; i < n; i++)
    {
    if (spans[i] > idX)
      {
      break;
      }
    inStencil = !inStencil;
    }

  // Set the primary span state variables
  this->SpanIndex = i;
  this->InStencil = inStencil;

  // Clamp the span end to MaxX+1
  int endIdX = this->Extent[1] + 1;
  if (i < n && spans[i] <= this->Extent[1])
    {
    endIdX = spans[i];
    }

  // Compute the pointers for idX and endIdX
  vtkIdType rowStart =
    this->RowEnd - (this->RowIncrement - this->RowEndIncrement);

  this->Id = rowStart + (idX - this->Extent[0]);
  this->SpanEnd = rowStart + (endIdX - this->Extent[0]);
}

//----------------------------------------------------------------------------
void vtkImagePointDataIterator::NextSpan()
{
  if (this->SpanEnd == this->RowEnd)
    {
    int spanIncr = 1;

    if (this->SpanEnd != this->SliceEnd)
      {
      // Move to the next row
      this->Id = this->RowEnd + this->RowEndIncrement;
      this->RowEnd += this->RowIncrement;
      this->SpanEnd = this->RowEnd;
      this->Index[1]++;
      }
    else if (this->SpanEnd != this->End)
      {
      // Move to the next slice
      this->Id = this->SliceEnd + this->SliceEndIncrement;
      this->SliceEnd += this->SliceIncrement;
      this->RowEnd = this->Id +
        (this->RowIncrement - this->RowEndIncrement);
      this->SpanEnd = this->RowEnd;
      this->Index[1] = this->StartY;
      this->Index[2]++;
      spanIncr += this->SpanSliceEndIncrement;
      }
    else
      {
      // reached End
      this->Id = this->End;
      return;
      }

    // Start of next row
    this->Index[0] = this->Extent[0];

    if (this->HasStencil)
      {
      if ((this->Index[1] >= this->Extent[2]) &&
          (this->Index[1] <= this->Extent[3]) &&
          (this->Index[2] >= this->Extent[4]) &&
          (this->Index[2] <= this->Extent[5]))
        {
        this->SpanCountPointer += spanIncr;
        this->SpanListPointer += spanIncr;
        this->SetSpanState(this->Extent[0]);
        }
      else
        {
        this->InStencil = false;
        }
      }

    if (this->Algorithm)
      {
      this->ReportProgress();
      }
    }
  else
    {
    // Move to the next span in the current row
    this->Id = this->SpanEnd;
    int spanCount = *this->SpanCountPointer;
    int endIdX = this->Extent[1] + 1;
    this->Index[0] = endIdX;
    if (this->SpanIndex < spanCount)
      {
      int tmpIdX = (*this->SpanListPointer)[this->SpanIndex];
      if (tmpIdX < endIdX)
        {
        this->Index[0] = tmpIdX;
        }
      }

    // Get the index to the start of the span after the next
    this->SpanIndex++;
    if (this->SpanIndex < spanCount)
      {
      int tmpIdX = (*this->SpanListPointer)[this->SpanIndex];
      if (tmpIdX < endIdX)
        {
        endIdX = tmpIdX;
        }
      }

    // Compute the end of the span
    this->SpanEnd = this->RowEnd -
      (this->RowIncrement - this->RowEndIncrement) +
      (endIdX - this->Extent[0]);

    // Flip the state
    this->InStencil = !this->InStencil;
    }
}

//----------------------------------------------------------------------------
void *vtkImagePointDataIterator::GetVoidPointer(
  vtkDataArray *array, vtkIdType i, int *pixelIncrement)
{
  int n = array->GetNumberOfComponents();
  if (pixelIncrement)
    {
    *pixelIncrement = n;
    }
  return array->GetVoidPointer(i*n);
}

//----------------------------------------------------------------------------
void *vtkImagePointDataIterator::GetVoidPointer(
  vtkImageData *image, vtkIdType i, int *pixelIncrement)
{
  return vtkImagePointDataIterator::GetVoidPointer(
    image->GetPointData()->GetScalars(), i, pixelIncrement);
}

//----------------------------------------------------------------------------
void vtkImagePointDataIterator::ReportProgress()
{
  if (this->Count % this->Target == 0)
    {
    if (this->Algorithm->GetAbortExecute())
      {
      this->Id = this->End;
      this->SpanEnd = this->End;
      this->RowEnd = this->End;
      this->SliceEnd = this->End;
      }
    else if (this->ThreadId == 0)
      {
      this->Algorithm->UpdateProgress(0.02*(this->Count/this->Target));
      }
    }
  this->Count++;
}
