#include "DataFormats/Math/interface/angle_units.h"
#include "DetectorDescription/DDCMS/interface/DDPlugins.h"
#include "DetectorDescription/DDCMS/interface/DDutils.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "DD4hep/DetFactoryHelper.h"

//#define EDM_ML_DEBUG
using namespace angle_units::operators;

static long algorithm(dd4hep::Detector& /* description */, cms::DDParsingContext& ctxt, xml_h e) {
  cms::DDNamespace ns(ctxt, e, true);
  cms::DDAlgoArguments args(ctxt, e);

  // Header section
  //     <---- Zout ---->
  //  |  ****************     |
  //  |  *              *     Wstep
  //  W  *              ***** |
  //  |  *                  *
  //  |  ********************
  //     <------ Zin ------->
  //     <------ Zout ------>         Zout = Full sector Z at position
  //  |  ********************         Zin  = Full sector Z at position
  //  |  *                 *
  //  W  *                * Angle = Theta sector
  //  |  *               *  )
  //  |  ****************--------
  //     <------ Zin ------->
  //     <------ Zout ------>         Zin(i)=Zout(i-1)
  //  |  ********************         Zout(i)=Zin(i)+W(i)/tan(Theta(i))
  //  |  *                 *
  //  W  *                *  Theta
  //  |  *               *
  //  |  ****************--------
  //     <--- Zin ------>
  std::string genMat = args.value<std::string>("MaterialName");           //General material
  int nsectors = args.value<int>("NSector");                              //Number of potenital straight edges
  int nsectortot = args.value<int>("NSectorTot");                         //Number of straight edges (actual)
  int nhalf = args.value<int>("NHalf");                                   //Number of half modules
  double rin = args.value<double>("RIn");                                 //(see Figure of hcalbarrel)
  std::vector<double> theta = args.value<std::vector<double> >("Theta");  //  .... (in degrees)
  std::vector<double> rmax = args.value<std::vector<double> >("RMax");    //  ....
  std::vector<double> zoff = args.value<std::vector<double> >("ZOff");    //  ....
  std::string absMat = args.value<std::string>("AbsMatName");             //Absorber material
  double thick = args.value<double>("Thickness");                         //Thickness of absorber
  double width1 = args.value<double>("Width1");                           //Width of absorber type 1
  double length1 = args.value<double>("Length1");                         //Length of absorber type 1
  double width2 = args.value<double>("Width2");                           //Width of absorber type 2
  double length2 = args.value<double>("Length2");                         //Length of absorber type 2
  double gap2 = args.value<double>("Gap2");                               //Gap between abosrbers of type 2
  std::string idName = args.value<std::string>("MotherName");             //Name of the "parent" volume.
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: General material " << genMat << "\tSectors " << nsectors << ", "
                               << nsectortot << "\tHalves " << nhalf << "\tRin " << cms::convert2mm(rin);
  for (unsigned int i = 0; i < theta.size(); i++)
    edm::LogVerbatim("HCalGeom") << "\t" << i << " Theta " << convertRadToDeg(theta[i]) << " rmax "
                                 << cms::convert2mm(rmax[i]) << " zoff " << cms::convert2mm(zoff[i]);
  edm::LogVerbatim("HCalGeom") << "\tCable mockup made of " << absMat << "\tThick " << cms::convert2mm(thick)
                               << "\tLength and width " << cms::convert2mm(length1) << ", " << cms::convert2mm(width1)
                               << " and " << cms::convert2mm(length2) << ", " << cms::convert2mm(width2) << " Gap "
                               << cms::convert2mm(gap2);
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: Parent " << args.parentName() << " idName " << idName
                               << " NameSpace " << ns.name() << " for solids";
#endif

  double alpha = 1._pi / nsectors;
  double dphi = nsectortot * 2._pi / nsectors;
  double zstep0 = zoff[1] + rmax[1] * tan(theta[1]) + (rin - rmax[1]) * tan(theta[2]);
  double zstep1 = zstep0 + thick / cos(theta[2]);
  double zstep2 = zoff[3];
  double rstep0 = rin + (zstep2 - zstep1) / tan(theta[2]);
  double rstep1 = rin + (zstep1 - zstep0) / tan(theta[2]);

  std::vector<double> pgonZ = {zstep0, zstep1, zstep2, zstep2 + thick / cos(theta[2])};
  std::vector<double> pgonRmin = {rin, rin, rstep0, rmax[2]};
  std::vector<double> pgonRmax = {rin, rstep1, rmax[2], rmax[2]};

  dd4hep::Solid solid = dd4hep::Polyhedra(ns.prepend(idName), nsectortot, -alpha, dphi, pgonZ, pgonRmin, pgonRmax);
  dd4hep::Material matter = ns.material(genMat);
  dd4hep::Volume genlogic(solid.name(), solid, matter);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << solid.name() << " Polyhedra made of " << genMat << " with "
                               << nsectortot << " sectors from " << convertRadToDeg(-alpha) << " to "
                               << convertRadToDeg(-alpha + dphi) << " and with " << pgonZ.size() << " sections";
  for (unsigned int i = 0; i < pgonZ.size(); i++)
    edm::LogVerbatim("HCalGeom") << "\t\tZ = " << cms::convert2mm(pgonZ[i])
                                 << "\tRmin = " << cms::convert2mm(pgonRmin[i])
                                 << "\tRmax = " << cms::convert2mm(pgonRmax[i]);
#endif

  dd4hep::Volume parent = ns.volume(args.parentName());
  dd4hep::Rotation3D rot;
  parent.placeVolume(genlogic, 1);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << genlogic.name() << " number 1 positioned in "
                               << parent.name() << " at (0, 0, 0) with no rotation";
#endif
  if (nhalf != 1) {
    rot = cms::makeRotation3D(90._deg, 180._deg, 90._deg, 90._deg, 180._deg, 0);
    parent.placeVolume(genlogic, 2, rot);
#ifdef EDM_ML_DEBUG
    edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << genlogic.name() << " number 2 positioned in "
                                 << parent.name() << " at (0, 0, 0) with rotation: " << rot;
#endif
  }

  //Construct sector (from -alpha to +alpha)
  std::string name = idName + "Module";
  solid = dd4hep::Polyhedra(ns.prepend(name), 1, -alpha, 2 * alpha, pgonZ, pgonRmin, pgonRmax);
  dd4hep::Volume seclogic(solid.name(), solid, matter);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << solid.name() << " Polyhedra made of " << genMat
                               << " with 1 sector from " << convertRadToDeg(-alpha) << " to " << convertRadToDeg(alpha)
                               << " and with " << pgonZ.size() << " sections";
  for (unsigned int i = 0; i < pgonZ.size(); i++)
    edm::LogVerbatim("HCalGeom") << "\t\tZ = " << cms::convert2mm(pgonZ[i])
                                 << "\tRmin = " << cms::convert2mm(pgonRmin[i])
                                 << "\tRmax = " << cms::convert2mm(pgonRmax[i]);
#endif

  for (int ii = 0; ii < nsectortot; ++ii) {
    double phi = ii * 2 * alpha;
    dd4hep::Rotation3D rotation;
    if (phi != 0) {
      rotation = dd4hep::RotationZ(phi);
#ifdef EDM_ML_DEBUG
      edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: Creating a new rotation "
                                   << "\t90," << convertRadToDeg(phi) << ",90," << (90 + convertRadToDeg(phi))
                                   << ", 0, 0";
#endif
    }
    genlogic.placeVolume(seclogic, ii + 1, rotation);
#ifdef EDM_ML_DEBUG
    edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << seclogic.name() << " number " << ii + 1
                                 << " positioned in " << genlogic.name() << " at (0, 0, 0) with rotation: " << rotation;
#endif
  }

  //Now a trapezoid of air
  double rinl = pgonRmin[0] + thick * sin(theta[2]);
  double routl = pgonRmax[2] - thick * sin(theta[2]);
  double dx1 = rinl * tan(alpha);
  double dx2 = 0.90 * routl * tan(alpha);
  double dy = 0.50 * thick;
  double dz = 0.50 * (routl - rinl);
  name = idName + "Trap";
  solid = dd4hep::Trap(ns.prepend(name), dz, 0, 0, dy, dx1, dx1, 0, dy, dx2, dx2, 0);
  dd4hep::Volume glog(solid.name(), solid, matter);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << solid.name() << " Trap made of " << genMat
                               << " of dimensions " << cms::convert2mm(dz) << ", 0, 0, " << cms::convert2mm(dy) << ", "
                               << cms::convert2mm(dx1) << ", " << cms::convert2mm(dx1) << ", 0, " << cms::convert2mm(dy)
                               << ", " << cms::convert2mm(dx2) << ", " << cms::convert2mm(dx2) << ", 0";
#endif

  rot = cms::makeRotation3D(90._deg, 270._deg, (180._deg - theta[2]), 0, (90._deg - theta[2]), 0);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: Creating a rotation: \t90, 270, "
                               << (180 - convertRadToDeg(theta[2])) << ", 0, " << (90 - convertRadToDeg(theta[2]))
                               << ", 0";
#endif
  dd4hep::Position r1(0.5 * (rinl + routl), 0, 0.5 * (pgonZ[1] + pgonZ[2]));
  seclogic.placeVolume(glog, 1, dd4hep::Transform3D(rot, r1));
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << glog.name() << " number 1 positioned in " << seclogic.name()
                               << " at (" << cms::convert2mm(0.5 * (rinl + routl)) << ", 0, "
                               << cms::convert2mm(0.5 * (pgonZ[1] + pgonZ[2])) << " with rotation: " << rot;
#endif
  //Now the cable of type 1
  name = idName + "Cable1";
  double phi = atan((dx2 - dx1) / (2 * dz));
  double xmid = 0.5 * (dx1 + dx2) - 1.0;
  solid = dd4hep::Box(ns.prepend(name), 0.5 * width1, 0.5 * thick, 0.5 * length1);
  dd4hep::Material absmatter = ns.material(absMat);
  dd4hep::Volume cablog1(solid.name(), solid, absmatter);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << solid.name() << " Box made of " << absMat << " of dimension "
                               << cms::convert2mm(0.5 * width1) << ", " << cms::convert2mm(0.5 * thick) << ", "
                               << cms::convert2mm(0.5 * length1);
#endif

  dd4hep::Rotation3D rot2 = cms::makeRotation3D((90._deg + phi), 0.0, 90._deg, 90._deg, phi, 0.0);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: Creating a rotation \t" << (90 + convertRadToDeg(phi))
                               << ", 0, 90, 90, " << convertRadToDeg(phi) << ", 0";
#endif
  dd4hep::Position r2((xmid - 0.5 * width1 * cos(phi)), 0, 0);
  glog.placeVolume(cablog1, 1, dd4hep::Transform3D(rot2, r2));
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << cablog1.name() << " number 1 positioned in " << glog.name()
                               << " at (" << cms::convert2mm(xmid - 0.5 * width1 * cos(phi))
                               << ", 0, 0) with rotation: " << rot2;
#endif
  dd4hep::Rotation3D rot3 = cms::makeRotation3D((90._deg - phi), 0, 90._deg, 90._deg, -phi, 0);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: Creating a rotation \t" << (90 - convertRadToDeg(phi))
                               << ", 0, 90, 90, " << convertRadToDeg(-phi) << ", 0";
#endif
  dd4hep::Position r3(-(xmid - 0.5 * width1 * cos(phi)), 0, 0);
  glog.placeVolume(cablog1, 2, dd4hep::Transform3D(rot3, r3));
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << cablog1.name() << " number 2 positioned in " << glog.name()
                               << " at (" << cms::convert2mm(xmid - 0.5 * width1 * cos(phi))
                               << ", 0, 0) with rotation: " << rot3;
#endif
  //Now the cable of type 2
  name = idName + "Cable2";
  solid = dd4hep::Box(ns.prepend(name), 0.5 * width2, 0.5 * thick, 0.5 * length2);
  dd4hep::Volume cablog2(solid.name(), solid, absmatter);
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << solid.name() << " Box made of " << absMat << " of dimension "
                               << cms::convert2mm(0.5 * width2) << ", " << cms::convert2mm(0.5 * thick) << ", "
                               << cms::convert2mm(0.5 * length2);
#endif

  glog.placeVolume(cablog2, 1, dd4hep::Position(0.5 * (width2 + gap2), 0, 0));
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << cablog2.name() << " number 1 positioned in " << glog.name()
                               << " at (" << cms::convert2mm(0.5 * (width2 + gap2)) << ", 0, 0) with no rotation";
#endif
  glog.placeVolume(cablog2, 2, dd4hep::Position(-0.5 * (width2 + gap2), 0, 0));
#ifdef EDM_ML_DEBUG
  edm::LogVerbatim("HCalGeom") << "DDHCalTBCableAlgo: " << cablog2.name() << " number 2 positioned in " << glog.name()
                               << " at " << cms::convert2mm(-0.5 * (width2 + gap2)) << ", 0, 0) with no rotation";

  edm::LogVerbatim("HCalGeom") << "<<== End of DDHCalTBCableAlgo construction";
#endif

  return cms::s_executed;
}

// first argument is the type from the xml file
DECLARE_DDCMS_DETELEMENT(DDCMS_hcal_DDHCalTBCableAlgo, algorithm);
