#include <RAT/ThresholdFlasherCut.hh>
#include <RAT/DB.hh>

namespace RAT {

  void ThresholdFlasherCut::BeginOfRun(DS::Run&)
  {
    fLClean = DB::Get()->GetLink("DATACLEANING",fName);

    fEventCounter = 0;
    fHitCounter.resize(9728,0);
    fIgnoreChannel.resize(9728,0);

    fAdjacentHits = fLClean->GetI("adjacent_channels");
    fAdjacentMaxed = fLClean->GetI("adjacent_channels_maxed");
    fIgnoreThreshold = fLClean->GetD("ignore_channel_threshold");

    fUnCalQHSLow = fLClean->GetI("uncalQHS_low");
    fUnCalQHSHigh = fLClean->GetI("uncalQHS_high");
    fUnCalQHLLow = fLClean->GetI("uncalQHL_low");
    fUnCalQHLHigh = fLClean->GetI("uncalQHL_high");
    fUnCalQLXLow = fLClean->GetI("uncalQLX_low");
    fUnCalQLXHigh = fLClean->GetI("uncalQLX_high");
    fCalQHSLow = fLClean->GetI("calQHS_low");
    fCalQHSHigh = fLClean->GetI("calQHS_high");
    fCalQHLLow = fLClean->GetI("calQHL_low");
    fCalQHLHigh = fLClean->GetI("calQHL_high");
    fCalQLXLow = fLClean->GetI("calQLX_low");
    fCalQLXHigh = fLClean->GetI("calQLX_high");

  }

  Processor::Result ThresholdFlasherCut::DSEvent(DS::Run&, DS::Entry& ds)
  {
    bool pass = true; // If any triggered event fails, they all fail
    for( size_t iEV = 0; iEV < ds.GetEVCount(); iEV++ )
      if( Event(ds, ds.GetEV(iEV)) != OKTRUE )
        pass = false;
    return pass ? OKTRUE : OKFALSE;
  }

  Processor::Result ThresholdFlasherCut::Event(DS::Entry&, DS::EV& ev)
  {

    fPassFlag = true;

    DS::CalPMTs cPMTs = ev.GetCalPMTs();
    DS::UncalPMTs uPMTs = ev.GetUncalPMTs();

    // Try uncalibrated channels first
    for (size_t iPMT = 0; iPMT < uPMTs.GetCount(); iPMT++){

      DS::PMTUncal uPMT = ev.GetUncalPMTs().GetPMT(iPMT);
      int id = uPMT.GetID();
      int channel = uPMT.GetChannel();
      float uqhs = uPMT.GetQHS();
      float uqhl = uPMT.GetQHL();
      float uqlx = uPMT.GetQLX();
      // Keep track of hits on each PMT
      fHitCounter[id]+=1;

      // If channel is very low or very high in charge
      if((uqlx < fUnCalQLXLow || uqhs < fUnCalQHSLow || uqhl < fUnCalQHLLow) ||
         (uqlx > fUnCalQLXHigh || uqhs > fUnCalQHSHigh || uqhl > fUnCalQHLHigh)){
        // If there are four channels around a high charge hit that
        // might have missed the cross-talk throw away the event.
        int nchannels_vthr = CountChannelsAboveThreshold(id, channel);
        // Keep track of high/low charge hits on each PMT
        fIgnoreChannel[id] += 1;
        // If at any point the number of high charge hits on a channel is greater than 10% of the
        // total number of hits on that channel in the run, than do NOT flag the event as bad, because
        // this is likely due to a bad channel. Wait for 10 hits on the channel before
        // imposing this check.
        if(nchannels_vthr >= fAdjacentMaxed &&
          (fIgnoreChannel[id]/fHitCounter[id] < fIgnoreThreshold || fHitCounter[id] < 10)){
          fPassFlag = false;
        }
      }
    }

    // Now try calibrated channels
    for (size_t iPMT = 0; iPMT < cPMTs.GetCount(); iPMT++){

      DS::PMTCal cPMT = ev.GetCalPMTs().GetPMT(iPMT);
      int id = cPMT.GetID();
      int channel = cPMT.GetChannel();
      float qhs = cPMT.GetQHS();
      float qhl = cPMT.GetQHL();
      float qlx = cPMT.GetQLX();
      // Keep track of hits on each PMT
      fHitCounter[id]+=1;

      // If channel is very low or very high in charge
      if((qlx < fCalQLXLow ||  qhs < fCalQHSLow || qhl < fCalQHLLow) ||
         (qlx > fCalQLXHigh || qhs > fCalQHSHigh || qhl > fCalQHLHigh)){
        // If there are four channels around a high charge hit that
        // might have missed the cross-talk throw away the event.
        int nchannels_vthr = CountChannelsAboveThreshold(id, channel);
        // Keep track of high/low charge hits on each PMT
        fIgnoreChannel[id] += 1;
        if(nchannels_vthr >= fAdjacentMaxed &&
          (fIgnoreChannel[id]/fHitCounter[id] < fIgnoreThreshold || fHitCounter[id] < 10)){
          fPassFlag = false;
        }
      }
    }

    fEventCounter++;

    UpdateMask(ev);

    return fPassFlag ? OKFALSE : OKTRUE;
  }

  int ThresholdFlasherCut::CountChannelsAboveThreshold(int id, int channel){
    int nchannels_vthr = 0;
    // Loop over 10 nearest channels
    const RAT::DU::ChanHWStatus& chanHWStatus = RAT::DU::Utility::Get()->GetChanHWStatus();
    for(int i = -fAdjacentHits; i < fAdjacentHits; i++){
      // If the channel is on the same FEC
      if(channel + i < 32 && channel + i > 0 && i != 0){
        // Find the absolute threshold and threshold above the noise
        int vthr = chanHWStatus.GetThreshold(id+i);
        int zvthr = chanHWStatus.GetThresholdAboveNoise(id+i);
        // If threshold above noise is greater than 25 or if the
        // threshold is greather than 200, we might not see the crosstalk
        // on the flasher, so count the "missed" crosstalk channel
        if(zvthr > 25 || vthr > 200){
          nchannels_vthr++;
        }
      }
    }
    return nchannels_vthr;
  }

} // namespace RAT