#ifndef PhysicsTools_PatAlgos_interface_SuperClusterHelper_h
#define PhysicsTools_PatAlgos_interface_SuperClusterHelper_h

#include "DataFormats/EgammaCandidates/interface/GsfElectron.h"
#include "RecoEcal/EgammaCoreTools/interface/EcalClusterTools.h"
#include "DataFormats/EcalRecHit/interface/EcalRecHitCollections.h"
#include "DataFormats/EgammaReco/interface/BasicCluster.h"
#include "DataFormats/PatCandidates/interface/Electron.h"
#include "Geometry/CaloTopology/interface/CaloSubdetectorTopology.h"
#include "Geometry/CaloGeometry/interface/CaloGeometry.h"

class SuperClusterHelper {
public:
  SuperClusterHelper(const reco::GsfElectron* electron,
                     const EcalRecHitCollection* rechits,
                     const CaloTopology*,
                     const CaloGeometry*);
  SuperClusterHelper(const pat::Electron* electron,
                     const EcalRecHitCollection* rechits,
                     const CaloTopology*,
                     const CaloGeometry*);
  ~SuperClusterHelper(){};

  float rawEnergy() const { return theSuperCluster_->rawEnergy(); }
  float eta() const { return theSuperCluster_->eta(); }
  float phi() const { return theSuperCluster_->phi(); }
  float etaWidth() const { return theSuperCluster_->etaWidth(); }
  float phiWidth() const { return theSuperCluster_->phiWidth(); }
  float clustersSize() const { return theSuperCluster_->clustersSize(); }
  float hadronicOverEm() const { return theElectron_->hadronicOverEm(); }
  float sigmaIetaIeta() const { return theElectron_->sigmaIetaIeta(); }
  float preshowerEnergy() const { return theSuperCluster_->preshowerEnergy(); }
  float preshowerEnergyOverRaw() const { return theSuperCluster_->preshowerEnergy() / theSuperCluster_->rawEnergy(); }
  float e3x3() const { return EcalClusterTools::e3x3(*seedCluster_, rechits_, topology_); }
  float e5x5() const { return EcalClusterTools::e5x5(*seedCluster_, rechits_, topology_); }
  float eMax() const { return EcalClusterTools::eMax(*seedCluster_, rechits_); }
  float e2nd() const { return EcalClusterTools::e2nd(*seedCluster_, rechits_); }
  float eTop() const { return EcalClusterTools::eTop(*seedCluster_, rechits_, topology_); }
  float eBottom() const { return EcalClusterTools::eBottom(*seedCluster_, rechits_, topology_); }
  float eLeft() const { return EcalClusterTools::eLeft(*seedCluster_, rechits_, topology_); }
  float eRight() const { return EcalClusterTools::eRight(*seedCluster_, rechits_, topology_); }
  float e2x5Max() const { return EcalClusterTools::e2x5Max(*seedCluster_, rechits_, topology_); }
  float e2x5Top() const { return EcalClusterTools::e2x5Top(*seedCluster_, rechits_, topology_); }
  float e2x5Bottom() const { return EcalClusterTools::e2x5Bottom(*seedCluster_, rechits_, topology_); }
  float e2x5Left() const { return EcalClusterTools::e2x5Left(*seedCluster_, rechits_, topology_); }
  float e2x5Right() const { return EcalClusterTools::e2x5Right(*seedCluster_, rechits_, topology_); }
  float r9() const {  //std::cout << " E3x3 " << e3x3() << " raw "  << theSuperCluster_->rawEnergy() << std::endl;
    return e3x3() / theSuperCluster_->rawEnergy();
  }
  float spp();
  float sep();
  float seedEta() const { return seedCluster_->eta(); }
  float seedPhi() const { return seedCluster_->phi(); }
  float seedEnergy() const { return seedCluster_->energy(); }
  int ietaSeed() {
    localCoordinates();
    return ietaSeed_;
  }
  int iphiSeed() {
    localCoordinates();
    return iphiSeed_;
  }
  float etaCrySeed() {
    localCoordinates();
    return etaCrySeed_;
  }
  float phiCrySeed() {
    localCoordinates();
    return phiCrySeed_;
  }
  float thetaTilt() {
    localCoordinates();
    return thetaTilt_;
  }
  float phiTilt() {
    localCoordinates();
    return phiTilt_;
  }

  // i=1,2,3 (0 would be the seed)
  float subClusterEnergy(unsigned i) const;
  float subClusterEta(unsigned i) const;
  float subClusterPhi(unsigned i) const;
  float subClusterEmax(unsigned i) const;
  float subClusterE3x3(unsigned i) const;
  // does not include the seed energy
  float eSubClusters() const { return eSubClusters_; }

  float esClusterEnergy(unsigned i) const;
  float esClusterEta(unsigned i) const;
  float esClusterPhi(unsigned i) const;

  float nPreshowerClusters() const { return nESClusters_; }
  float eESClusters() const { return eESClusters_; }

private:
  const reco::GsfElectron* theElectron_;
  const reco::SuperCluster* theSuperCluster_;
  std::vector<const reco::CaloCluster*> theBasicClusters_;
  std::vector<const reco::CaloCluster*> theESClusters_;
  const reco::CaloCluster* seedCluster_;
  const EcalRecHitCollection* rechits_;
  const CaloTopology* topology_;
  const CaloGeometry* geometry_;
  bool barrel_;

  /// cached variables
  /// covariance matrix
  bool covComputed_;
  float spp_;
  float sep_;
  /// local coordinates
  bool localCoordinatesComputed_;
  int ietaSeed_;  // ix in the endcaps
  int iphiSeed_;  // iy in the endcaps
  float etaCrySeed_;
  float phiCrySeed_;
  float thetaTilt_;
  float phiTilt_;
  unsigned nBasicClusters_;
  unsigned nESClusters_;
  float eSubClusters_;
  float eESClusters_;

private:
  void computeLocalCovariances();
  void localCoordinates();

  static bool sortClusters(const reco::CaloCluster* c1, const reco::CaloCluster* c2) {
    return (c1->energy() > c2->energy());
  }
};

#endif
