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

  Program:   ParaView
  Module:    vtkPVGlyphFilter.cxx

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkPVGlyphFilter.h"

#include "vtkAppendPolyData.h"
#include "vtkCompositeDataPipeline.h"
#include "vtkCompositeDataIterator.h"
#include "vtkCompositeDataSet.h"
#include "vtkGarbageCollector.h"
#include "vtkHierarchicalBoxDataSet.h"
#include "vtkCompositeDataSet.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMaskPoints.h"
#include "vtkMath.h"
#include "vtkMultiProcessController.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkUniformGrid.h"

#include <stdlib.h>
#include <time.h>
#include <vtkstd/algorithm>

vtkStandardNewMacro(vtkPVGlyphFilter);

//-----------------------------------------------------------------------------
vtkPVGlyphFilter::vtkPVGlyphFilter() :
   RandomPtsInDataset()
{
  this->SetColorModeToColorByScalar();
  this->SetScaleModeToScaleByVector();
  this->MaskPoints = vtkMaskPoints::New();
  this->RandomMode = this->MaskPoints->GetRandomMode();
  this->MaximumNumberOfPoints = 5000;
  this->NumberOfProcesses = vtkMultiProcessController::GetGlobalController() ?
    vtkMultiProcessController::GetGlobalController()->GetNumberOfProcesses() : 1;
  this->UseMaskPoints = 1;
  this->InputIsUniformGrid = 0;

  this->BlockOnRatio = 0;
  this->BlockMaxNumPts = 0;
  this->BlockPointCounter = 0;
  this->BlockNumGlyphedPts = 0;
  this->BlockGlyphAllPoints = 0;
}

//-----------------------------------------------------------------------------
vtkPVGlyphFilter::~vtkPVGlyphFilter()
{
  if(this->MaskPoints)
    {
    this->MaskPoints->Delete();
    }
}

//-----------------------------------------------------------------------------
void vtkPVGlyphFilter::SetRandomMode(int mode)
{
  if (mode==this->MaskPoints->GetRandomMode())
    {
    // no change
    return;
    }
  // Store random mode to so that we don't have to call
  // MaskPoints->GetRandomMode() in tight loop.
  this->MaskPoints->SetRandomMode(mode);
  this->RandomMode = mode;
  this->Modified();
}

//-----------------------------------------------------------------------------
int vtkPVGlyphFilter::GetRandomMode()
{
  return this->MaskPoints->GetRandomMode();
}

//-----------------------------------------------------------------------------
void vtkPVGlyphFilter::SetUseMaskPoints(int useMaskPoints)
{
  if (useMaskPoints==this->UseMaskPoints)
    {
    // no change
    return;
    }
  this->UseMaskPoints=useMaskPoints;
  this->BlockGlyphAllPoints= !this->UseMaskPoints;
  this->Modified();
}

//----------------------------------------------------------------------------
int vtkPVGlyphFilter::FillInputPortInformation(int port,
                                               vtkInformation* info)
{
  if(!this->Superclass::FillInputPortInformation(port, info))
    {
    return 0;
    }
  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject");

  return 1;
}

//----------------------------------------------------------------------------
vtkExecutive* vtkPVGlyphFilter::CreateDefaultExecutive()
{
  return vtkCompositeDataPipeline::New();
}

//-----------------------------------------------------------------------------
vtkIdType vtkPVGlyphFilter::GatherTotalNumberOfPoints(vtkIdType localNumPts)
{
  // Although this is not perfectly process invariant, it is better
  // than we had before (divide by number of processes).
  vtkIdType totalNumPts = localNumPts;
  vtkMultiProcessController *controller = 
    vtkMultiProcessController::GetGlobalController();
  if (controller)
    {
    vtkIdType tmp;
    // This could be done much easier with MPI specific calls.
    if (controller->GetLocalProcessId() == 0)
      {
      int i;
      // Sum points on all processes.
      for (i = 1; i < controller->GetNumberOfProcesses(); ++i)
        {
        controller->Receive(&tmp, 1, i, vtkPVGlyphFilter::GlyphNPointsGather);
        totalNumPts += tmp;
        }
      // Send results back to all processes.
      for (i = 1; i < controller->GetNumberOfProcesses(); ++i)
        {
        controller->Send(&totalNumPts, 1, 
                         i, vtkPVGlyphFilter::GlyphNPointsScatter);
        }
      }
    else
      {
      controller->Send(&localNumPts, 1, 
                       0, vtkPVGlyphFilter::GlyphNPointsGather);
      controller->Receive(&totalNumPts, 1, 
                          0, vtkPVGlyphFilter::GlyphNPointsScatter);
      }
    }

  return totalNumPts;
}

//-----------------------------------------------------------------------------
int vtkPVGlyphFilter::RequestData(
    vtkInformation *request,
    vtkInformationVector **inputVector,
    vtkInformationVector *outputVector)
{
  this->BlockOnRatio = 0;

  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT());
  vtkCompositeDataSet *hdInput = 
    vtkCompositeDataSet::SafeDownCast(input);
  if (hdInput) 
    {
    return this->RequestCompositeData(request, inputVector, outputVector);
    }


  vtkDataSet* dsInput = vtkDataSet::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));

  if (!dsInput)
    {
    if (input)
      {
      vtkErrorMacro("This filter cannot process input of type: "
        << input->GetClassName());
      }
    return 0;
    }

  // Glyph everything? 
  if (!this->UseMaskPoints)
    {
    // yes.
    int retVal
      = this->Superclass::RequestData(request, inputVector, outputVector);
    this->BlockGlyphAllPoints= !this->UseMaskPoints;
    return retVal;
    }

  // Glyph a subset.
  double maxNumPts = static_cast<double> (this->MaximumNumberOfPoints);
  vtkIdType numPts = dsInput->GetNumberOfPoints();
  vtkIdType totalNumPts = this->GatherTotalNumberOfPoints(numPts);

  // What fraction of the points will this processes get allocated?
  maxNumPts
    = (double)((double)(maxNumPts)*(double)(numPts)/(double)(totalNumPts));

  maxNumPts = (maxNumPts > numPts) ? numPts : maxNumPts;

  vtkInformationVector* inputVs[2];

  vtkInformationVector* inputV = inputVector[0];
  inputVs[0] = vtkInformationVector::New();
  inputVs[0]->SetNumberOfInformationObjects(1);
  vtkInformation* newInInfo = vtkInformation::New();
  newInInfo->Copy(inputV->GetInformationObject(0));
  inputVs[0]->SetInformationObject(0, newInInfo);
  newInInfo->Delete();
  inputVs[1] = inputVector[1];



  // We will glyph this many points.
  this->BlockMaxNumPts = static_cast<vtkIdType>(maxNumPts + 0.5) ;
  if (this->BlockMaxNumPts == 0)
    {
    return 1;
    }
  this->CalculatePtsToGlyph( numPts );

  // We have set all ofthe parameters that will be used in 
  // our overloaded IsPoitVisible. Now let the glypher take over.
  newInInfo->Set(vtkDataObject::DATA_OBJECT(), dsInput);
  int retVal =
    this->Superclass::RequestData(request, inputVs, outputVector);

  inputVs[0]->Delete();
  return retVal;
}

//----------------------------------------------------------------------------
// We are overloading this so that blanking will be supported
// otehrwise we could use vtkMaskPoints filter.
int vtkPVGlyphFilter::IsPointVisible(vtkDataSet* ds, vtkIdType ptId)
{
  if (this->BlockGlyphAllPoints==1)
    {
    return 1;
    }

  // check if point has been blanked. If so skip it and
  // do not count it.
  if (this->InputIsUniformGrid)
    {
    vtkUniformGrid* ug = vtkUniformGrid::SafeDownCast( ds );
    if( ug && !ug->IsPointVisible(ptId) )
      {
      return 0;
      }
    }

  // Have we glyphed enough points yet? And are we
  // at the next point? If so we'll return 1 indicating
  // that this point should be glyphed and compute the
  // next point.
  int pointIsVisible=0;
  if ( (this->BlockNumGlyphedPts < this->BlockMaxNumPts) 
    && (this->BlockPointCounter==this->BlockNextPoint) )
    {
    this->BlockNumGlyphedPts++;
    if (this->RandomMode)
      {
      if( this->RandomPtsInDataset.empty() )
        {
        return 0;
        }

      if ( this->BlockNumGlyphedPts < this->BlockMaxNumPts )
        {
        this->BlockNextPoint = 
          this->RandomPtsInDataset[this->BlockNumGlyphedPts];
        }
      else
        {
        this->BlockNextPoint = this->BlockMaxNumPts;
        }
      }
    else
      {
      this->BlockNextPoint = this->BlockNumGlyphedPts;
      }
    pointIsVisible=1;
    }

  // Count all non-blanked points.
  ++this->BlockPointCounter;
  return pointIsVisible;
}

//----------------------------------------------------------------------------
int vtkPVGlyphFilter::RequestCompositeData(vtkInformation* request,
    vtkInformationVector** inputVector,
    vtkInformationVector* outputVector)
{
  // input
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkCompositeDataSet *hdInput
    =vtkCompositeDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));

  // output
  vtkInformation* info = outputVector->GetInformationObject(0);
  vtkPolyData *output
    = vtkPolyData::SafeDownCast(info->Get(vtkDataObject::DATA_OBJECT()));
  if (!output)
    {
    vtkErrorMacro("Expected vtkPolyData in output.");
    return 0;
    }

  // Get number of points we have in this block
  // and the number of points in all blocks.
  vtkIdType numPts = hdInput->GetNumberOfPoints();
  vtkIdType totalNumPts = this->GatherTotalNumberOfPoints(numPts);

  vtkAppendPolyData* append = vtkAppendPolyData::New();
  int numInputs = 0;

  vtkInformationVector* inputVs[2];

  vtkInformationVector* inputV = inputVector[0];
  inputVs[0] = vtkInformationVector::New();
  inputVs[0]->SetNumberOfInformationObjects(1);
  vtkInformation* newInInfo = vtkInformation::New();
  newInInfo->Copy(inputV->GetInformationObject(0));
  inputVs[0]->SetInformationObject(0, newInInfo);
  newInInfo->FastDelete();
  inputVs[1] = inputVector[1];

  int retVal = 1;
  this->InputIsUniformGrid = 0;

  vtkCompositeDataIterator* iter = hdInput->NewIterator();

  while(!iter->IsDoneWithTraversal())
    {
    vtkDataSet* ds = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject());
    if (ds)
      {
      vtkPolyData* tmpOut = vtkPolyData::New();

      // Uniform grids might be blanked, we make a note if we 
      // have a uniform grid to facilitate blanking friendly
      // glyph sampling.
      vtkUniformGrid* ug = vtkUniformGrid::SafeDownCast(ds);
      if (ug)
        {
        this->InputIsUniformGrid = 1;
        }
      else
        {
        this->InputIsUniformGrid = 0;
        }

      // Certain AMR data sets provide blanking information
      // We will skip blanked points and glyph what's
      // left if any.
      vtkIdType numBlankedPts = 0;
      vtkInformation* blockInfo = iter->GetCurrentMetaData();
      if (blockInfo)
        {
        if
          (blockInfo->Has(vtkHierarchicalBoxDataSet::NUMBER_OF_BLANKED_POINTS()))
          {
          numBlankedPts
            = blockInfo->Get(vtkHierarchicalBoxDataSet::NUMBER_OF_BLANKED_POINTS());
          }
        }

      //Calculate the number of points on this block that need to be glyphed
      double nPtsNotBlanked
        = static_cast<double>(ds->GetNumberOfPoints() - numBlankedPts);
      double nPtsVisibleOverAll
        = static_cast<double>(this->MaximumNumberOfPoints);
      double nPtsInDataSet
        = static_cast<double>(totalNumPts);
      double fractionOfPtsInBlock = nPtsNotBlanked/nPtsInDataSet;
      double nPtsVisibleOverBlock = nPtsVisibleOverAll*fractionOfPtsInBlock;
      nPtsVisibleOverBlock = (  (nPtsVisibleOverBlock > nPtsNotBlanked)
        ? nPtsNotBlanked : nPtsVisibleOverBlock );

      // We will glyph this many points.
      this->BlockMaxNumPts = static_cast<vtkIdType>(nPtsVisibleOverBlock + 0.5) ;
      if(this->BlockMaxNumPts == 0)
        {
        iter->GoToNextItem();
        continue;
        }
      this->CalculatePtsToGlyph( nPtsNotBlanked );

      // We have set all ofthe parameters that will be used in 
      // our overloaded IsPoitVisible. Now let the glypher take over.
      newInInfo->Set(vtkDataObject::DATA_OBJECT(), ds);
      retVal = this->Superclass::RequestData(request, inputVs, outputVector);

      //Accumulate the results.
      tmpOut->ShallowCopy(output);
      append->AddInput(tmpOut);

      // Call FastDelete() instead of Delete() to avoid garbage
      // collection checks. This improves the preformance significantly
      tmpOut->FastDelete();

      // Glypher failed, so we skip the rest and fail as well.
      if (!retVal)
        {
        vtkErrorMacro("vtkGlyph3D failed.");
        iter->Delete();
        inputVs[0]->Delete();
        append->Delete();
        return 0;
        }
      numInputs++;
      }
    iter->GoToNextItem();
    }

  // copy the accumulated results to the output.
  if (numInputs > 0)
    {
    append->Update();
    output->ShallowCopy(append->GetOutput());
    }

  // clean up
  iter->Delete();
  inputVs[0]->Delete();
  append->Delete();

  return retVal;
}

//-----------------------------------------------------------------------------
void vtkPVGlyphFilter::CalculatePtsToGlyph(double PtsNotBlanked)
{
  // When mask points is checked, we glyph the first N points. When mask points
  // and random mode is checked, we glyph N random points from the total number
  // of points on the block.

  if( this->BlockMaxNumPts > PtsNotBlanked )
    {
    vtkErrorMacro("This filter cannot glyph points more than: " << PtsNotBlanked);
    return;
    }

  //Reset the random points vector
  this->RandomPtsInDataset.clear();

  this->BlockPointCounter = 0;
  this->BlockNumGlyphedPts = 0;

  //Populate Random points in the vector if random mode selected
  if(this->RandomMode)
    {
    srand( time(NULL) );
    int r;
    for(int i = 0; i < this->BlockMaxNumPts; i++)
      {
      r = rand() % static_cast<int>( floor(PtsNotBlanked));
      while( vtkstd::find(this->RandomPtsInDataset.begin(), this->RandomPtsInDataset.end(), r) != this->RandomPtsInDataset.end() )
        {
        r = rand() % static_cast<int>( floor(PtsNotBlanked) );
        }
      this->RandomPtsInDataset.push_back( static_cast<vtkIdType>(r) );
      }
    vtkstd::sort( this->RandomPtsInDataset.begin(), this->RandomPtsInDataset.end() );
    }


  // Identify the first point to glyph.
  if(this->RandomMode)
    {
    this->BlockNextPoint = this->RandomPtsInDataset[0];
    }
  else
    {
    this->BlockNextPoint=0;
    } 
}

//-----------------------------------------------------------------------------
void vtkPVGlyphFilter::ReportReferences(vtkGarbageCollector* collector)
{
  this->Superclass::ReportReferences(collector);
  vtkGarbageCollectorReport(collector, this->MaskPoints, "MaskPoints");
}

//-----------------------------------------------------------------------------
void vtkPVGlyphFilter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  
  os << indent << "MaximumNumberOfPoints: " << this->GetMaximumNumberOfPoints()
     << endl;

  os << indent << "UseMaskPoints: " << (this->UseMaskPoints?"on":"off") << endl;

  os << indent << "NumberOfProcesses: " << this->NumberOfProcesses << endl;
}
