#ifndef L1GCTGLOBALENERGYALGOS_H_
#define L1GCTGLOBALENERGYALGOS_H_

#include "DataFormats/L1GlobalCaloTrigger/interface/L1GctEtTotal.h"
#include "DataFormats/L1GlobalCaloTrigger/interface/L1GctEtHad.h"
#include "DataFormats/L1GlobalCaloTrigger/interface/L1GctEtMiss.h"

#include "L1Trigger/GlobalCaloTrigger/interface/L1GctProcessor.h"
#include "L1Trigger/GlobalCaloTrigger/interface/L1GctMet.h"

#include <vector>

class L1GctWheelEnergyFpga;
class L1GctWheelJetFpga;
class L1GctGlobalHfSumAlgos;

/*!
 * \class L1GctGlobalEnergyAlgos
 * \brief Emulates the GCT global energy algorithms
 *
 * This class carries out the final stage of summing of
 * total Et, missing Et components Ex and Ey, calibrated
 * jet energy Ht, and jet counts. It converts the final
 * missing Ex and Ey sums to magnitude and direction.
 * The inputs come from the two Wheel cards
 * and the outputs are sent to the Global Trigger.
 *
 * \author Jim Brooke & Greg Heath
 * \date 20/2/2006
 * 
 */

class L1GctGlobalEnergyAlgos : public L1GctProcessor {
public:
  typedef L1GctUnsignedInt<L1GctEtTotal::kEtTotalNBits> etTotalType;
  typedef L1GctUnsignedInt<L1GctEtHad::kEtHadNBits> etHadType;
  typedef L1GctMet::etMissType etMissType;
  typedef L1GctMet::etMissPhiType etMissPhiType;
  typedef L1GctMet::etmiss_vec etmiss_vec;
  typedef L1GctWheelEnergyFpga::etComponentType etComponentType;

  enum maxValues {
    etTotalMaxValue = L1GctEtTotal::kEtTotalMaxValue,
    etHadMaxValue = L1GctEtHad::kEtHadMaxValue,
    etMissMaxValue = L1GctEtMiss::kEtMissMaxValue
  };

  /// Constructor needs the Wheel card Fpgas set up first
  L1GctGlobalEnergyAlgos(const std::vector<L1GctWheelEnergyFpga*>& WheelFpga,
                         const std::vector<L1GctWheelJetFpga*>& WheelJetFpga);
  /// Destructor
  ~L1GctGlobalEnergyAlgos() override;

  /// Overload << operator
  friend std::ostream& operator<<(std::ostream& os, const L1GctGlobalEnergyAlgos& fpga);

  /// clear internal buffers
  void reset();

  /// get input data from sources
  void fetchInput() override;

  /// process the data, fill output buffers
  void process() override;

  /// define the bunch crossing range to process
  void setBxRange(const int firstBx, const int numberOfBx);

  /// partially clear buffers
  void setNextBx(const int bx);

  /// set input Ex value per wheel (0 or 1); not used in normal operation
  void setInputWheelEx(unsigned wheel, int energy, bool overflow);
  /// set input Ey value per wheel (0 or 1); not used in normal operation
  void setInputWheelEy(unsigned wheel, int energy, bool overflow);
  /// set input Et value per wheel (0 or 1); not used in normal operation
  void setInputWheelEt(unsigned wheel, unsigned energy, bool overflow);
  /// set input Ht value per wheel (0 or 1); not used in normal operation
  void setInputWheelHt(unsigned wheel, unsigned energy, bool overflow);
  /// set input Ht component values per wheel (0 or 1); not used in normal operation
  void setInputWheelHx(unsigned wheel, unsigned energy, bool overflow);
  void setInputWheelHy(unsigned wheel, unsigned energy, bool overflow);

  /// set input jet count (number 0-11) per wheel (0 or 1); not used in normal operation
  void setInputWheelJc(unsigned wheel, unsigned jcnum, unsigned count);

  /// provide access to input pointer, Wheel Energy Fpga 1
  L1GctWheelEnergyFpga* getPlusWheelFpga() const { return m_plusWheelFpga; }
  /// provide access to input pointer, Wheel Energy Fpga 0
  L1GctWheelEnergyFpga* getMinusWheelFpga() const { return m_minusWheelFpga; }
  /// provide access to input pointer, Wheel Jet Fpga 1
  L1GctWheelJetFpga* getPlusWheelJetFpga() const { return m_plusWheelJetFpga; }
  /// provide access to input pointer, Wheel Jet Fpga 0
  L1GctWheelJetFpga* getMinusWheelJetFpga() const { return m_minusWheelJetFpga; }
  /// provide access to hf sum processor
  L1GctGlobalHfSumAlgos* getHfSumProcessor() const { return m_hfSumProcessor; }

  /// return input Ex value wheel 1
  inline std::vector<etComponentType> getInputExValPlusWheel() const { return m_exValPlusPipe.contents; }
  /// return input Ex value wheel 1
  inline std::vector<etComponentType> getInputEyValPlusWheel() const { return m_eyValPlusPipe.contents; }
  /// return input Ey value wheel 0
  inline std::vector<etComponentType> getInputExVlMinusWheel() const { return m_exVlMinusPipe.contents; }
  /// return input Ey value wheel 0
  inline std::vector<etComponentType> getInputEyVlMinusWheel() const { return m_eyVlMinusPipe.contents; }
  /// return input Et value wheel 1
  inline std::vector<etTotalType> getInputEtValPlusWheel() const { return m_etValPlusPipe.contents; }
  /// return input Ht value wheel 1
  inline std::vector<etHadType> getInputHtValPlusWheel() const { return m_htValPlusPipe.contents; }
  /// return input Ht component values wheel 1
  inline std::vector<etComponentType> getInputHxValPlusWheel() const { return m_hxValPlusPipe.contents; }
  inline std::vector<etComponentType> getInputHyValPlusWheel() const { return m_hyValPlusPipe.contents; }
  /// return input Et value wheel 0
  inline std::vector<etTotalType> getInputEtVlMinusWheel() const { return m_etVlMinusPipe.contents; }
  /// return input Ht value wheel 0
  inline std::vector<etHadType> getInputHtVlMinusWheel() const { return m_htVlMinusPipe.contents; }
  /// return input Ht value wheel 0
  inline std::vector<etComponentType> getInputHxVlMinusWheel() const { return m_hxVlMinusPipe.contents; }
  inline std::vector<etComponentType> getInputHyVlMinusWheel() const { return m_hyVlMinusPipe.contents; }

  /// Access to output quantities for all bunch crossings
  /// return output missing Et magnitude
  inline std::vector<etMissType> getEtMissColl() const { return m_outputEtMiss.contents; }
  /// return output missing Et value
  inline std::vector<etMissPhiType> getEtMissPhiColl() const { return m_outputEtMissPhi.contents; }
  /// return output total scalar Et
  inline std::vector<etTotalType> getEtSumColl() const { return m_outputEtSum.contents; }
  /// return std::vector< output calibrated jet Et
  inline std::vector<etHadType> getEtHadColl() const { return m_outputEtHad.contents; }
  /// return output missing Ht magnitude
  inline std::vector<etMissType> getHtMissColl() const { return m_outputHtMiss.contents; }
  /// return output missing Ht value
  inline std::vector<etMissPhiType> getHtMissPhiColl() const { return m_outputHtMissPhi.contents; }

  void setJetFinderParams(const L1GctJetFinderParams* const jfpars);
  void setHtMissScale(const L1CaloEtScale* const scale);

  // get the missing Ht LUT (used by L1GctPrintLuts)
  const L1GctHtMissLut* getHtMissLut() const { return m_mhtComponents.getHtMissLut(); }

  /// check setup
  bool setupOk() const;

protected:
  /// Separate reset methods for the processor itself and any data stored in pipelines
  void resetProcessor() override;
  void resetPipelines() override;

  /// Initialise inputs with null objects for the correct bunch crossing if required
  void setupObjects() override {}

private:
  // Here are the algorithm types we get our inputs from
  L1GctWheelEnergyFpga* m_plusWheelFpga;
  L1GctWheelEnergyFpga* m_minusWheelFpga;
  L1GctWheelJetFpga* m_plusWheelJetFpga;
  L1GctWheelJetFpga* m_minusWheelJetFpga;

  // Here's the class that does the Hf sums
  L1GctGlobalHfSumAlgos* m_hfSumProcessor;

  // Missing Et and missing Ht
  L1GctMet m_metComponents;
  L1GctMet m_mhtComponents;

  // input data
  etComponentType m_exValPlusWheel;
  etComponentType m_eyValPlusWheel;
  etTotalType m_etValPlusWheel;
  etHadType m_htValPlusWheel;
  etComponentType m_hxValPlusWheel;
  etComponentType m_hyValPlusWheel;

  etComponentType m_exVlMinusWheel;
  etComponentType m_eyVlMinusWheel;
  etTotalType m_etVlMinusWheel;
  etHadType m_htVlMinusWheel;
  etComponentType m_hxVlMinusWheel;
  etComponentType m_hyVlMinusWheel;

  // stored copies of input data
  Pipeline<etComponentType> m_exValPlusPipe;
  Pipeline<etComponentType> m_eyValPlusPipe;
  Pipeline<etTotalType> m_etValPlusPipe;
  Pipeline<etHadType> m_htValPlusPipe;
  Pipeline<etComponentType> m_hxValPlusPipe;
  Pipeline<etComponentType> m_hyValPlusPipe;

  Pipeline<etComponentType> m_exVlMinusPipe;
  Pipeline<etComponentType> m_eyVlMinusPipe;
  Pipeline<etTotalType> m_etVlMinusPipe;
  Pipeline<etHadType> m_htVlMinusPipe;
  Pipeline<etComponentType> m_hxVlMinusPipe;
  Pipeline<etComponentType> m_hyVlMinusPipe;

  // output data
  Pipeline<etMissType> m_outputEtMiss;
  Pipeline<etMissPhiType> m_outputEtMissPhi;
  Pipeline<etTotalType> m_outputEtSum;
  Pipeline<etHadType> m_outputEtHad;
  Pipeline<etMissType> m_outputHtMiss;
  Pipeline<etMissPhiType> m_outputHtMissPhi;

  bool m_setupOk;
};

std::ostream& operator<<(std::ostream& os, const L1GctGlobalEnergyAlgos& fpga);

#endif /*L1GCTGLOBALENERGYALGOS_H_*/
