//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt 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.
//============================================================================
#ifndef vtk_m_filter_FBSampling_h
#define vtk_m_filter_FBSampling_h

#include <vtkm/filter/FilterDataSet.h>

namespace vtkm
{
namespace filter
{
/// \brief Generate a mask/stencil using the feature-based sampling algorithm
///
/// It takes the input dataset and generates a mask/stencil of the result of sampling 
/// the dataset using a gradient and scalar value based sampling algorithm
/// [paper]: Biswas et al. "Probabilistic data-driven sampling via multi-criteria 
///      importance analysis". IEEE TVCG, pp. 1–1,2020. 
///
class FBSampling : public vtkm::filter::FilterDataSet<FBSampling>
{
public:
  VTKM_CONT FBSampling();

  VTKM_CONT void SetFieldId(vtkm::Id fId);

  VTKM_CONT void SetBinCount(vtkm::Id numBins);

  VTKM_CONT void SetSampleRate(vtkm::Float32 sample_rate);

  VTKM_CONT void getMarginalHistogram(vtkm::cont::DataSet& ndhist, bool var1_flag, bool var2_flag, vtkm::Id binCount, 
                            std::vector<vtkm::cont::ArrayHandle<vtkm::Id>>& marginalBinIds,
                            vtkm::cont::ArrayHandle<vtkm::Id>& marginalFreqs);
 
  VTKM_CONT void getConditionalGradientHistogram(vtkm::cont::DataSet& ndhist, vtkm::Id binCount, vtkm::Id dataBinId, std::vector<vtkm::Id>& gradCondFrequency);
 

  template <typename Policy>
  VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inData,
                                          vtkm::filter::PolicyBase<Policy> policy);


  template <typename T, typename StorageType, typename DerivedPolicy>
  VTKM_CONT bool DoMapField(vtkm::cont::DataSet& result,
                            const vtkm::cont::ArrayHandle<T, StorageType>& input,
                            const vtkm::filter::FieldMetadata& fieldMeta,
                            vtkm::filter::PolicyBase<DerivedPolicy> policy);

  struct VariableCondition
  {
    VariableCondition(vtkm::cont::ArrayHandle<bool> mv):marginalVariable(mv){}
    VTKM_EXEC
    bool operator()(vtkm::Id var, vtkm::Id binId) const
    {
      //find the id of the non-marginal variable
      vtkm::Id vid = 1;
      if(!marginalVariable.GetPortalConstControl().Get(0)) vid = 0;
      if (var == vid)
      {
        //if (1 < binId && binId < 4)
          return true;
      }

      return false;
    }

    vtkm::cont::ArrayHandle<bool> marginalVariable;
  };

  struct randArraygen
  {
    VTKM_EXEC_CONT  vtkm::Float32 operator() ( vtkm::UInt32 seed)  const  // RAND_MAX assumed to be 32767 
    { 
        seed = seed * 1103515245 + 12345; 
        return ((unsigned int)(seed/65536) % 32768)/32767.0; 
    } 
  };

  struct equalsK1_2d
  {
    equalsK1_2d(vtkm::cont:: ArrayHandle <vtkm::Float32 > sample_probs):sample_probabilites(sample_probs){}
    equalsK1_2d(){};
    VTKM_EXEC_CONT  bool operator() ( vtkm:: Pair<vtkm::Id,vtkm::Float32> x) const 
    {
      vtkm::cont::ArrayHandle<vtkm::Float32>::PortalConstControl probPortal = sample_probabilites.GetPortalConstControl();
      //cout<<x.first<<" "<<x.second<<" "<<probPortal.Get(x.first)<<endl;
      return x.second<probPortal.Get(x.first); 
    }

    vtkm::cont:: ArrayHandle <vtkm::Float32 > sample_probabilites;
  };

  struct op_marginal_grad_bins
  {
    op_marginal_grad_bins(vtkm::Id val):op_val(val){}
    op_marginal_grad_bins(){};
    VTKM_EXEC_CONT  vtkm::Id operator() ( vtkm::Id bcount) const 
    {
      
      return (bcount + op_val); 
    }

    vtkm::Id op_val;
  };

private:
  vtkm::Id fieldId;
  vtkm::Id binCount;
  vtkm::Float32 sample_percent;

};
}
} // namespace vtkm::filter

#include <vtkm/filter/FBSampling.hxx>

#endif //vtk_m_filter_FBSampling_h
