#include <memory>
#include <cctype>

#include "CLHEP/Random/DRand48Engine.h"
#include "CLHEP/Random/RandGauss.h"
#include "CLHEP/Random/Randomize.h"

#include "DataFormats/GeometryCommonDetAlgo/interface/AlignmentPositionError.h"
#include "FWCore/ServiceRegistry/interface/Service.h"
#include "FWCore/Utilities/interface/RandomNumberGenerator.h"
#include "FWCore/MessageLogger/interface/MessageLogger.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"

#include "Alignment/CommonAlignment/interface/AlignableComposite.h"
#include "Alignment/CommonAlignment/interface/AlignableModifier.h"

#include "Geometry/CommonTopologies/interface/SurfaceDeformationFactory.h"
#include "Geometry/CommonTopologies/interface/SurfaceDeformation.h"

//__________________________________________________________________________________________________
AlignableModifier::AlignableModifier(void)
    : distribution_(""),
      random_(false),
      gaussian_(false),
      setError_(false),
      setRotations_(false),
      setTranslations_(false),
      seed_(0),
      scaleError_(0.),
      scale_(0.),
      phiX_(0.),
      phiY_(0.),
      phiZ_(0.),
      phiXlocal_(0.),
      phiYlocal_(0.),
      phiZlocal_(0.),
      dX_(0.),
      dY_(0.),
      dZ_(0.),
      dXlocal_(0.),
      dYlocal_(0.),
      dZlocal_(0.),
      twist_(0.),
      shear_(0.) {
  theDRand48Engine = new CLHEP::DRand48Engine();
}

//__________________________________________________________________________________________________
AlignableModifier::~AlignableModifier() { delete theDRand48Engine; }

//__________________________________________________________________________________________________
void AlignableModifier::init_(void) {
  // Initialize all known parameters (according to ORCA's MisalignmentScenario.cc)
  distribution_ = "";           // Switch for distributions ("fixed","flat","gaussian")
  setError_ = false;            // Apply alignment errors
  setRotations_ = true;         // Apply rotations
  setTranslations_ = true;      // Apply translations
  scale_ = 1.;                  // Scale to apply to all movements
  scaleError_ = 1.;             // Scale to apply to alignment errors
  phiX_ = 0.;                   // Rotation angle around X [rad]
  phiY_ = 0.;                   // Rotation angle around Y [rad]
  phiZ_ = 0.;                   // Rotation angle around Z [rad]
  phiXlocal_ = 0.;              // Local rotation angle around X [rad]
  phiYlocal_ = 0.;              // Local rotation angle around Y [rad]
  phiZlocal_ = 0.;              // Local rotation angle around Z [rad]
  dX_ = 0.;                     // X displacement [cm]
  dY_ = 0.;                     // Y displacement [cm]
  dZ_ = 0.;                     // Z displacement [cm]
  dXlocal_ = 0.;                // Local X displacement [cm]
  dYlocal_ = 0.;                // Local Y displacement [cm]
  dZlocal_ = 0.;                // Local Z displacement [cm]
  deformation_.first.clear();   // SurfaceDeformation: type
  deformation_.second.clear();  //SurfaceDeformation: parameter vector
  twist_ = 0.;                  // Twist angle [rad]
  shear_ = 0.;                  // Shear angle [rad]

  // These are set through 'distribution'
  random_ = true;    // Use random distributions
  gaussian_ = true;  // Use gaussian distribution (otherwise flat)
}

//__________________________________________________________________________________________________
// Return true if given parameter name should be propagated down
bool AlignableModifier::isPropagated(const std::string& parameterName) const {
  if (parameterName == "distribution" || parameterName == "setError" || parameterName == "scaleError" ||
      parameterName == "setRotations" || parameterName == "setTranslations" || parameterName == "scale")
    return true;

  return false;
}

//__________________________________________________________________________________________________
/// All known parameters and defaults are defined here! Returns true if modification actually applied.
bool AlignableModifier::modify(Alignable* alignable, const edm::ParameterSet& pSet) {
  // Initialize parameters
  this->init_();
  int rotX_ = 0, rotY_ = 0, rotZ_ = 0;  // To check correct backward compatibility

  // Reset counter
  m_modified = 0;

  // Retrieve parameters
  std::ostringstream error;
  std::vector<std::string> parameterNames = pSet.getParameterNames();
  for (std::vector<std::string>::iterator iParam = parameterNames.begin(); iParam != parameterNames.end(); ++iParam) {
    if ((*iParam) == "distribution")
      distribution_ = pSet.getParameter<std::string>(*iParam);
    else if ((*iParam) == "setError")
      setError_ = pSet.getParameter<bool>(*iParam);
    else if ((*iParam) == "setRotations")
      setRotations_ = pSet.getParameter<bool>(*iParam);
    else if ((*iParam) == "setTranslations")
      setTranslations_ = pSet.getParameter<bool>(*iParam);
    else if ((*iParam) == "scale")
      scale_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "scaleError")
      scaleError_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "phiX")
      phiX_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "phiY")
      phiY_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "phiZ")
      phiZ_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dX")
      dX_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dY")
      dY_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dZ")
      dZ_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dXlocal")
      dXlocal_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dYlocal")
      dYlocal_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "dZlocal")
      dZlocal_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "twist")
      twist_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "shear")
      shear_ = pSet.getParameter<double>(*iParam);
    else if ((*iParam) == "localX") {
      phiXlocal_ = pSet.getParameter<double>(*iParam);
      rotX_++;
    } else if ((*iParam) == "localY") {
      phiYlocal_ = pSet.getParameter<double>(*iParam);
      rotY_++;
    } else if ((*iParam) == "localZ") {
      phiZlocal_ = pSet.getParameter<double>(*iParam);
      rotZ_++;
    } else if ((*iParam) == "phiXlocal") {
      phiXlocal_ = pSet.getParameter<double>(*iParam);
      rotX_++;
    } else if ((*iParam) == "phiYlocal") {
      phiYlocal_ = pSet.getParameter<double>(*iParam);
      rotY_++;
    } else if ((*iParam) == "phiZlocal") {
      phiZlocal_ = pSet.getParameter<double>(*iParam);
      rotZ_++;
    } else if ((*iParam) == "deformation") {
      const edm::ParameterSet deform(pSet.getParameter<edm::ParameterSet>(*iParam));
      deformation_.first = deform.getParameter<std::string>("type");
      deformation_.second = deform.getParameter<std::vector<double> >("parameters");
    } else if (pSet.existsAs<edm::ParameterSet>(*iParam)) {
      // Other PSets than 'deformation' must refer to hierarchy structures, i.e. their name
      // is a level name followed by 's' or ending with a digit (see
      // MisalignmentScenarioBuilder::getParameterSet_). Pitfall is to forget the trailing 's'!
      // 'Muon' is an especially allowed case used in MuonScenarioBuilder::moveMuon(..),
      //  also check that we do not have any mistyping like 'deformations' or 'deformation2'
      const auto lastCharacter = (iParam->empty() ? '_' : (*iParam)[iParam->size() - 1]);
      if ((lastCharacter != 's' && !isdigit(lastCharacter) && (*iParam) != "Muon") ||
          iParam->find("deformation") != std::string::npos) {
        throw cms::Exception("BadConfig") << "@SUB=AlignableModifier::modify(..):\n"
                                          << "I see parameter '" << *iParam << "' of type PSet, "
                                          << "but expect either 'deformation' or a level name "
                                          << "with 's' or a digit at the end.\n";
      }  // other PSets should now be hierarchy levels and thus be OK to ignore here
    } else {
      if (!error.str().length())
        error << "Unknown parameter name(s): ";
      error << " " << *iParam;
    }
  }

  // Check if both 'localN' and 'phiNlocal' have been used
  if (rotX_ == 2)
    throw cms::Exception("BadConfig") << "Found both localX and phiXlocal";
  if (rotY_ == 2)
    throw cms::Exception("BadConfig") << "Found both localY and phiYlocal";
  if (rotZ_ == 2)
    throw cms::Exception("BadConfig") << "Found both localZ and phiZlocal";

  // Check error
  if (error.str().length())
    throw cms::Exception("BadConfig") << error.str();

  // Decode distribution
  this->setDistribution(distribution_);

  //if (scale_) { NO! Different random sequence if only parts scale to zero!

  // Apply displacements
  if (std::abs(dX_) + std::abs(dY_) + std::abs(dZ_) > 0 && setTranslations_)
    this->moveAlignable(alignable, random_, gaussian_, scale_ * dX_, scale_ * dY_, scale_ * dZ_);

  // Apply local displacements
  if (std::abs(dXlocal_) + std::abs(dYlocal_) + std::abs(dZlocal_) > 0 && setTranslations_)
    this->moveAlignableLocal(alignable, random_, gaussian_, scale_ * dXlocal_, scale_ * dYlocal_, scale_ * dZlocal_);

  // Apply rotations
  if (std::abs(phiX_) + std::abs(phiY_) + std::abs(phiZ_) > 0 && setRotations_)
    this->rotateAlignable(alignable, random_, gaussian_, scale_ * phiX_, scale_ * phiY_, scale_ * phiZ_);

  // Apply local rotations
  if (std::abs(phiXlocal_) + std::abs(phiYlocal_) + std::abs(phiZlocal_) > 0 && setRotations_)
    this->rotateAlignableLocal(
        alignable, random_, gaussian_, scale_ * phiXlocal_, scale_ * phiYlocal_, scale_ * phiZlocal_);

  // Apply twist
  if (std::abs(twist_) > 0)
    edm::LogError("NotImplemented") << "Twist is not implemented yet";

  // Apply shear
  if (std::abs(shear_) > 0)
    edm::LogError("NotImplemented") << "Shear is not implemented yet";

  if (!deformation_.first.empty()) {
    this->addDeformation(alignable, deformation_, random_, gaussian_, scale_);
  }

  // Apply error - first add scale_ to error
  scaleError_ *= scale_;
  if (setError_ && scaleError_) {
    // Alignment Position Error for flat distribution: 1 sigma
    if (!gaussian_)
      scaleError_ *= 0.68;

    // Error on displacement
    if (std::abs(dX_) + std::abs(dY_) + std::abs(dZ_) > 0 && setTranslations_)
      this->addAlignmentPositionError(alignable, scaleError_ * dX_, scaleError_ * dY_, scaleError_ * dZ_);

    // Error on local displacements
    if (std::abs(dXlocal_) + std::abs(dYlocal_) + std::abs(dZlocal_) > 0 && setTranslations_)
      this->addAlignmentPositionErrorLocal(
          alignable, scaleError_ * dXlocal_, scaleError_ * dYlocal_, scaleError_ * dZlocal_);

    // Error on rotations
    if (std::abs(phiX_) + std::abs(phiY_) + std::abs(phiZ_) > 0 && setRotations_)
      this->addAlignmentPositionErrorFromRotation(
          alignable, scaleError_ * phiX_, scaleError_ * phiY_, scaleError_ * phiZ_);

    // Error on local rotations
    if (std::abs(phiXlocal_) + std::abs(phiYlocal_) + std::abs(phiZlocal_) > 0 && setRotations_)
      this->addAlignmentPositionErrorFromLocalRotation(
          alignable, scaleError_ * phiXlocal_, scaleError_ * phiYlocal_, scaleError_ * phiZlocal_);
    // Do we need to add any APE for deformations?
    // Probably we would do so if there wouldn't be data, but only MC to play with... ;-)
  }
  // } // end if (scale_)

  return (m_modified > 0);
}

//__________________________________________________________________________________________________
void AlignableModifier::setDistribution(const std::string& distr) {
  if (distr == "fixed")
    random_ = false;
  else if (distr == "flat") {
    random_ = true;
    gaussian_ = false;
  } else if (distr == "gaussian") {
    random_ = true;
    gaussian_ = true;
  }
}

//__________________________________________________________________________________________________
/// If 'seed' is zero, asks  RandomNumberGenerator service.
void AlignableModifier::setSeed(const long seed) {
  long m_seed;

  if (seed > 0)
    m_seed = seed;
  else {
    edm::Service<edm::RandomNumberGenerator> rng;
    m_seed = rng->mySeed();
  }

  LogDebug("PrintArgs") << "Setting generator seed to " << m_seed;

  theDRand48Engine->setSeed(m_seed);
}

//__________________________________________________________________________________________________
/// If 'random' is false, the given movements are strictly applied. Otherwise, a random
/// number is generated according to a gaussian or a flat distribution depending on 'gaussian'.
void AlignableModifier::moveAlignable(
    Alignable* alignable, bool random, bool gaussian, float sigmaX, float sigmaY, float sigmaZ) {
  std::ostringstream message;

  // Get movement vector according to arguments
  GlobalVector moveV(sigmaX, sigmaY, sigmaZ);  // Default: fixed
  if (random) {
    std::vector<float> randomNumbers;
    message << "random ";
    if (gaussian) {
      randomNumbers = this->gaussianRandomVector(sigmaX, sigmaY, sigmaZ);
      message << "gaussian ";
    } else {
      randomNumbers = this->flatRandomVector(sigmaX, sigmaY, sigmaZ);
      message << "flat ";
    }
    moveV = GlobalVector(randomNumbers[0], randomNumbers[1], randomNumbers[2]);
  }

  message << " move with sigma " << sigmaX << " " << sigmaY << " " << sigmaZ;

  LogDebug("PrintArgs") << message.str();  // Arguments

  LogDebug("PrintMovement") << "applied displacement: " << moveV;  // Actual movements
  alignable->move(moveV);
  m_modified++;
}

//__________________________________________________________________________________________________
/// If 'random' is false, the given movements are strictly applied. Otherwise, a random
/// number is generated according to a gaussian or a flat distribution depending on 'gaussian'.
void AlignableModifier::moveAlignableLocal(
    Alignable* alignable, bool random, bool gaussian, float sigmaX, float sigmaY, float sigmaZ) {
  std::ostringstream message;

  // Get movement vector according to arguments
  align::LocalVector moveV(sigmaX, sigmaY, sigmaZ);  // Default: fixed
  if (random) {
    std::vector<float> randomNumbers;
    message << "random ";
    if (gaussian) {
      randomNumbers = this->gaussianRandomVector(sigmaX, sigmaY, sigmaZ);
      message << "gaussian ";
    } else {
      randomNumbers = this->flatRandomVector(sigmaX, sigmaY, sigmaZ);
      message << "flat ";
    }
    moveV = align::LocalVector(randomNumbers[0], randomNumbers[1], randomNumbers[2]);
  }

  message << " move with sigma " << sigmaX << " " << sigmaY << " " << sigmaZ;

  LogDebug("PrintArgs") << message.str();  // Arguments

  LogDebug("PrintMovement") << "applied local displacement: " << moveV;  // Actual movements
  alignable->move(alignable->surface().toGlobal(moveV));
  m_modified++;
}

//__________________________________________________________________________________________________
void AlignableModifier ::addDeformation(Alignable* alignable,
                                        const AlignableModifier::DeformationMemberType& deformation,
                                        bool random,
                                        bool gaussian,
                                        double scale) {
  const SurfaceDeformationFactory::Type deformType =
      SurfaceDeformationFactory::surfaceDeformationType(deformation.first);

  // Scale and randomize
  // (need a little hack since ySplit must not be treated)!
  const bool rndNotLast = (deformType == SurfaceDeformationFactory::kTwoBowedSurfaces);
  std::vector<double> rndDeformation(deformation.second.begin(), deformation.second.end() - (rndNotLast ? 1 : 0));
  for (unsigned int i = 0; i < rndDeformation.size(); ++i) {
    rndDeformation[i] *= scale;
  }
  if (random) {
    this->randomise(rndDeformation, gaussian);
  }
  if (rndNotLast) {  // put back ySplit at the end
    rndDeformation.push_back(deformation.second.back());
  }

  // auto_ptr has exception safe delete (in contrast to bare pointer)
  const std::unique_ptr<SurfaceDeformation> surfDef(SurfaceDeformationFactory::create(deformType, rndDeformation));

  alignable->addSurfaceDeformation(surfDef.get(), true);  // true to propagate down
  ++m_modified;
}

//__________________________________________________________________________________________________
/// If 'random' is false, the given rotations are strictly applied. Otherwise, a random
/// number is generated according to a gaussian or a flat distribution depending on 'gaussian'.
void AlignableModifier::rotateAlignable(
    Alignable* alignable, bool random, bool gaussian, float sigmaPhiX, float sigmaPhiY, float sigmaPhiZ) {
  std::ostringstream message;

  // Get rotation vector according to arguments
  GlobalVector rotV(sigmaPhiX, sigmaPhiY, sigmaPhiZ);  // Default: fixed
  if (random) {
    std::vector<float> randomNumbers;
    message << "random ";
    if (gaussian) {
      randomNumbers = this->gaussianRandomVector(sigmaPhiX, sigmaPhiY, sigmaPhiZ);
      message << "gaussian ";
    } else {
      randomNumbers = flatRandomVector(sigmaPhiX, sigmaPhiY, sigmaPhiZ);
      message << "flat ";
    }
    rotV = GlobalVector(randomNumbers[0], randomNumbers[1], randomNumbers[2]);
  }

  message << "global rotation by angles " << sigmaPhiX << " " << sigmaPhiY << " " << sigmaPhiZ;

  LogDebug("PrintArgs") << message.str();  // Arguments

  LogDebug("PrintMovement") << "applied rotation angles: " << rotV;  // Actual movements
  if (std::abs(sigmaPhiX))
    alignable->rotateAroundGlobalX(rotV.x());
  if (std::abs(sigmaPhiY))
    alignable->rotateAroundGlobalY(rotV.y());
  if (std::abs(sigmaPhiZ))
    alignable->rotateAroundGlobalZ(rotV.z());
  m_modified++;
}

//__________________________________________________________________________________________________
/// If 'random' is false, the given rotations are strictly applied. Otherwise, a random
/// number is generated according to a gaussian or a flat distribution depending on 'gaussian'.
void AlignableModifier::rotateAlignableLocal(
    Alignable* alignable, bool random, bool gaussian, float sigmaPhiX, float sigmaPhiY, float sigmaPhiZ) {
  std::ostringstream message;

  // Get rotation vector according to arguments
  align::LocalVector rotV(sigmaPhiX, sigmaPhiY, sigmaPhiZ);  // Default: fixed
  if (random) {
    std::vector<float> randomNumbers;
    message << "random ";
    if (gaussian) {
      randomNumbers = this->gaussianRandomVector(sigmaPhiX, sigmaPhiY, sigmaPhiZ);
      message << "gaussian ";
    } else {
      randomNumbers = flatRandomVector(sigmaPhiX, sigmaPhiY, sigmaPhiZ);
      message << "flat ";
    }
    rotV = align::LocalVector(randomNumbers[0], randomNumbers[1], randomNumbers[2]);
  }

  message << "local rotation by angles " << sigmaPhiX << " " << sigmaPhiY << " " << sigmaPhiZ;

  LogDebug("PrintArgs") << message.str();  // Arguments

  LogDebug("PrintMovement") << "applied local rotation angles: " << rotV;  // Actual movements
  if (std::abs(sigmaPhiX))
    alignable->rotateAroundLocalX(rotV.x());
  if (std::abs(sigmaPhiY))
    alignable->rotateAroundLocalY(rotV.y());
  if (std::abs(sigmaPhiZ))
    alignable->rotateAroundLocalZ(rotV.z());
  m_modified++;
}

//__________________________________________________________________________________________________
const std::vector<float> AlignableModifier::gaussianRandomVector(float sigmaX, float sigmaY, float sigmaZ) const {
  // Get absolute value if negative arguments
  if (sigmaX < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for gaussian sigma_x";
    sigmaX = std::abs(sigmaX);
  }
  if (sigmaY < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for gaussian sigma_y";
    sigmaY = std::abs(sigmaY);
  }
  if (sigmaZ < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for gaussian sigma_z";
    sigmaZ = std::abs(sigmaZ);
  }

  // Pass by reference, otherwise pointer is deleted!
  CLHEP::RandGauss aGaussObjX(*theDRand48Engine, 0., sigmaX);
  CLHEP::RandGauss aGaussObjY(*theDRand48Engine, 0., sigmaY);
  CLHEP::RandGauss aGaussObjZ(*theDRand48Engine, 0., sigmaZ);

  std::vector<float> randomVector;
  randomVector.push_back(aGaussObjX.fire());
  randomVector.push_back(aGaussObjY.fire());
  randomVector.push_back(aGaussObjZ.fire());

  return randomVector;
}

//__________________________________________________________________________________________________
const std::vector<float> AlignableModifier::flatRandomVector(float sigmaX, float sigmaY, float sigmaZ) const {
  // Get absolute value if negative arguments
  if (sigmaX < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for flat sigma_x";
    sigmaX = std::abs(sigmaX);
  }
  if (sigmaY < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for flat sigma_y";
    sigmaY = std::abs(sigmaY);
  }
  if (sigmaZ < 0) {
    edm::LogWarning("BadConfig") << " taking absolute value for flat sigma_z";
    sigmaZ = std::abs(sigmaZ);
  }

  CLHEP::RandFlat aFlatObjX(*theDRand48Engine, -sigmaX, sigmaX);
  CLHEP::RandFlat aFlatObjY(*theDRand48Engine, -sigmaY, sigmaY);
  CLHEP::RandFlat aFlatObjZ(*theDRand48Engine, -sigmaZ, sigmaZ);

  std::vector<float> randomVector;
  randomVector.push_back(aFlatObjX.fire());
  randomVector.push_back(aFlatObjY.fire());
  randomVector.push_back(aFlatObjZ.fire());

  return randomVector;
}

//__________________________________________________________________________________________________
void AlignableModifier::randomise(std::vector<double>& rnd, bool gaussian) const {
  for (unsigned int i = 0; i < rnd.size(); ++i) {
    if (rnd[i] < 0.) {
      edm::LogWarning("BadConfig") << " taking absolute value to randomise " << i;
      rnd[i] = std::abs(rnd[i]);
    }

    if (gaussian) {
      CLHEP::RandGauss aGaussObj(*theDRand48Engine, 0., rnd[i]);
      rnd[i] = aGaussObj.fire();
    } else {
      CLHEP::RandFlat aFlatObj(*theDRand48Engine, -rnd[i], rnd[i]);
      rnd[i] = aFlatObj.fire();
    }
  }
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionError(Alignable* alignable, float dx, float dy, float dz) {
  LogDebug("PrintArgs") << "Adding an AlignmentPositionError of size " << dx << " " << dy << " " << dz;

  AlignmentPositionError ape(dx, dy, dz);
  alignable->addAlignmentPositionError(ape, true);
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionErrorLocal(Alignable* alignable, float dx, float dy, float dz) {
  LogDebug("PrintArgs") << "Adding a local AlignmentPositionError of size " << dx << " " << dy << " " << dz;

  AlgebraicSymMatrix as(3, 0);  //3x3, zeroed
  as[0][0] = dx * dx;
  as[1][1] = dy * dy;
  as[2][2] = dz * dz;                                    //diagonals
  align::RotationType rt = alignable->globalRotation();  //get rotation
  AlgebraicMatrix am(3, 3);
  am[0][0] = rt.xx();
  am[0][1] = rt.xy();
  am[0][2] = rt.xz();
  am[1][0] = rt.yx();
  am[1][1] = rt.yy();
  am[1][2] = rt.yz();
  am[2][0] = rt.zx();
  am[2][1] = rt.zy();
  am[2][2] = rt.zz();
  as = as.similarityT(am);  //rotate error matrix

  GlobalError ge(asSMatrix<3>(as));
  GlobalErrorExtended gee(ge.cxx(),
                          ge.cyx(),
                          ge.czx(),
                          0.,
                          0.,
                          0.,
                          ge.cyy(),
                          ge.czy(),
                          0.,
                          0.,
                          0.,
                          ge.czz(),
                          0.,
                          0.,
                          0.,
                          0.,
                          0.,
                          0.,
                          0.,
                          0.,
                          0.);
  AlignmentPositionError ape(gee);

  alignable->addAlignmentPositionError(ape, true);  // propagate down to components
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionErrorFromRotation(Alignable* alignable, float phiX, float phiY, float phiZ) {
  align::RotationType rotx(Basic3DVector<float>(1.0, 0.0, 0.0), phiX);
  align::RotationType roty(Basic3DVector<float>(0.0, 1.0, 0.0), phiY);
  align::RotationType rotz(Basic3DVector<float>(0.0, 0.0, 1.0), phiZ);
  align::RotationType rot = rotz * roty * rotx;

  this->addAlignmentPositionErrorFromRotation(alignable, rot);
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionErrorFromLocalRotation(Alignable* alignable,
                                                                   float phiX,
                                                                   float phiY,
                                                                   float phiZ) {
  align::RotationType rotx(Basic3DVector<float>(1.0, 0.0, 0.0), phiX);
  align::RotationType roty(Basic3DVector<float>(0.0, 1.0, 0.0), phiY);
  align::RotationType rotz(Basic3DVector<float>(0.0, 0.0, 1.0), phiZ);
  align::RotationType rot = rotz * roty * rotx;

  this->addAlignmentPositionErrorFromLocalRotation(alignable, rot);
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionErrorFromRotation(Alignable* alignable, align::RotationType& rotation) {
  LogDebug("PrintArgs") << "Adding an AlignmentPositionError from Rotation" << std::endl << rotation;

  alignable->addAlignmentPositionErrorFromRotation(rotation, true);  // propagate down to components
}

//__________________________________________________________________________________________________
void AlignableModifier::addAlignmentPositionErrorFromLocalRotation(Alignable* alignable,
                                                                   align::RotationType& rotation) {
  LogDebug("PrintArgs") << "Adding an AlignmentPositionError from Local Rotation" << std::endl << rotation;

  // true: propagate down to components
  alignable->addAlignmentPositionErrorFromLocalRotation(rotation, true);
}
