#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/global/EDAnalyzer.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/EventSetup.h"
#include "FWCore/Framework/interface/ESTransientHandle.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "FWCore/Utilities/interface/StreamID.h"

#include "RecoMTD/DetLayers/interface/MTDDetLayerGeometry.h"
#include "RecoMTD/Records/interface/MTDRecoGeometryRecord.h"

#include "RecoMTD/DetLayers/interface/MTDSectorForwardDoubleLayer.h"
#include "RecoMTD/DetLayers/interface/MTDDetSector.h"

#include <DataFormats/ForwardDetId/interface/ETLDetId.h>

#include <sstream>

using namespace std;
using namespace edm;

class TestETLNavigation : public global::EDAnalyzer<> {
public:
  TestETLNavigation(const ParameterSet& pset);

  void analyze(edm::StreamID, edm::Event const&, edm::EventSetup const&) const override;

private:
  const edm::ESInputTag tag_;
  edm::ESGetToken<MTDDetLayerGeometry, MTDRecoGeometryRecord> geomToken_;
};

TestETLNavigation::TestETLNavigation(const ParameterSet& iConfig) : tag_(edm::ESInputTag{"", ""}) {
  geomToken_ = esConsumes<MTDDetLayerGeometry, MTDRecoGeometryRecord>(tag_);
}

void TestETLNavigation::analyze(edm::StreamID, edm::Event const&, edm::EventSetup const& es) const {
  auto geo = es.getTransientHandle(geomToken_);

  const vector<const DetLayer*>& layers = geo->allETLLayers();

  // dump of ETL layers structure

  LogVerbatim("MTDLayerDump") << "\n\nTest of ETL navigation \n\n";

  for (const auto& ilay : layers) {
    const MTDSectorForwardDoubleLayer* layer = static_cast<const MTDSectorForwardDoubleLayer*>(ilay);

    LogVerbatim("MTDLayerDump") << std::fixed << "\nETL layer " << std::setw(4) << layer->subDetector()
                                << " at z = " << std::setw(14) << layer->surface().position().z()
                                << " sectors = " << std::setw(14) << layer->sectors().size()
                                << " dets = " << std::setw(14) << layer->basicComponents().size()
                                << " front dets = " << std::setw(14) << layer->frontLayer()->basicComponents().size()
                                << " back dets = " << std::setw(14) << layer->backLayer()->basicComponents().size();

    unsigned int isectInd(0);
    for (const auto& isector : layer->sectors()) {
      isectInd++;
      LogVerbatim("MTDLayerDump") << std::fixed << "\nSector " << std::setw(4) << isectInd << "\n" << (*isector);
      unsigned int imodInd(0);
      for (const auto& imod : isector->basicComponents()) {
        imodInd++;
        ETLDetId modId(imod->geographicalId().rawId());
        LogVerbatim("MTDLayerDump") << std::fixed << std::setw(5) << imodInd << " ETLDetId " << modId.rawId()
                                    << " side = " << std::setw(4) << modId.mtdSide()
                                    << " Disc/Side/Sector = " << std::setw(4) << modId.nDisc() << " " << std::setw(4)
                                    << modId.discSide() << " " << std::setw(4) << modId.sector()
                                    << " mod/type = " << std::setw(4) << modId.module() << " " << std::setw(4)
                                    << modId.modType() << " pos = " << std::setprecision(4) << imod->position();
        for (int iside = -1; iside <= 1; iside += 2) {
          size_t idetNew = isector->hshift(modId, iside);
          if (idetNew >= isector->basicComponents().size()) {
            LogVerbatim("MTDLayerDump") << "...............hshift= " << std::fixed << std::setw(2) << iside
                                        << " out of range";
          } else {
            ETLDetId newId(isector->basicComponents()[idetNew]->geographicalId().rawId());
            LogVerbatim("MTDLayerDump") << std::fixed << "...............hshift= " << std::setw(2) << iside
                                        << " side = " << std::setw(4) << newId.mtdSide()
                                        << " Disc/Side/Sector = " << std::setw(4) << newId.nDisc() << " "
                                        << std::setw(4) << newId.discSide() << " " << std::setw(4) << newId.sector()
                                        << " mod/type = " << std::setw(4) << newId.module() << " " << std::setw(4)
                                        << newId.modType() << " pos = " << std::setprecision(4)
                                        << isector->basicComponents()[idetNew]->position();
          }
        }
        for (int iside = -1; iside <= 1; iside += 2) {
          size_t closest(isector->basicComponents().size());
          size_t idetNew = isector->vshift(modId, iside, closest);
          if (idetNew >= isector->basicComponents().size()) {
            LogVerbatim("MTDLayerDump") << "...............vshift= " << std::fixed << std::setw(2) << iside
                                        << " out of range";
            if (closest < isector->basicComponents().size()) {
              ETLDetId newId(isector->basicComponents()[closest]->geographicalId().rawId());
              LogVerbatim("MTDLayerDump")
                  << std::fixed << ".......closest.vshift= " << std::setw(2) << iside << " side = " << std::setw(4)
                  << newId.mtdSide() << " Disc/Side/Sector = " << std::setw(4) << newId.nDisc() << " " << std::setw(4)
                  << newId.discSide() << " " << std::setw(4) << newId.sector() << " mod/type = " << std::setw(4)
                  << newId.module() << " " << std::setw(4) << newId.modType() << " pos = " << std::setprecision(4)
                  << isector->basicComponents()[closest]->position();
            }
          } else {
            ETLDetId newId(isector->basicComponents()[idetNew]->geographicalId().rawId());
            LogVerbatim("MTDLayerDump") << std::fixed << "...............vshift= " << std::setw(2) << iside
                                        << " side = " << std::setw(4) << newId.mtdSide()
                                        << " Disc/Side/Sector = " << std::setw(4) << newId.nDisc() << " "
                                        << std::setw(4) << newId.discSide() << " " << std::setw(4) << newId.sector()
                                        << " mod/type = " << std::setw(4) << newId.module() << " " << std::setw(4)
                                        << newId.modType() << " pos = " << std::setprecision(4)
                                        << isector->basicComponents()[idetNew]->position();
          }
        }
      }
    }
  }
}

//define this as a plug-in
#include <FWCore/Framework/interface/MakerMacros.h>
DEFINE_FWK_MODULE(TestETLNavigation);
