// ************************************************************************* //
//  File: avtBinnerFilter.C
// ************************************************************************* //

#include <avtBinnerFilter.h>
#include <avtDatasetExaminer.h>
#include <avtDataAttributes.h>

#include <avtExtents.h>

#include <vtkStructuredGrid.h>
#include <vtkDataArray.h>
#include <vtkFloatArray.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkMath.h>
#include <InvalidDimensionsException.h>

#ifdef PARALLEL_IO
#include <mpi.h>
#include <avtParallel.h>
#endif


// ****************************************************************************
//  Method: avtBinnerFilter constructor
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
// ****************************************************************************

avtBinnerFilter::avtBinnerFilter()
{
	nprocs = 1;
	#ifdef PARALLEL_IO
	nprocs = PAR_Size();
	#endif
	sgrid = NULL;	
	dims[0] = 10;
	dims[1] = 10;
	dims[2] = 10;

}


// ****************************************************************************
//  Method: avtBinnerFilter destructor
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
//  Modifications:
//
// ****************************************************************************

avtBinnerFilter::~avtBinnerFilter()
{
}


// ****************************************************************************
//  Method:  avtBinnerFilter::Create
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
// ****************************************************************************

avtFilter *
avtBinnerFilter::Create()
{
    return new avtBinnerFilter();
}


// ****************************************************************************
//  Method:      avtBinnerFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
// ****************************************************************************

void
avtBinnerFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const Binner*)a;
}



// ****************************************************************************
//  Method: avtBinnerFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtBinnerFilter with the given
//      parameters would result in an equivalent avtBinnerFilter.
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
// ****************************************************************************

bool
avtBinnerFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(Binner*)a);
}


// ****************************************************************************
//  Method: avtBinnerFilter::PreExecute
//
//  Purpose:
//      Called before "Execute".  This will set up the range's we are operating
//      on and the data structures we use to count the density.
//
//  Programmer:
//  Creation: 
//
// ****************************************************************************

void
avtBinnerFilter::PreExecute(void)

{
	cout << "In PreExecute\n";
	avtStreamer::PreExecute();
	ndims = GetInput()->GetInfo().GetAttributes().GetSpatialDimension();
	if (ndims < 2) {
		 EXCEPTION2(InvalidDimensionsException, "Binner", " <2D");
	}
  dims[0]  = atts.GetDim1()+1;
  dims[1]  = atts.GetDim2()+1;
	if (ndims == 2)
	dims[2] == 1;
	else
  dims[2]  = atts.GetDim3()+1;
	grid_size = dims[0]*dims[1]*dims[2];
	data = (float *) calloc (grid_size, sizeof(float));
  avtDataset_p input = GetTypedInput();
  avtDatasetExaminer::GetSpatialExtents(input, extents);
#ifdef PARALLEL_IO
        UnifyMinMax(extents, 2*ndims, 0);
#endif
	for (int i=0; i < ndims; i++){	
  	cout << "spatial extents:  " << extents[2*i+1] << " " << extents[2*i] << "\n";
	}
}

// ****************************************************************************
//  Method: avtBinnerFilter::ExecuteData
//
//  Purpose:
//      Sends the specified input and output through the Binner filter.
//
//  Arguments:
//      in_ds      The input dataset.
//      <unused>   The domain number.
//      <unused>   The label.
//
//  Returns:       The output dataset.
//
//  Programmer: cristina -- generated by xml2avt
//  Creation:   Thu Mar 16 15:04:17 PST 2006
//
// ****************************************************************************

vtkDataSet *
avtBinnerFilter::ExecuteData(vtkDataSet *in_ds, int domain, std::string)
{

cout << "domain: " << domain << "\n";
  float x[3];

  int numvar = GetInput()->GetInfo().GetAttributes().GetNumberOfVariables();
  cout << "numvar: " << numvar << "\n";
	for (int i=0; i < numvar; i++){
		cout << GetInput()->GetInfo().GetAttributes().GetVariableName(i) << "\n";
	}
  for (int i=0; i < ndims; i++){
    step[i] = (extents[2*i+1] - extents[2*i])/(dims[i]-1);
  }

  for (int i=0; i < ndims; i++){
    extents[2*i] = extents[2*i] - 2*step[i];
    extents[2*i+1] = extents[2*i+1] + 2*step[i];
  }

  for (int i=0; i < ndims; i++){
    step[i] = (extents[2*i+1] - extents[2*i])/(dims[i]-1);
  }

	vtkPointData *inPD  = in_ds->GetPointData();
	vtkDataArray *in_data = inPD->GetScalars();
	vtkPointData *outPD = sgrid->GetPointData();

	vtkIdType npoints = in_ds->GetNumberOfPoints();
	cout << "I have data: npoints: " << in_data->GetName() <<  " " << npoints << "\n";
	int bin[3];
	float delta[3];
	for (vtkIdType i = 0; i < npoints; i++) {
		in_ds->GetPoint(i, x); 

		/* find the corresponding bin */
		for (int j=0; j < ndims; j++){
			delta[j] = (x[j] - extents[2*j])/step[j];
			bin[j] = (int) delta[j];
		}
		if (ndims == 2){
			bin[2] = 0;
		}

    /* 5 contribution */
    data[bin[0]+ dims[0] * ( dims[1] * bin[2] + bin[1])] += 1;
	}	
	float check_nptlc = 0.0;
	for (vtkIdType i=0; i < grid_size; i++){
		check_nptlc += data[i];
	}
	cout << "nptlc " << check_nptlc << "\n";
	cout << "npoints " << npoints << "\n";
	return NULL;
}

// ****************************************************************************
//  Method: avtBinnerFilter::RefashionDataObjectInfo
//
//  Purpose:
//      Tells the output that
//
// ****************************************************************************

void
avtBinnerFilter::RefashionDataObjectInfo(void)
{
	cout << "Refashioning\n";
	avtDataAttributes &inAtts = GetInput()->GetInfo().GetAttributes();
	avtDataAttributes &outAtts     = GetOutput()->GetInfo().GetAttributes();
  if (pipelineVariable != NULL){
        outAtts.AddVariable(pipelineVariable);
        outAtts.SetVariableDimension(1, pipelineVariable);
        outAtts.SetVariableType(AVT_SCALAR_VAR, pipelineVariable);
        outAtts.SetCentering(AVT_ZONECENT, pipelineVariable);
        outAtts.SetActiveVariable(pipelineVariable);
	outAtts.GetTrueDataExtents(pipelineVariable)->Clear();
	outAtts.GetTrueDataExtents(pipelineVariable)->Set(drange);
	outAtts.GetCurrentDataExtents()->Clear();
	outAtts.GetCurrentDataExtents()->Set(drange);
	}
	outAtts.GetEffectiveSpatialExtents()->Set(extents);
	outAtts.GetCumulativeCurrentSpatialExtents()->Set(extents);
	outAtts.GetCumulativeTrueSpatialExtents()->Set(extents);
 	outAtts.GetTrueSpatialExtents()->Set(extents);
	outAtts.SetSpatialDimension(inAtts.GetSpatialDimension());
	outAtts.SetTopologicalDimension(inAtts.GetSpatialDimension());			
}

// ****************************************************************************
//  Method: avtBinnerFilter::VerifyInput
//
//  Purpose:
//      Verifies the input
// ****************************************************************************
void
avtBinnerFilter::VerifyInput(void)
{
	cout << "Checking Spatial Dimensions: " << GetInput()->GetInfo().GetAttributes().GetSpatialDimension() << "\n";
	cout << "Checking Topological Dimensions: " << GetInput()->GetInfo().GetAttributes().GetTopologicalDimension() << "\n";
		 	
}


void
avtBinnerFilter::PostExecute(void)
{
	avtPluginStreamer::PostExecute();
	/* need to put the data together */
#ifdef PARALLEL_IO
	cout << "In PostExecute : " << PAR_Rank() << "\n";
#endif
	vtkFloatArray *out_data = vtkFloatArray::New();
	out_data->Resize(grid_size);
	out_data->FillComponent(0,0.0);
#ifdef PARALLEL_IO
	SumFloatArrayAcrossAllProcessors(data, out_data->GetPointer(0), grid_size);
#else
	memcpy(out_data->GetPointer(0), data, grid_size*sizeof(float));
#endif
  // Create the structured grid.
  float x[3];
#ifdef PARALLEL_IO
	cout << "making the grid: " << PAR_Rank() << "\n";
#endif
 	sgrid = vtkStructuredGrid::New();
 	sgrid->SetDimensions(dims);

  vtkPoints *points = vtkPoints::New();
	points->Allocate(grid_size);


  for ( int k=0; k<dims[2]; k++){
		if (ndims == 2)
			x[2] = 0;
		else 
			x[2]=step[2]*k + extents[4];
		
    for (int j=0; j<dims[1]; j++){ 
			x[1]=step[1]*j + extents[2];
      for (int i=0; i<dims[0]; i++){
				x[0]=step[0]*i + extents[0];
        points->InsertNextPoint(x);
        }
      }
  }
  sgrid->SetPoints(points);
  points->Delete();

 	out_data->SetName(pipelineVariable);
	sgrid->GetPointData()->SetScalars(out_data);
	sgrid->GetScalarRange(dataRange);
	drange[0] = (double) dataRange[0];
	drange[1] = (double) dataRange[1];
#ifdef PARALLEL_IO
	UnifyMinMax(drange, 2, 0);
#endif
cout << "drange: " << drange[0] << " " << drange[1] << "\n";
	out_data->Delete();
	out_data = NULL;
	sgrid->Update();
#ifdef PARALLEL_IO
	if (PAR_Rank() == 0){
#endif
	avtDataTree_p tree = new avtDataTree();
	tree = new avtDataTree(sgrid, 0);	
	SetOutputDataTree(tree);
	sgrid->Delete();
	sgrid = NULL;
#ifdef PARALLEL_IO
	} 
#endif
	RefashionDataObjectInfo();
}
