text: //   COCOA class implementation file
  //Id:  OptOMirror.cc
  //CAT: Model
  //
  //   History: v1.0
  //   Pedro Arce
  
  #include "Alignment/CocoaModel/interface/OptOMirror.h"
  #include "Alignment/CocoaModel/interface/LightRay.h"
  #include "Alignment/CocoaModel/interface/ALIPlane.h"
  #include "Alignment/CocoaModel/interface/Measurement.h"
  #include <iostream>
  #include <iomanip>
  #ifdef COCOA_VIS
  #include "Alignment/IgCocoaFileWriter/interface/IgCocoaFileMgr.h"
  #include "Alignment/CocoaVisMgr/interface/ALIColour.h"
  #endif
  #include "Alignment/CocoaDDLObjects/interface/CocoaSolidShapeBox.h"
  #include "Alignment/CocoaUtilities/interface/GlobalOptionMgr.h"
  
  using namespace CLHEP;
  
  //---------- Default behaviour: create a LightRay object
  void OptOMirror::defaultBehaviour(LightRay& lightray, Measurement& meas) { detailedDeviatesLightRay(lightray); }
  
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //@@ Detailed simulation of Reflection in Mirror
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::detailedDeviatesLightRay(LightRay& lightray) {
    if (ALIUtils::debug >= 2)
      std::cout << "LR: DETAILED REFLECTION IN MIRROR " << name() << std::endl;
    if (ALIUtils::debug >= 3)
      ALIUtils::dump3v(centreGlob(), " centre Global ");
  
    //---------- Get forward plate and intersect lightray with it
    ALIPlane plate = getPlate(true, false);
    lightray.intersect(plate);
    CLHEP::Hep3Vector inters = lightray.point();
  
    //---------- Get centre of forward plate
    //----- Get Z axis
    CLHEP::Hep3Vector ZAxis = getZAxis();
    CLHEP::HepRotation rmt = rmGlob();
    ZAxis = rmt * ZAxis;
    //----- Get centre
    ALIdouble width = findExtraEntryValue("width");
    CLHEP::Hep3Vector plate_centre = centreGlob() - 0.5 * width * ZAxis;
    //-  if(ALIUtils::debug >= 4) std::cout << " mirror width " << width << std::endl;
  
    //---------- Get the distance between the intersection point and the centre of the forward plate
    ALIdouble distance = (plate_centre - inters).mag();
  
    //---------- Get normal to mirror at intersection point
    //------- Get angle of mirror surface (angle between plate centre and intersection)
    ALIdouble flatness = findExtraEntryValue("flatness");
    //-- flatness is defined as number of 632 nm wavelengths
    flatness *= 632.E-9;
    ALIdouble length = 0.;
  
    ALIdouble curvature_radius;
    ALIdouble angFlatness;
    if (flatness != 0) {
      length = findExtraEntryValueMustExist("length");
      curvature_radius = (flatness * flatness + length * length) / (2 * flatness);
      angFlatness = asin(distance / curvature_radius);
    } else {
      curvature_radius = ALI_DBL_MAX;
      angFlatness = 0;
    }
  
    if (ALIUtils::debug >= 3) {
      std::cout << " intersection with plate " << inters << std::endl;
      std::cout << " plate_centre " << plate_centre << std::endl;
      std::cout << " distance plate_centre - intersection " << distance << std::endl;
      std::cout << " flatness " << flatness << ", length " << length;
      std::cout << ", curvature radius " << curvature_radius << " angle of flatness " << angFlatness << std::endl;
    }
  
    //----- Axis of rotation is perpendicular to Z Axis and to line plate_centre - intersection
    CLHEP::Hep3Vector ipcV = inters - plate_centre;
    if (ipcV.mag() != 0)
      ipcV *= 1. / ipcV.mag();
    CLHEP::HepRotation rtm = rmGlob();
    ipcV = rtm * ipcV;
    CLHEP::Hep3Vector rotationAxis = ipcV.cross(ZAxis);
    //----- normal is object normal rotated around this axis
    CLHEP::Hep3Vector inters_normal = CLHEP::Hep3Vector(0., 0., 1.);
    inters_normal.rotate(angFlatness, rotationAxis);
    inters_normal = rmt * inters_normal;
  
    if (ALIUtils::debug >= 2) {
      ALIUtils::dump3v(ipcV, " intersection -  plate_centre std::vector ");
      std::cout << "rotation Axis " << rotationAxis << std::endl;
      std::cout << " plate normal at intersection point " << inters_normal << std::endl;
    }
    //---------- Reflect in plate
    ALIdouble cosang = -(inters_normal * lightray.direction()) / inters_normal.mag() / lightray.direction().mag();
    CLHEP::Hep3Vector lrold = lightray.direction();
    lightray.setDirection(lightray.direction() + inters_normal * 2 * cosang);
  
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Reflected in mirror");
    }
  }
  
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //@@ Fast simulation of Reflection in Mirror
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::fastDeviatesLightRay(LightRay& lightray) {
    if (ALIUtils::debug >= 2)
      std::cout << "LR: FAST REFLECTION IN MIRROR " << name() << std::endl;
  
    //---------- Get forward plate
    ALIPlane plate = getPlate(true, false);
  
    //---------- Reflect in plate (including intersection with it)
    lightray.reflect(plate);
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Reflected in plate");
    }
    //---------- Deviate Lightray
    //  ALIdouble deviX = findExtraEntryValue("deviX");
    // ALIdouble deviY = findExtraEntryValue("deviY");
    //  lightray.shiftAndDeviateWhileTraversing( this, 0., 0., 0., deviX, deviY, 0.);
    lightray.shiftAndDeviateWhileTraversing(this, 'R');
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Deviated ");
    }
  }
  
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::detailedTraversesLightRay(LightRay& lightray) {
    if (ALIUtils::debug >= 2)
      std::cout << "LR: DETAILED TRAVERSE IN MIRROR " << name() << std::endl;
  
    //---------- Get forward plate
    ALIPlane plate = getPlate(true, true);
    //---------- If width is 0, just keep the same point
    ALIdouble width = findExtraEntryValue("width");
    if (width == 0) {
      if (ALIUtils::debug >= 3)
        lightray.dumpData("Traversed with 0 width");
      return;
    }
  
    //---------- Refract while entering mirror
    ALIdouble refra_ind1 = 1.;
    ALIdouble refra_ind2 = findExtraEntryValue("refra_ind");
    lightray.refract(plate, refra_ind1, refra_ind2);
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Refracted in first plate");
    }
  
    //---------- Get backward plate
    plate = getPlate(false, true);
    //---------- Refract while exiting mirror
    lightray.refract(plate, refra_ind2, refra_ind1);
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Refracted in first plate");
    }
  }
  
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::fastTraversesLightRay(LightRay& lightray) {
    if (ALIUtils::debug >= 2)
      std::cout << "LR: TRAVERSE MIRROR  " << name() << std::endl;
  
    //---------- Get backward plate
    ALIPlane plate = getPlate(false, false);
    lightray.intersect(plate);
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Intersected with plate");
    }
    //---------- Shift and Deviate
    lightray.shiftAndDeviateWhileTraversing(this, 'T');
    if (ALIUtils::debug >= 2) {
      lightray.dumpData("Shifted and Deviated");
    }
  }
  
  #ifdef COCOA_VIS
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::fillIguana() {
    ALIdouble width;
    ALIbool wexists = findExtraEntryValueIfExists("width", width);
    if (!wexists)
      width = 1.;
    ALIdouble length;
    wexists = findExtraEntryValueIfExists("length", length);
    if (!wexists)
      length = 4.;
  
    ALIColour* col = new ALIColour(0., 0., 1., 0.);
    std::vector<ALIdouble> spar;
    spar.push_back(length);
    spar.push_back(length);
    spar.push_back(width);
    IgCocoaFileMgr::getInstance().addSolid(*this, "BOX", spar, col);
  }
  #endif
  
  //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  void OptOMirror::constructSolidShape() {
    ALIdouble go;
    GlobalOptionMgr* gomgr = GlobalOptionMgr::getInstance();
    gomgr->getGlobalOptionValue("VisScale", go);
  
    theSolidShape = new CocoaSolidShapeBox(
        "Box", go * 5. * cm / m, go * 5. * cm / m, go * 1. * cm / m);  //COCOA internal units are meters
  }
  