/// Install the consitions and the alignment manager
ConditionsManager dd4hep::ConditionExamples::installManager(Detector& description)  {
  // Now we instantiate the conditions manager
  description.apply("DD4hep_ConditionsManagerInstaller",0,(char**)0);
  ConditionsManager manager = ConditionsManager::from(description);
  manager["PoolType"]       = "DD4hep_ConditionsLinearPool";
  manager["UserPoolType"]   = "DD4hep_ConditionsMapUserPool";
  manager["UpdatePoolType"] = "DD4hep_ConditionsLinearUpdatePool";
  manager.initialize();
  return manager;
}
/**
 *  Factory: DD4hep_AlignmentExample_align_telescope
 *
 *  \author  M.Frank
 *  \version 1.0
 *  \date    01/12/2016
 */
static int AlignmentExample_align_telescope (Detector& description, int argc, char** argv)  {
  string input, setup;
  bool arg_error = false, dump = false;

  for(int i=0; i<argc && argv[i]; ++i)  {
    if ( 0 == ::strncmp("-input",argv[i],4) )
      input = argv[++i];
    else if ( 0 == ::strncmp("-setup",argv[i],5) )
      setup = argv[++i];
    else if ( 0 == ::strncmp("-dump",argv[i],5) )
      dump = true;
    else
      arg_error = true;
  }
  if ( arg_error || input.empty() || setup.empty() )   {
    /// Help printout describing the basic command line interface
    cout <<
      "Usage: -plugin <name> -arg [-arg]                                             \n"
      "     name:   factory name     DD4hep_AlignmentExample_align_telescope         \n"
      "     -input   <string>        Geometry file                                   \n"
      "     -setup   <string>        Alignment setups (Conditions, etc)              \n"
      "     -dump                    Dump conditions user pool before and afterwards.\n"
      "\tArguments given: " << arguments(argc,argv) << endl << flush;
    ::exit(EINVAL);
  }

  // First we load the geometry
  description.fromXML(input);
  ConditionsManager manager = installManager(description);
  const void* setup_args[]  = {setup.c_str(), 0}; // Better zero-terminate

  description.apply("DD4hep_ConditionsXMLRepositoryParser",1,(char**)setup_args);
  // Now the deltas are stored in the conditions manager in the proper IOV pools
  const IOVType* iov_typ = manager.iovType("run");
  if ( 0 == iov_typ )  {
    except("ConditionsPrepare","++ Unknown IOV type supplied.");
  }
  IOV req_iov(iov_typ,1500);      // IOV goes from run 1000 ... 2000
  shared_ptr<ConditionsContent> content(new ConditionsContent());
  shared_ptr<ConditionsSlice>   slice(new ConditionsSlice(manager,content));
  ConditionsManager::Result cres = manager.prepare(req_iov,*slice);
  cond::fill_content(manager,*content,*iov_typ);

  // Collect all the delta conditions and make proper alignment conditions out of them
  map<DetElement, Delta> deltas;
  const auto coll = deltaCollector(*slice,deltas);
  auto proc = detectorProcessor(coll);
  //auto proc = detectorProcessor(deltaCollector(*slice,deltas));
  proc.process(description.world(),0,true);
  printout(INFO,"Prepare","Got a total of %ld deltas for processing alignments.",deltas.size());
  
  // ++++++++++++++++++++++++ Compute the tranformation matrices
  AlignmentsCalculator alignCalc;
  AlignmentsCalculator::Result ares = alignCalc.compute(deltas,*slice);
  printout(INFO,"Example",
           "Setup %ld/%ld conditions (S:%ld,L:%ld,C:%ld,M:%ld) (A:%ld,M:%ld) for IOV:%-12s",
           slice->conditions().size(),
           cres.total(), cres.selected, cres.loaded, cres.computed, cres.missing, 
           ares.computed, ares.missing, iov_typ->str().c_str());

  printout(INFO,"Example","=========================================================");
  printout(INFO,"Example","====  Alignment transformation BEFORE manipulation  =====");
  printout(INFO,"Example","=========================================================");
  if ( dump ) slice->pool->print("*");
  
  AlignmentsCalib calib(description,*slice);
  try  {  // These are only valid if alignments got pre-loaded!
    print_world_trafo(calib,"/world/Telescope");
    print_world_trafo(calib,"/world/Telescope/module_1");
    print_world_trafo(calib,"/world/Telescope/module_1/sensor");

    print_world_trafo(calib,"/world/Telescope/module_3");
    print_world_trafo(calib,"/world/Telescope/module_3/sensor");

    print_world_trafo(calib,"/world/Telescope/module_5");
    print_world_trafo(calib,"/world/Telescope/module_5/sensor");
    print_world_trafo(calib,"/world/Telescope/module_8/sensor");
  }
  catch(const std::exception& e)  {
    printout(ERROR,"Example","Exception: %s.", e.what());
  }
  catch(...)  {
    printout(ERROR,"Example","UNKNOWN Exception....");
  }

  /// Let's change something:
  Delta delta(Position(333.0,0,0));
  calib.set(calib.detector("/world/Telescope"),Delta(Position(55.0,0,0)));
  calib.set(calib.detector("/world/Telescope/module_1"),delta);
  calib.set("/world/Telescope/module_3",delta);
  /// Commit transaction and push deltas to the alignment conditions
  calib.commit();
  
  printout(INFO,"Example","=========================================================");
  printout(INFO,"Example","====  Alignment transformation AFTER manipulation   =====");
  printout(INFO,"Example","=========================================================");
  if ( dump ) slice->pool->print("*");

  print_world_trafo(calib,"/world/Telescope");
  print_world_trafo(calib,"/world/Telescope/module_1");
  print_world_trafo(calib,"/world/Telescope/module_1/sensor");
  print_world_trafo(calib,"/world/Telescope/module_2/sensor");

  print_world_trafo(calib,"/world/Telescope/module_3");
  print_world_trafo(calib,"/world/Telescope/module_3/sensor");
  print_world_trafo(calib,"/world/Telescope/module_4/sensor");

  print_world_trafo(calib,"/world/Telescope/module_5");
  print_world_trafo(calib,"/world/Telescope/module_5/sensor");
  print_world_trafo(calib,"/world/Telescope/module_6/sensor");
  print_world_trafo(calib,"/world/Telescope/module_7/sensor");
  print_world_trafo(calib,"/world/Telescope/module_8/sensor");

  return 1;
}
/**
 *  Factory: DD4hep_ConditionExample_manual_load
 *
 *  \author  M.Frank
 *  \version 1.0
 *  \date    01/12/2016
 */
static int condition_example (Detector& description, int argc, char** argv)  {
  string input, conditions;
  int    num_runs = 10;
  bool   arg_error = false;
  for(int i=0; i<argc && argv[i]; ++i)  {
    if ( 0 == ::strncmp("-input",argv[i],4) )
      input = argv[++i];
    else if ( 0 == ::strncmp("-conditions",argv[i],4) )
      conditions = argv[++i];
    else if ( 0 == ::strncmp("-runs",argv[i],4) )
      num_runs = ::atol(argv[++i]);
    else
      arg_error = true;
  }
  if ( arg_error || input.empty() || conditions.empty() ) help(argc,argv);

  // First we load the geometry
  description.fromXML(input);

  // Now we instantiate and initialize the conditions manager
  description.apply("DD4hep_ConditionsManagerInstaller",0,(char**)0);

  ConditionsManager manager = ConditionsManager::from(description);
  manager["PoolType"]       = "DD4hep_ConditionsLinearPool";
  manager["UserPoolType"]   = "DD4hep_ConditionsMapUserPool";
  manager["UpdatePoolType"] = "DD4hep_ConditionsLinearUpdatePool";
  manager["LoaderType"]     = "DD4hep_Conditions_root_Loader";
  manager.initialize();

  printout(ALWAYS,"Example","Load conditions data from file:%s",conditions.c_str());
  manager.loader().addSource(conditions);

  /// Create the container with the desired conditions content and an empty conditions slice
  shared_ptr<ConditionsContent> content(new ConditionsContent());
  shared_ptr<ConditionsSlice>   slice(new ConditionsSlice(manager,content));
  
  // Populate the content of required conditions:
  // We scan the DetElement hierarchy and add a couple of conditions
  // for each DetElement
  Scanner(ConditionsKeys(*content,INFO),description.world());

  // Add for each DetElement 3 derived conditions,
  // which all depend on the persistent condition "derived_data"
  // (In the real world this would be very specific derived actions)
  Scanner(ConditionsDependencyCreator(*content,DEBUG),description.world());

  // Now emulate a pseudo event loop: Our conditions are of type "run"
  const IOVType* iov_typ = manager.iovType("run");
  ConditionsManager::Result total;
  for ( int irun=0; irun < num_runs; ++irun )  {
    IOV req_iov(iov_typ,irun*10+5);
    // Select the proper set of conditions from the store (or load them if needed)
    // Attach the selected conditions to the user pool
    ConditionsManager::Result r = manager.prepare(req_iov,*slice);
    total += r;
    if ( 0 == irun )  { // First one we print...
      // We can access the conditions directly from the slice, since the slice
      // implements the ConditionsMap interface.
      Scanner(ConditionsPrinter(slice.get(),"Example"),description.world());
    }
    // Print summary
    printout(ALWAYS,"Prepare","Total %ld conditions (S:%ld,L:%ld,C:%ld,M:%ld) of IOV %s",
             r.total(), r.selected, r.loaded, r.computed, r.missing, req_iov.str().c_str());
  }  
  printout(ALWAYS,"Statistics","+=========================================================================");
  printout(ALWAYS,"Statistics","+  Accessed a total of %ld conditions (S:%6ld,L:%6ld,C:%6ld,M:%ld)",
           total.total(), total.selected, total.loaded, total.computed, total.missing);
  printout(ALWAYS,"Statistics","+=========================================================================");
  // All done.
  return 1;
}