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

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

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

vtkStandardNewMacro(vtkImageMask);

//------------------------------------------------------------------------------
vtkImageMask::vtkImageMask()
{
  this->NotMask = 0;
  this->MaskedOutputValue = new double[3];
  this->MaskedOutputValueLength = 3;
  this->MaskedOutputValue[0] = this->MaskedOutputValue[1] = this->MaskedOutputValue[2] = 0.0;
  this->MaskAlpha = 1.0;
  this->SetNumberOfInputPorts(2);
}

//------------------------------------------------------------------------------
vtkImageMask::~vtkImageMask()
{
  delete[] this->MaskedOutputValue;
}

//------------------------------------------------------------------------------
void vtkImageMask::SetImageInputData(vtkImageData* in)
{
  this->SetInput1Data(in);
}

//------------------------------------------------------------------------------
void vtkImageMask::SetMaskInputData(vtkImageData* in)
{
  this->SetInput2Data(in);
}

//------------------------------------------------------------------------------
void vtkImageMask::SetMaskedOutputValue(int num, double* v)
{
  int idx;

  if (num < 1)
  {
    vtkErrorMacro("Output value must have length greater than 0");
    return;
  }
  if (num != this->MaskedOutputValueLength)
  {
    this->Modified();
  }

  if (num > this->MaskedOutputValueLength)
  {
    delete[] this->MaskedOutputValue;
    this->MaskedOutputValue = new double[num];
    this->MaskedOutputValueLength = num;
  }

  this->MaskedOutputValueLength = num;
  for (idx = 0; idx < num; ++idx)
  {
    if (this->MaskedOutputValue[idx] != v[idx])
    {
      this->Modified();
    }
    this->MaskedOutputValue[idx] = v[idx];
  }
}

//------------------------------------------------------------------------------
// This templated function executes the filter for any type of data.
template <class T>
void vtkImageMaskExecute(vtkImageMask* self, int ext[6], vtkImageData* in1Data, T* in1Ptr,
  vtkImageData* in2Data, unsigned char* in2Ptr, vtkImageData* outData, T* outPtr, int id)
{
  int num0, num1, num2, numC, pixSize;
  int idx0, idx1, idx2, idxC;
  vtkIdType in1Inc0, in1Inc1, in1Inc2;
  vtkIdType in2Inc0, in2Inc1, in2Inc2;
  vtkIdType outInc0, outInc1, outInc2;
  T* maskedValue;
  double* v;
  int nv;
  int maskState;
  double maskAlpha, oneMinusMaskAlpha;
  unsigned long count = 0;
  unsigned long target;

  // create a masked output value with the correct length by cycling
  numC = outData->GetNumberOfScalarComponents();
  maskedValue = new T[numC];
  v = self->GetMaskedOutputValue();
  nv = self->GetMaskedOutputValueLength();
  for (idx0 = 0, idx1 = 0; idx0 < numC; ++idx0, ++idx1)
  {
    if (idx1 >= nv)
    {
      idx1 = 0;
    }
    maskedValue[idx0] = static_cast<T>(v[idx1]);
  }
  pixSize = numC * sizeof(T);
  maskState = self->GetNotMask();
  maskAlpha = self->GetMaskAlpha();
  oneMinusMaskAlpha = 1.0 - maskAlpha;

  // Get information to march through data
  in1Data->GetContinuousIncrements(ext, in1Inc0, in1Inc1, in1Inc2);
  in2Data->GetContinuousIncrements(ext, in2Inc0, in2Inc1, in2Inc2);
  outData->GetContinuousIncrements(ext, outInc0, outInc1, outInc2);
  num0 = ext[1] - ext[0] + 1;
  num1 = ext[3] - ext[2] + 1;
  num2 = ext[5] - ext[4] + 1;

  target = static_cast<unsigned long>(num2 * num1 / 50.0);
  target++;

  // Loop through output pixels
  for (idx2 = 0; idx2 < num2; ++idx2)
  {
    for (idx1 = 0; !self->AbortExecute && idx1 < num1; ++idx1)
    {
      if (!id)
      {
        if (!(count % target))
        {
          self->UpdateProgress(count / (50.0 * target));
        }
        count++;
      }
      for (idx0 = 0; idx0 < num0; ++idx0)
      {
        if (maskAlpha == 1.0)
        {
          // Pixel operation
          if (*in2Ptr && maskState == 1)
          {
            memcpy(outPtr, maskedValue, pixSize);
          }
          else if (!*in2Ptr && maskState == 0)
          {
            memcpy(outPtr, maskedValue, pixSize);
          }
          else
          {
            memcpy(outPtr, in1Ptr, pixSize);
          }
          in1Ptr += numC;
          outPtr += numC;
        }
        else
        {
          // We need to do an over operation
          int doMask = 0;
          if (*in2Ptr && maskState == 1)
          {
            doMask = 1;
          }
          else if (!*in2Ptr && maskState == 0)
          {
            doMask = 1;
          }
          if (doMask)
          {
            // Do an over operation
            for (idxC = 0; idxC < numC; ++idxC)
            {
              *outPtr = static_cast<T>(oneMinusMaskAlpha * *in1Ptr + maskedValue[idxC] * maskAlpha);
              ++outPtr;
              ++in1Ptr;
            }
          }
          else
          {
            // Copy verbatim
            for (idxC = 0; idxC < numC; ++idxC)
            {
              *outPtr = *in1Ptr;
              ++outPtr;
              ++in1Ptr;
            }
          }
        }
        in2Ptr += 1;
      }
      in1Ptr += in1Inc1;
      in2Ptr += in2Inc1;
      outPtr += outInc1;
    }
    in1Ptr += in1Inc2;
    in2Ptr += in2Inc2;
    outPtr += outInc2;
  }

  delete[] maskedValue;
}

//------------------------------------------------------------------------------
// This method is passed a input and output Datas, and executes the filter
// algorithm to fill the output from the inputs.
// It just executes a switch statement to call the correct function for
// the Datas data types.
void vtkImageMask::ThreadedRequestData(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector),
  vtkImageData*** inData, vtkImageData** outData, int outExt[6], int id)
{
  void* inPtr1;
  void* inPtr2;
  void* outPtr;
  int* tExt;

  inPtr1 = inData[0][0]->GetScalarPointerForExtent(outExt);
  inPtr2 = inData[1][0]->GetScalarPointerForExtent(outExt);
  outPtr = outData[0]->GetScalarPointerForExtent(outExt);

  tExt = inData[1][0]->GetExtent();
  if (tExt[0] > outExt[0] || tExt[1] < outExt[1] || tExt[2] > outExt[2] || tExt[3] < outExt[3] ||
    tExt[4] > outExt[4] || tExt[5] < outExt[5])
  {
    vtkErrorMacro("Mask extent not large enough");
    return;
  }

  if (inData[1][0]->GetNumberOfScalarComponents() != 1)
  {
    vtkErrorMacro("Masks can have one component");
  }

  if (inData[0][0]->GetScalarType() != outData[0]->GetScalarType() ||
    inData[1][0]->GetScalarType() != VTK_UNSIGNED_CHAR)
  {
    vtkErrorMacro(<< "Execute: image ScalarType (" << inData[0][0]->GetScalarType()
                  << ") must match out ScalarType (" << outData[0]->GetScalarType()
                  << "), and mask scalar type (" << inData[1][0]->GetScalarType()
                  << ") must be unsigned char.");
    return;
  }

  switch (inData[0][0]->GetScalarType())
  {
    vtkTemplateMacro(
      vtkImageMaskExecute(this, outExt, inData[0][0], static_cast<VTK_TT*>(inPtr1), inData[1][0],
        static_cast<unsigned char*>(inPtr2), outData[0], static_cast<VTK_TT*>(outPtr), id));
    default:
      vtkErrorMacro(<< "Execute: Unknown ScalarType");
      return;
  }
}

//------------------------------------------------------------------------------
// The output extent is the intersection.
int vtkImageMask::RequestInformation(vtkInformation* vtkNotUsed(request),
  vtkInformationVector** inputVector, vtkInformationVector* outputVector)
{
  // get the info objects
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* inInfo2 = inputVector[1]->GetInformationObject(0);

  int ext[6], ext2[6], idx;

  inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext);
  inInfo2->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext2);
  for (idx = 0; idx < 3; ++idx)
  {
    if (ext2[idx * 2] > ext[idx * 2])
    {
      ext[idx * 2] = ext2[idx * 2];
    }
    if (ext2[idx * 2 + 1] < ext[idx * 2 + 1])
    {
      ext[idx * 2 + 1] = ext2[idx * 2 + 1];
    }
  }

  outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), ext, 6);

  return 1;
}

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

  int idx;

  os << indent << "MaskedOutputValue: " << this->MaskedOutputValue[0];
  for (idx = 1; idx < this->MaskedOutputValueLength; ++idx)
  {
    os << ", " << this->MaskedOutputValue[idx];
  }
  os << endl;

  os << indent << "NotMask: " << (this->NotMask ? "On\n" : "Off\n");
  os << indent << "MaskAlpha: " << this->MaskAlpha << "\n";
}
