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

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

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCompositeDataSet.h"
#include "vtkCompositeDataPipeline.h"
#include "vtkDataSet.h"
#include "vtkImplicitFunction.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkOverlappingAMR.h"
#include "vtkPlane.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkAppendPolyData.h"

#include <cmath>
#include "vtkObjectFactory.h"
#include "vtkCompositeDataIterator.h"
#include "vtkSmartPointer.h"
#include <cassert>

vtkStandardNewMacro(vtkCompositeCutter);

#ifdef DEBUGME
#define PRINT(x) {cout<<x<<endl;}
#else
#define PRINT(x)
#endif
namespace
{
  inline double Sign(double a)
  {
    return a==0.0? 0.0 : (a<0.0? -1.0 : 1.0);
  }
  inline bool IntersectBox(vtkImplicitFunction* func, double bounds[6], double value)
  {
    double fVal[8];
    fVal[0] = func->EvaluateFunction(bounds[0],bounds[2],bounds[4]);
    fVal[1] = func->EvaluateFunction(bounds[0],bounds[2],bounds[5]);
    fVal[2] = func->EvaluateFunction(bounds[0],bounds[3],bounds[4]);
    fVal[3] = func->EvaluateFunction(bounds[0],bounds[3],bounds[5]);
    fVal[4] = func->EvaluateFunction(bounds[1],bounds[2],bounds[4]);
    fVal[5] = func->EvaluateFunction(bounds[1],bounds[2],bounds[5]);
    fVal[6] = func->EvaluateFunction(bounds[1],bounds[3],bounds[4]);
    fVal[7] = func->EvaluateFunction(bounds[1],bounds[3],bounds[5]);

    double sign0  = Sign(fVal[0]-value);
    for (int i=1; i<8;i++)
    {
      if (Sign(fVal[i]-value) != sign0)
      {
        //this corner is on different side than first, piece
        //intersects and cannot be rejected
        return true;
      }
    }
    return false;
  }
};

vtkCompositeCutter::vtkCompositeCutter(vtkImplicitFunction *cf):vtkCutter(cf)
{

}

//----------------------------------------------------------------------------
vtkCompositeCutter::~vtkCompositeCutter()
{

}

int vtkCompositeCutter::FillInputPortInformation(int, vtkInformation *info)
{
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject");
  return 1;
}

int vtkCompositeCutter::RequestUpdateExtent(vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *)
{
  vtkDebugMacro("Request-Update");

  vtkInformation * inInfo = inputVector[0]->GetInformationObject(0);

  for (int c=0; c < this->ContourValues->GetNumberOfContours(); c++)
  {
    vtkDebugMacro("Contours "<<this->ContourValues->GetValue(c));
  }


  // Check if metadata are passed downstream
  if(inInfo->Has(vtkCompositeDataPipeline::COMPOSITE_DATA_META_DATA() ) )
  {
    std::vector<int> intersected;

    vtkCompositeDataSet * meta= vtkCompositeDataSet::SafeDownCast(inInfo->Get(vtkCompositeDataPipeline::COMPOSITE_DATA_META_DATA()));
    vtkSmartPointer<vtkCompositeDataIterator> iter;
    iter.TakeReference(meta->NewIterator());
    iter->SetSkipEmptyNodes(false);
    for(iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
    {
      double* bb = iter->GetCurrentMetaData()->Get(vtkDataObject::BOUNDING_BOX());
      for (int c=0; c < this->ContourValues->GetNumberOfContours(); c++)
      {
        if(IntersectBox(this->GetCutFunction(),bb,this->ContourValues->GetValue(c)))
        {
          intersected.push_back(iter->GetCurrentFlatIndex());
          break;
        }
      }
    }
    PRINT("Cutter demand "<<intersected.size()<<" blocks");
    inInfo->Set(vtkCompositeDataPipeline::UPDATE_COMPOSITE_INDICES(), &intersected[0], static_cast<int>(intersected.size()));
  }
  return 1;
}

int vtkCompositeCutter::RequestData(vtkInformation *request,
                                    vtkInformationVector ** inputVector,
                                    vtkInformationVector * outputVector)
{
  vtkInformation * inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation * outInfo = outputVector->GetInformationObject(0);
  vtkSmartPointer<vtkCompositeDataSet> inData = vtkCompositeDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
  if(!inData)
  {
    return Superclass::RequestData(request,inputVector,outputVector);
  }

  vtkSmartPointer<vtkCompositeDataIterator> itr;
  itr.TakeReference(inData->NewIterator());
  itr->SetSkipEmptyNodes(true);

  vtkNew<vtkAppendPolyData> append;
  int numObjects(0);
  itr->GoToFirstItem();
  while(!itr->IsDoneWithTraversal())
  {
    vtkDataSet* data = vtkDataSet::SafeDownCast(itr->GetCurrentDataObject());
    assert(data);
    inInfo->Set(vtkDataObject::DATA_OBJECT(),data);
    vtkNew<vtkPolyData> out;
    outInfo->Set(vtkDataObject::DATA_OBJECT(),out.GetPointer());
    this->Superclass::RequestData(request,inputVector,outputVector);
    append->AddInputData(out.GetPointer());
    numObjects++;
    itr->GoToNextItem();
  }
  append->Update();

  vtkPolyData* appoutput = append->GetOutput();
  inInfo->Set(vtkDataObject::DATA_OBJECT(), inData); //set it back to the original input
  outInfo->Set(vtkDataObject::DATA_OBJECT(), appoutput);
  return 1;
}

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