#ifndef __ADCSCALE_CXX__
#define __ADCSCALE_CXX__

#include "ADCScale.h"
#include "DataFormat/EventImage2D.h"

namespace larcv {

  static ADCScaleProcessFactory __global_ADCScaleProcessFactory__;

  ADCScale::ADCScale(const std::string name)
    : ProcessBase(name)
  {}
    
  void ADCScale::configure(const PSet& cfg)
  {
    _image_producer = cfg.get<std::string>         ( "ImageProducer" );
    _gaus_mean_v    = cfg.get<std::vector<double> >( "ADCScaleMean"  );
    _gaus_sigma_v   = cfg.get<std::vector<double> >( "ADCScaleSigma" );
    _per_pixel      = cfg.get<bool>( "PixelWise", true );
    if(_gaus_mean_v.size() != _gaus_sigma_v.size()) {
      LARCV_CRITICAL() << "ADCScale Mean & Sigma must be of same length!" << std::endl;
      throw larbys();
    }

    /*
    // Threaded gaussian commented out (too big pool size)
    _gaus_pool_size_v.clear();
    _gaus_pool_size_v.resize(3,9600*3456);
    _gaus_pool_size_v  = cfg.get<std::vector<size_t> >( "RandomPoolSize", _gaus_pool_size_v);

    if(_gaus_mean_v.size() != _gaus_pool_size_v.size()) {
      LARCV_CRITICAL() << "ADCScale Mean/Sigma and pool size must be of same length!" << std::endl;
      throw larbys();
    }

    _randg_v.clear();
    for(size_t i=0; i<_gaus_mean_v.size(); ++i)

      _randg_v.emplace_back(_gaus_mean_v[i], _gaus_sigma_v[i], _gaus_pool_size_v[i]);
    */
  }

  void ADCScale::initialize()
  {
    _image_id = kINVALID_SIZE;
  }

  bool ADCScale::process(IOManager& mgr)
  {
    if(_image_id == kINVALID_SIZE)
      _image_id = mgr.producer_id(kProductImage2D,_image_producer);

    // Smear ADCs if random gaussian is provided
    if(!_gaus_mean_v.empty()) {

      auto event_image = (EventImage2D*)(mgr.get_data(_image_id));
      std::vector<larcv::Image2D> tpc_image_v;
      event_image->Move(tpc_image_v);
      
      for(size_t i=0; i<tpc_image_v.size(); ++i) {
	std::random_device rd;
	std::mt19937 gen(rd());
	std::normal_distribution<> d(_gaus_mean_v[i],_gaus_sigma_v[i]);
	
	//auto& randg = _randg_v[i];
	auto& tpc_image = tpc_image_v[i];
	// Throw warning: @ this code it "should be" index = plane id
	if(tpc_image.meta().plane() != i)
	  LARCV_WARNING() << "Image index != plane ID is detected... " << std::endl;

	if(_per_pixel) {
	  auto const& img_vec = tpc_image.as_vector();
	  
	  for(size_t i=0; i<img_vec.size(); ++i) {
	    
	    if(img_vec[i] < 1.) continue;
	    float factor = d(gen);
	    size_t col = i / tpc_image.meta().rows();
	    size_t row = i - col * tpc_image.meta().rows();
	    tpc_image.set_pixel(row,col,img_vec[i] * factor);
	  }
	}else{
	  float factor = d(gen);
	  LARCV_INFO() << "Applying scaling factor " << factor << " to image " << i << std::endl;
	  /*
	  float min=1e9;
	  float max=0;
	  for(auto const& v : tpc_image.as_vector()) { if(v<min) min = v; if (v>max) max = v; }
	  LARCV_INFO() << "BEFORE: min = " << min << " ... max = " << max << std::endl;
	  */
	  tpc_image *= factor;
	  /*
	  min = 1e9;
	  max = 0;
	  for(auto const& v : tpc_image.as_vector()) { if(v<min) min = v; if (v>max) max = v; }
	  LARCV_INFO() << "AFTER: min = " << min << " ... max = " << max << std::endl;
	  */
	}
      }
      event_image->Emplace(std::move(tpc_image_v));
    }
    /*
    // Thread method commented out... too much memory for pool!
    if(!_randg_v.empty()) {

      auto event_image = (EventImage2D*)(mgr.get_data(id));
      std::vector<larcv::Image2D> tpc_image_v;
      event_image->Move(tpc_image_v);

      // Make sure enough generators exist (# images)
      if(tpc_image_v.size() < _randg_v.size()) {
	LARCV_CRITICAL() << "# images > # of gaussian generator... check config!" << std::endl;
	throw larbys();	
      }
      // Loop over images
      for(size_t i=0; i<tpc_image_v.size(); ++i) {
	
	//auto& randg = _randg_v[i];
	auto& tpc_image = tpc_image_v[i];
	// Throw warning: @ this code it "should be" index = plane id
	if(tpc_image.meta().plane() != i)
	  LARCV_WARNING() << "Image index != plane ID is detected... " << std::endl;
	// Prepare random image and fill from gaussian generator
	std::vector<float> rand_img;
	_randg_v[i].get(rand_img);
	// If size is too small regenerate
	if(rand_img.size() < tpc_image.size()) {
	  LARCV_CRITICAL() << "Detected image size > random number pool size!" << std::endl;
	  throw larbys();
	}
	// Perform elt-wise multiplication. Allow random image to be larger in size
	LARCV_INFO() << "ElementWise mult on TPC Image2D " << std::endl;
	tpc_image.eltwise(rand_img,true);
	// Start thread for gaussian random number generation
	sleep(5);
	
	_randg_v[i].start_filling();
      }
      event_image->Emplace(std::move(tpc_image_v));
    }
    */
    return true;
  }

  void ADCScale::finalize()
  {}

}
#endif
