//-------------------------------------------------
//
//   Class: L1MuGMTPattern
//
//   Description:   Create GMT HW test patterns
//
//
//
//   I. Mikulec            HEPHY Vienna
//
//--------------------------------------------------

//-----------------------
// This Class's Header --
//-----------------------
#include "L1Trigger/GlobalMuonTrigger/test/L1MuGMTPattern.h"

//---------------
// C++ Headers --
//---------------

#include <fstream>
#include <iomanip>
#include <cmath>

//-------------------------------
// Collaborating Class Headers --
//-------------------------------
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"

#include "DataFormats/L1GlobalMuonTrigger/interface/L1MuGMTReadoutCollection.h"

using namespace std;

//----------------
// Constructors --
//----------------
L1MuGMTPattern::L1MuGMTPattern(const edm::ParameterSet& ps) {
  m_inputTag = ps.getUntrackedParameter<edm::InputTag>("GMTInputTag", edm::InputTag("gmt"));
  m_inputCaloTag = ps.getUntrackedParameter<edm::InputTag>("GMTInputCaloTag", edm::InputTag("source"));
  m_outfilename = ps.getUntrackedParameter<string>("OutputFile", "gmt_testfile.dat");
  m_outputType = ps.getUntrackedParameter<int>("OutputType", 1);
}

//--------------
// Destructor --
//--------------
L1MuGMTPattern::~L1MuGMTPattern() {}

void L1MuGMTPattern::beginJob() {
  ofstream of(m_outfilename.c_str(), ios::trunc);
  if (m_outputType == 1) {
    of << "-- GMT HW Test file generated by L1MuGMTPattern" << endl;
    of << "--" << endl;
  }
  of.close();
  if (m_outputType == 4) {
    of.open((m_outfilename + string("DT")).c_str(), ios::trunc);
    of.close();
    of.open((m_outfilename + string("CSC")).c_str(), ios::trunc);
    of.close();
    of.open((m_outfilename + string("BRPC")).c_str(), ios::trunc);
    of.close();
    of.open((m_outfilename + string("FRPC")).c_str(), ios::trunc);
    of.close();
  }
}

void L1MuGMTPattern::endJob() {}

//--------------
// Operations --
//--------------

void L1MuGMTPattern::analyze(const edm::Event& e, const edm::EventSetup& es) {
  // GENERAL block
  if (m_outputType == 1) {
    ofstream of(m_outfilename.c_str(), ios::app);
    int runn = e.id().run();
    int eventn = e.id().event();
    of << "--" << endl;
    of << "RUN  " << runn << endl;
    of << "EVT  " << eventn << endl;
    of.close();
  }

  // Get GMTReadoutCollection

  edm::Handle<L1MuGMTReadoutCollection> gmtrc_handle;
  e.getByLabel(m_inputTag, gmtrc_handle);
  L1MuGMTReadoutCollection const* gmtrc = gmtrc_handle.product();

  L1MuGMTReadoutRecord gmt_record = gmtrc->getRecord(0);

  // Regional Candidates
  printRegional("DT", gmt_record.getDTBXCands());
  printRegional("CSC", gmt_record.getCSCCands());
  printRegional("BRPC", gmt_record.getBrlRPCCands());
  printRegional("FRPC", gmt_record.getFwdRPCCands());

  //
  // MIP and ISO bits
  //
  edm::Handle<L1CaloRegionCollection> calocoll_h;
  e.getByLabel(m_inputCaloTag, calocoll_h);
  if (calocoll_h.isValid()) {
    printMipIso(calocoll_h.product());
  } else {
    edm::LogVerbatim("GMT_PSB_info") << " Calorimeter MIP/QUIET bits not found in the Event ";
  }

  // GMT Trigger
  printGMT("GMT", gmt_record.getGMTCands());
  printGMT("GMTB", gmt_record.getGMTBrlCands());
  printGMT("GMTF", gmt_record.getGMTFwdCands());

  edm::Handle<vector<unsigned> > mi_h;
  e.getByLabel(m_inputTag, mi_h);
  if (mi_h.isValid()) {
    printMI(mi_h.product());
  } else {
    edm::LogVerbatim("GMT_PSB_info") << " MI product invalid ";
  }

  printCANC();
}

void L1MuGMTPattern::printRegional(string tag, const vector<L1MuRegionalCand>& rmc) {
  ofstream of;
  if (m_outputType == 4) {
    of.open((m_outfilename + tag).c_str(), ios::app);
  } else {
    of.open(m_outfilename.c_str(), ios::app);
  }

  int i = 0;
  vector<L1MuRegionalCand>::const_iterator iter1;
  edm::LogVerbatim("GMTPattern") << "L1 " << tag.data() << " TRIGGER muons (bx0): " << endl;
  for (iter1 = rmc.begin(); iter1 != rmc.end(); iter1++) {
    if (i > 3)
      break;
    edm::LogVerbatim("GMTPattern") << " " << ++i << " : ";
    (*iter1).print();

    if (m_outputType == 1 && !((*iter1).empty())) {
      of << left << setw(4) << tag.data() << right << " " << setw(1) << (*iter1).charge_valid_packed() << " " << setw(1)
         << (*iter1).charge_packed() << " " << setw(1) << (*iter1).finehalo_packed() << " " << setw(2)
         << (*iter1).eta_packed() << " " << setw(1) << (*iter1).quality_packed() << " " << setw(2)
         << (*iter1).pt_packed() << " " << setw(3) << (*iter1).phi_packed();

      of << " -- " << left << setw(4) << tag.data() << right << " vch=" << (*iter1).charge_valid_packed()
         << " ch=" << (*iter1).chargeValue() << " hf=" << (*iter1).finehalo_packed() << " eta=" << (*iter1).etaValue()
         << " q=" << (*iter1).quality() << " pt=" << (*iter1).ptValue() << " phi=" << (*iter1).phiValue() << endl;
    }
    if (m_outputType == 2 || m_outputType == 4) {
      of << " " << hex << setfill('0') << setw(8) << invertQPt((*iter1).getDataWord()) << dec;
    }
  }
  if (m_outputType == 2 || m_outputType == 4) {
    for (; i < 4; i++) {
      of << " 0000ffff";
    }
  }
  if (m_outputType == 2) {
    of << " |";
  }
  if (m_outputType == 4) {
    of << endl;
  }
}

void L1MuGMTPattern::printGMT(string tag, const vector<L1MuGMTExtendedCand>& exc) {
  ofstream of(m_outfilename.c_str(), ios::app);
  int i = 0;
  vector<L1MuGMTExtendedCand>::const_iterator gmt_iter;
  const char* sysign_txt[4] = {"POS  ", "NEG  ", "UNDEF", "SYNC "};
  edm::LogVerbatim("GMTPattern") << "L1 " << tag.data() << " muons (bx0): ";
  for (gmt_iter = exc.begin(); gmt_iter != exc.end(); gmt_iter++) {
    if (i > 3)
      break;
    edm::LogVerbatim("GMTPattern") << " " << ++i << " : ";
    (*gmt_iter).print();

    if (m_outputType == 1 && !((*gmt_iter).empty())) {
      of << left << setw(4) << tag.data() << right << " " << setw(1) << (*gmt_iter).sysign() << " " << setw(1)
         << (*gmt_iter).mip() << " " << setw(1) << (*gmt_iter).isol() << " " << setw(2) << (*gmt_iter).etaIndex() << " "
         << setw(1) << (*gmt_iter).quality() << " " << setw(2) << (*gmt_iter).ptIndex() << " " << setw(3)
         << (*gmt_iter).phiIndex() << " " << setw(3) << (*gmt_iter).rank();

      of << " -- " << left << setw(4) << tag.data() << right << " sy_sign/charge=" << sysign_txt[(*gmt_iter).sysign()]
         << " MIP=" << (*gmt_iter).mip() << " ISO=" << (*gmt_iter).isol() << " eta=" << (*gmt_iter).etaValue()
         << " q=" << (*gmt_iter).quality() << " pt=" << (*gmt_iter).ptValue() << " phi=" << (*gmt_iter).phiValue()
         << " rank=" << (*gmt_iter).rank() << endl;
    }
    if (m_outputType > 1 && tag == "GMT") {
      of << " " << hex << setfill('0') << setw(8) << (*gmt_iter).getDataWord() << dec;
    }
  }
  if (m_outputType > 1 && tag == "GMT") {
    for (; i < 4; i++) {
      of << " 00000000";
    }
    of << endl;
  }
}

void L1MuGMTPattern::printMipIso(L1CaloRegionCollection const* regions) {
  int nmip = 0;
  int nnq = 0;

  if (m_outputType == 1) {
    ofstream of(m_outfilename.c_str(), ios::app);
    L1CaloRegionCollection::const_iterator iter;

    edm::LogVerbatim("GMT_PSB_info") << "MIP/QUIET bits rceived by the GMT :";

    for (iter = regions->begin(); iter != regions->end(); iter++) {
      if ((*iter).id().ieta() < 4 || (*iter).id().ieta() > 17 || (*iter).id().iphi() > 17)
        continue;
      if ((*iter).mip())
        nmip++;
      if (!(*iter).quiet())
        nnq++;
      /*
            edm::LogVerbatim("GMT_PSB_info") << (*iter).id().ieta()-4 << " "
                                             << (*iter).id().iphi() << " "
                                             << (*iter).quiet() << " "
                                             << (*iter).mip();
       */
    }

    of << "MIP  " << nmip << " ";
    for (iter = regions->begin(); iter != regions->end(); iter++) {
      if ((*iter).id().ieta() < 4 || (*iter).id().ieta() > 17 || (*iter).id().iphi() > 17)
        continue;
      if ((*iter).mip())
        of << (*iter).id().ieta() - 4 << " " << (((*iter).id().iphi() + 9) % 18) << " ";
    }
    of << endl;

    of << "NQ   " << nnq << " ";
    for (iter = regions->begin(); iter != regions->end(); iter++) {
      if ((*iter).id().ieta() < 4 || (*iter).id().ieta() > 17 || (*iter).id().iphi() > 17)
        continue;
      if (!(*iter).quiet())
        of << (*iter).id().ieta() - 4 << " " << (((*iter).id().iphi() + 9) % 18) << " ";
    }
    of << endl;
  }
}

void L1MuGMTPattern::printMI(const vector<unsigned>* mi) {
  if (m_outputType == 1) {
    ofstream of(m_outfilename.c_str(), ios::app);
    of << "MI  ";
    vector<unsigned>::const_iterator imi;
    for (imi = mi->begin(); imi != mi->end(); imi++) {
      of << " " << *imi;
    }
    of << endl;
  }
}

void L1MuGMTPattern::printCANC() {
  if (m_outputType == 1) {
    ofstream of(m_outfilename.c_str(), ios::app);
    of << "CANC 0 0 0 0" << endl;
  }
}

unsigned L1MuGMTPattern::invertQPt(unsigned w) {
  unsigned qpt = (w >> 8) & 0xff;
  w &= 0xffff00ff;
  w |= ((~qpt) << 8) & 0x0000ff00;
  return w;
}

//define this as a plug-in
DEFINE_FWK_MODULE(L1MuGMTPattern);
