/*
 * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
 *
 * This software is distributed under the terms of the GNU General Public
 * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
 * the license.
 */

#pragma once

#include <corsika/geometry/Line.h>
#include <corsika/geometry/Plane.h>
#include <corsika/geometry/Sphere.h>
#include <corsika/geometry/Trajectory.h>
#include <corsika/geometry/Vector.h>
#include <corsika/particles/ParticleProperties.h>
#include <corsika/units/PhysicalUnits.h>
#include <corsika/utl/quartic.h>
#include <optional>
#include <type_traits>
#include <utility>

#include <fstream>
#include <iostream>
#include <boost/histogram.hpp>
#include <boost/histogram/ostream.hpp>
#include <corsika/process/tracking_line/dump_bh.hpp>

namespace corsika::environment {
  template <typename IEnvironmentModel>
  class Environment;
  template <typename IEnvironmentModel>
  class VolumeTreeNode;
} // namespace corsika::environment

using namespace boost::histogram;
static auto histL = make_histogram(axis::regular<>(100, 0, 100000, "Leap-Frog-ength L'"));
static auto histS = make_histogram(axis::regular<>(100, 0, 100000, "Direct Length S"));
static auto histB = make_histogram(axis::regular<>(100, 0, 100000, "Arc Length B"));
static auto histLlog = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "Leap-Frog-ength L'"));
static auto histLloggeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "Leap-Frog-ength L for geometric steps"));
static auto histLlogmag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "Leap-Frog-ength L for magnetic steps"));
static auto histSlog = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "Direct Length S"));
static auto histBlog = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "Arc Length B"));
static auto histLB = make_histogram(axis::regular<>(100, 0, 100, "L - B"));
static auto histLS = make_histogram(axis::regular<>(100, 0, 100, "L - S"));
static auto histLBrelgeo = make_histogram(axis::regular<double, axis::transform::log> (40,1e-12,1e-2,"L/B -1"));
static auto histLBrelmag = make_histogram(axis::regular<double, axis::transform::log> (40,1e-12,1e-2,"L/B -1"));
static auto histLSrelgeo = make_histogram(axis::regular<double, axis::transform::log>(40,1e-12,1e-2, "L/S -1"));
static auto histLSrelmag = make_histogram(axis::regular<double, axis::transform::log>(40,1e-12,1e-2, "L/S -1"));
static auto histELSrel = make_histogram(axis::regular<double, axis::transform::log>(50,1e-8,1e-3, "L/S -1"),axis::regular<double, axis::transform::log>(30, 3, 3e1, "E / GeV"));
static auto histELSrelp = make_histogram(axis::regular<double, axis::transform::log>(50,1e-8,1e-3, "L/S -1"),axis::regular<double, axis::transform::log>(30, 3, 3e1, "E / GeV"));
static auto histELSrelpi = make_histogram(axis::regular<double, axis::transform::log>(50,1e-8,1e-3, "L/S -1"),axis::regular<double, axis::transform::log>(30, 3, 3e1, "E / GeV"));
static auto histELSrelmu = make_histogram(axis::regular<double, axis::transform::log>(50,1e-8,1e-3, "L/S -1"),axis::regular<double, axis::transform::log>(30, 3, 3e1, "E / GeV"));
static auto histELSrele = make_histogram(axis::regular<double, axis::transform::log>(50,1e-8,1e-3, "L/S -1"),axis::regular<double, axis::transform::log>(30, 3, 3e1, "E / GeV"));
static auto histBS = make_histogram(axis::regular<>(100, 0, 0.01, "B - S"));
static auto histLpgeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Protonen"));
static auto histLpigeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Pionen"));
static auto histLmugeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Myonen"));
static auto histLegeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Elektronen"));
static auto histLygeo = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Photonen"));
static auto histLpmag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Protonen"));
static auto histLpimag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Pionen"));
static auto histLmumag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Myonen"));
static auto histLemag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Elektronen"));
static auto histLymag = make_histogram(axis::regular<double, axis::transform::log>(100, 1, 1e7, "L' f�r Photonen"));
static double L = 0;
static std::vector<double> Lsmall;
static std::vector<double> Bbig;
static std::vector<double> Sbig;
static std::vector<double> Lsmallmag;
static std::vector<double> Bbigmag;
static std::vector<double> Sbigmag;
static std::vector<double> Emag;
static std::vector<double> E;
static std::vector<double> Steplimitmag;


namespace corsika::process {

  namespace tracking_line {
  
    std::optional<std::pair<corsika::units::si::TimeType, corsika::units::si::TimeType>>
    TimeOfIntersection(geometry::Line const&, geometry::Sphere const&);

    corsika::units::si::TimeType TimeOfIntersection(geometry::Line const&,
                                                    geometry::Plane const&);
                                                  
    class TrackingLine {

    public:
      TrackingLine() = default;
      ~TrackingLine(){
		  std::ofstream myfile;
          myfile.open ("histograms.txt");
          myfile << histLlog << std::endl;
          myfile << histBlog << std::endl;
          myfile << histLB << std::endl;
          myfile << histLS << std::endl;
          myfile.close(); 
          
          std::ofstream myfile2;
          myfile2.open ("lengen.txt");
          myfile2 << "L " << std::endl;
          for(double n : Lsmall) {
            myfile2 << n << '\n';
          }
          myfile2 << "B " << std::endl;
          for(double n : Bbig) {
            myfile2 << n << '\n';
          }
          myfile2 << "S " << std::endl;
          for(double n : Sbig) {
            myfile2 << n << '\n';
          }
          myfile2 << "E in GeV" << std::endl;
          for(double n : E) {
            myfile2 << n << '\n';
          }
          myfile2 << "L mag" << std::endl;
          for(double n : Lsmallmag) {
            myfile2 << n << '\n';
          }
          myfile2 << "B mag" << std::endl;
          for(double n : Bbigmag) {
            myfile2 << n << '\n';
          }
          myfile2 << "S mag" << std::endl;
          for(double n : Sbigmag) {
            myfile2 << n << '\n';
          }
          myfile2 << "E mag in GeV" << std::endl;
          for(double n : Emag) {
            myfile2 << n << '\n';
          }
          myfile2 << "Schrittl�nge" << std::endl;
          for(double n : Steplimitmag) {
            myfile2 << n << '\n';
          }
          myfile2.close();
		  
          std::ofstream file1("histL.json");
          dump_bh(file1, histL);
          file1.close();
          std::ofstream file2("histS.json");
          dump_bh(file2, histS);
          file2.close();
          std::ofstream file3("histB.json");
          dump_bh(file3, histB);
          file3.close();
          std::ofstream file4("histLB.json");
          dump_bh(file4, histLB);
          file4.close();
          std::ofstream file5("histLS.json");
          dump_bh(file5, histLS);
          file5.close();
          std::ofstream file6("histBS.json");
          dump_bh(file6, histBS);
          file6.close();
          std::ofstream file7("histLBrelgeo.json");
          dump_bh(file7, histLBrelgeo);
          file7.close();
          std::ofstream file8("histLSrelgeo.json");
          dump_bh(file8, histLSrelgeo);
          file8.close();
          std::ofstream file9("histLBrelmag.json");
          dump_bh(file9, histLBrelmag);
          file9.close();
          std::ofstream file0("histLSrelmag.json");
          dump_bh(file0, histLSrelmag);
          file0.close();
          
          std::ofstream file10("histELSrel.json");
          dump_bh(file10, histELSrel);
          file10.close();
          std::ofstream file11("histLmugeo.json");
          dump_bh(file11, histLmugeo);
          file11.close();
          std::ofstream file12("histLpigeo.json");
          dump_bh(file12, histLpigeo);
          file12.close();
          std::ofstream file13("histLpgeo.json");
          dump_bh(file13, histLpgeo);
          file13.close();
          std::ofstream file14("histLlog.json");
          dump_bh(file14, histLlog);
          file14.close();
          std::ofstream file15("histBlog.json");
          dump_bh(file15, histBlog);
          file15.close();
          std::ofstream file16("histSlog.json");
          dump_bh(file16, histSlog);
          file16.close();
          std::ofstream file17("histELSrelmu.json");
          dump_bh(file17, histELSrelmu);
          file17.close();
          std::ofstream file18("histELSrelp.json");
          dump_bh(file18, histELSrelp);
          file18.close();
          std::ofstream file19("histELSrelpi.json");
          dump_bh(file19, histELSrelpi);
          file19.close();
          
          std::ofstream file21("histLgeo.json");
          dump_bh(file21, histLloggeo);
          file21.close();
          std::ofstream file22("histLmag.json");
          dump_bh(file22, histLlogmag);
          file22.close();
          std::ofstream file23("histLmumag.json");
          dump_bh(file23, histLmumag);
          file23.close();
          std::ofstream file24("histLpimag.json");
          dump_bh(file24, histLpimag);
          file24.close();
          std::ofstream file25("histLpmag.json");
          dump_bh(file25, histLpmag);
          file25.close();
          std::ofstream file26("histLemag.json");
          dump_bh(file26, histLemag);
          file26.close();
          std::ofstream file27("histLegeo.json");
          dump_bh(file27, histLegeo);
          file27.close();
          std::ofstream file28("histELSrele.json");
          dump_bh(file28, histELSrele);
          file28.close();
          std::ofstream file29("histLymag.json");
          dump_bh(file29, histLymag);
          file29.close();
          std::ofstream file20("histLygeo.json");
          dump_bh(file20, histLygeo);
          file20.close();
		  
		  };

      template <typename Particle> // was Stack previously, and argument was
                                   // Stack::StackIterator
      auto GetTrack(Particle const& p) {
        using namespace corsika::units::si;
        using namespace corsika::geometry;
        geometry::Vector<SpeedType::dimension_type> velocity =
            p.GetMomentum() / p.GetEnergy() * corsika::units::constants::c;

        auto const currentPosition = p.GetPosition();
        std::cout << "TrackingLine pid: " << p.GetPID()
                  << " , E = " << p.GetEnergy() / 1_GeV << " GeV" << std::endl;
        std::cout << "TrackingLine pos: "
                  << currentPosition.GetCoordinates()
                  // << " [" << p.GetNode()->GetModelProperties().GetName() << "]"
                  << std::endl;
        std::cout << "TrackingLine   E: " << p.GetEnergy() / 1_GeV << " GeV" << std::endl;
        std::cout << "TrackingLine   p: " << p.GetMomentum().GetComponents() / 1_GeV
                  << " GeV " << std::endl;
        std::cout << "TrackingLine   v: " << velocity.GetComponents() << std::endl;
        
        geometry::Line line(currentPosition, velocity);
        
        auto const* currentLogicalVolumeNode = p.GetNode();
        //~ auto const* currentNumericalVolumeNode =
        //~ fEnvironment.GetUniverse()->GetContainingNode(currentPosition);
        auto const numericallyInside =
            currentLogicalVolumeNode->GetVolume().Contains(currentPosition);

        std::cout << "numericallyInside = " << (numericallyInside ? "true" : "false") << std::endl;

        auto const& children = currentLogicalVolumeNode->GetChildNodes();
        auto const& excluded = currentLogicalVolumeNode->GetExcludedNodes();

        std::vector<std::pair<TimeType, decltype(p.GetNode())>> intersections;
        
        //charge of the particle
        int chargeNumber = 0;
        if (corsika::particles::IsNucleus(p.GetPID())) {
        	chargeNumber = p.GetNuclearZ();
        } else {
        	chargeNumber = corsika::particles::GetChargeNumber(p.GetPID());
        }
        auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(currentPosition);
   	    std::cout << " Magnetic Field: " << magneticfield.GetComponents() / 1_uT << " uT " << std::endl;
        LengthType Steplength1 = 0_m;
        LengthType Steplength2 = 0_m;
        LengthType Steplimit = 1_m / 0;
        //infinite kann man auch anders schreiben
        bool intersection = true;
        
        // for entering from outside
        auto addIfIntersects = [&](auto const& vtn) {
          auto const& volume = vtn.GetVolume();
          auto const& sphere = dynamic_cast<geometry::Sphere const&>(
              volume); // for the moment we are a bit bold here and assume
          // everything is a sphere, crashes with exception if not
          
          // creating Line with magnetic field
          if (chargeNumber != 0) {
            auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (velocity.norm() * p.GetEnergy() * 1_V);
            geometry::Vector<dimensionless_d> const directionBefore = velocity.normalized();
          	// determine steplength to next volume
            double a = ((directionBefore.cross(magneticfield)).dot(currentPosition - sphere.GetCenter()) * k + 1) * 4 / 
                      (1_m * 1_m * (directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k);
            double b = directionBefore.dot(currentPosition - sphere.GetCenter()) * 8 / 
                      ((directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k * 1_m * 1_m * 1_m);
            double c = ((currentPosition - sphere.GetCenter()).GetSquaredNorm() - 
                      (sphere.GetRadius() * sphere.GetRadius())) * 4 /
                      ((directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k * 1_m * 1_m * 1_m * 1_m);
            std::complex<double>*  solutions = solve_quartic(0, a, b, c);
            std::vector<double> tmp;
            for (int i = 0; i < 4; i++) {
              if (solutions[i].imag() == 0 && solutions[i].real() > 0) {
                tmp.push_back(solutions[i].real());
                std::cout << "Solutions for next Volume: " << solutions[i].real() << std::endl;
              }
            }
            if (tmp.size() > 0) {
              Steplength1 = 1_m * *std::min_element(tmp.begin(),tmp.end());
              std::cout << "Steplength to next volume = " << Steplength1 << std::endl;
              std::cout << "Time to next volume = " << Steplength1 / velocity.norm() << std::endl;
            } else {
              std::cout << "no intersection (1)!" << std::endl;
              intersection = false;
            }
            delete [] solutions;
          }
          
          if (intersection) {
            auto line1 = MagneticStep(p, line, Steplength1, false);
            // instead of changing the line with magnetic field, the TimeOfIntersection() could be changed
            // using line has some errors for huge steps
  
            if (auto opt = TimeOfIntersection(line1, sphere); opt.has_value()) {
              auto const [t1, t2] = *opt;
              std::cout << "intersection times: " << t1 / 1_s << "; "
                        << t2 / 1_s
                        // << " " << vtn.GetModelProperties().GetName()
                        << std::endl;
              if (t1.magnitude() > 0)
                intersections.emplace_back(t1, &vtn);
              else if (t2.magnitude() > 0)
                std::cout << "inside other volume" << std::endl;
            }
          }
        };

        for (auto const& child : children) { addIfIntersects(*child); }
        for (auto const* ex : excluded) { addIfIntersects(*ex); }

        {
          auto const& sphere = dynamic_cast<geometry::Sphere const&>(
              currentLogicalVolumeNode->GetVolume());
          // for the moment we are a bit bold here and assume
          // everything is a sphere, crashes with exception if not
          
          // creating Line with magnetic field
          if (chargeNumber != 0) {
            auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (velocity.norm() * p.GetEnergy() * 1_V);
            geometry::Vector<dimensionless_d> const directionBefore = velocity.normalized();
          	// determine steplength to next volume
            double a = ((directionBefore.cross(magneticfield)).dot(currentPosition - sphere.GetCenter()) * k + 1) * 4 / 
                      (1_m * 1_m * (directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k);
            double b = directionBefore.dot(currentPosition - sphere.GetCenter()) * 8 / 
                      ((directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k * 1_m * 1_m * 1_m);
            double c = ((currentPosition - sphere.GetCenter()).GetSquaredNorm() - 
                      (sphere.GetRadius() * sphere.GetRadius())) * 4 /
                      ((directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k * 1_m * 1_m * 1_m * 1_m);
            std::complex<double>*  solutions = solve_quartic(0, a, b, c);
            std::vector<double> tmp;
            for (int i = 0; i < 4; i++) {
              if (solutions[i].imag() == 0 && solutions[i].real() > 0) {
                tmp.push_back(solutions[i].real());
                std::cout << "Solutions for current Volume: " << solutions[i].real() << std::endl;
              }
            }
            if (tmp.size() > 0) {
			  Steplength2 = 1_m * *std::min_element(tmp.begin(),tmp.end());
			  if (numericallyInside == false) {
			    int p = std::min_element(tmp.begin(),tmp.end()) - tmp.begin();
				tmp.erase(tmp.begin() + p);
				Steplength2 = 1_m * *std::min_element(tmp.begin(),tmp.end());
			  }
			  std::cout << "steplength out of current volume = " << Steplength2 << std::endl;
              std::cout << "Time out of current volume = " << Steplength2 / velocity.norm() << std::endl;
            } else {
              std::cout << "no intersection (2)!" << std::endl;
              // what to do when this happens? (very unlikely)
            }
            delete [] solutions;
            
            geometry::Vector<SpeedType::dimension_type> const velocityVerticalMag = velocity -
            velocity.parallelProjectionOnto(magneticfield);
            LengthType const gyroradius = p.GetEnergy() * velocityVerticalMag.GetNorm() * 1_V / 
					                            (corsika::units::constants::cSquared * abs(chargeNumber) * 
					                            magneticfield.GetNorm() * 1_eV);
            Steplimit = 2 * sin(0.1) * gyroradius;
            std::cout << "Steplimit: " << Steplimit << std::endl;
          }
		
          auto line2 = MagneticStep(p, line, Steplength2, false);
          // instead of changing the line with magnetic field, the TimeOfIntersection() could be changed
          // using line has some errors for huge steps
          
          [[maybe_unused]] auto const [t1, t2] = *TimeOfIntersection(line2, sphere);
          [[maybe_unused]] auto dummy_t1 = t1;
          intersections.emplace_back(t2, currentLogicalVolumeNode->GetParent());
        }

        auto const minIter = std::min_element(
            intersections.cbegin(), intersections.cend(),
            [](auto const& a, auto const& b) { return a.first < b.first; });

        TimeType min;

        if (minIter == intersections.cend()) {
          min = 1_s; // todo: do sth. more reasonable as soon as tracking is able
          // to handle the numerics properly
          throw std::runtime_error("no intersection with anything!");
        } else {
          min = minIter->first;
        }

        std::cout << " t-intersect: "
                  << min
                  // << " " << minIter->second->GetModelProperties().GetName()
                  << std::endl;
        
        //if the used Steplengths are too big, min gets false and we do another Step
        std::cout << Steplength1 << Steplength2 << std::endl;
        std::cout << intersection << " " << Steplimit << std::endl;
        if ((Steplength1 > Steplimit || Steplength1 == 0_m) && Steplength2 > Steplimit) {
          min = Steplimit / velocity.norm();
          auto lineWithB = MagneticStep(p, line, Steplimit, true);
          
 			histL(L);
            histLlog(L);
            histLlogmag(L);
            histS(lineWithB.ArcLength(0_s,min) / 1_m);
            histSlog(lineWithB.ArcLength(0_s,min) / 1_m);
            histLS(L-lineWithB.ArcLength(0_s,min) / 1_m);
                        
            if(chargeNumber != 0) {
              if(L * 1_m/lineWithB.ArcLength(0_s,min) -1 > 0) {
                histLSrelmag(L * 1_m/lineWithB.ArcLength(0_s,min) -1);
                //histELSrel(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
              } else {
Lsmallmag.push_back(L); Sbigmag.push_back(lineWithB.ArcLength(0_s,min) / 1_m);
Emag.push_back(p.GetEnergy() / 1_GeV);Steplimitmag.push_back(Steplimit / 1_m);
              }
              auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(currentPosition);
              geometry::Vector<SpeedType::dimension_type> const velocityVerticalMag = velocity -
                   velocity.parallelProjectionOnto(magneticfield);
              LengthType const gyroradius = p.GetEnergy() * velocityVerticalMag.GetNorm() * 1_V / 
                                            (corsika::units::constants::cSquared * abs(chargeNumber) * 
                                            magneticfield.GetNorm() * 1_eV);
              std::cout << "Schrittlaenge " << velocity.norm() * min << " gyroradius " << gyroradius << std::endl;
              LengthType B = 2 * gyroradius * asin ( lineWithB.ArcLength(0_s,min) / 2 / gyroradius);
              std::cout << "Bogenlaenge" << B << std::endl;
              histB(B / 1_m);
              histBlog(B / 1_m);
              if(L-B/1_m > 0) {
                histLB(L-B/1_m);
                histBS(B/1_m - lineWithB.ArcLength(0_s,min) / 1_m);
                histLBrelmag(L * 1_m/B-1);
              } else {
Bbigmag.push_back(B / 1_m);
              }
            } else {
            histB(lineWithB.ArcLength(0_s,min) / 1_m);
            histBlog(lineWithB.ArcLength(0_s,min) / 1_m);
            }
            
            int pdg = static_cast<int>(particles::GetPDG(p.GetPID()));
            if (abs(pdg) == 13)
              histLmumag(L);
            if (abs(pdg) == 211 || abs(pdg) == 111)
              histLpimag(L);
            if (abs(pdg) == 2212 || abs(pdg) == 2112)
              histLpmag(L);
            if (abs(pdg) == 11)
              histLemag(L);
            if (abs(pdg) == 22)
              histLymag(L);
          
          return std::make_tuple(geometry::Trajectory<geometry::Line>(line, min),
                               geometry::Trajectory<geometry::Line>(lineWithB, min),
                               Steplimit * 1.0001, Steplimit, minIter->second);
        }
        
        auto lineWithB = MagneticStep(p, line, velocity.norm() * min, true);
        
			histL(L);
            histLlog(L);
            histLloggeo(L);
            histS(lineWithB.ArcLength(0_s,min) / 1_m);
            histSlog(lineWithB.ArcLength(0_s,min) / 1_m);
            histLS(L-lineWithB.ArcLength(0_s,min) / 1_m);
                        
            if(chargeNumber != 0) {
              if(L * 1_m/lineWithB.ArcLength(0_s,min) -1 > 0) {
                histLSrelgeo(L * 1_m/lineWithB.ArcLength(0_s,min) -1);
                histELSrel(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
              } else {
Lsmall.push_back(L); Sbig.push_back(lineWithB.ArcLength(0_s,min) / 1_m);
E.push_back(p.GetEnergy() / 1_GeV);
              }
              auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(currentPosition);
              geometry::Vector<SpeedType::dimension_type> const velocityVerticalMag = velocity -
                   velocity.parallelProjectionOnto(magneticfield);
              LengthType const gyroradius = p.GetEnergy() * velocityVerticalMag.GetNorm() * 1_V / 
                                            (corsika::units::constants::cSquared * abs(chargeNumber) * 
                                            magneticfield.GetNorm() * 1_eV);
              std::cout << "Schrittlaenge " << velocity.norm() * min << " gyroradius " << gyroradius << std::endl;
              LengthType B = 2 * gyroradius * asin ( lineWithB.ArcLength(0_s,min) / 2 / gyroradius);
              std::cout << "Bogenlaenge" << B << std::endl;
              histB(B / 1_m);
              histBlog(B / 1_m);
              if(L-B/1_m > 0) {
                histLB(L-B/1_m);
                histBS(B/1_m - lineWithB.ArcLength(0_s,min) / 1_m);
                histLBrelgeo(L * 1_m/B-1);
              } else {
Bbig.push_back(B / 1_m);
              }
            } else {
            histB(lineWithB.ArcLength(0_s,min) / 1_m);
            histBlog(lineWithB.ArcLength(0_s,min) / 1_m);
            }
            
            int pdg = static_cast<int>(particles::GetPDG(p.GetPID()));
            if (abs(pdg) == 13)  {
              histLmugeo(L);
              histELSrelmu(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
            }
            if (abs(pdg) == 211 || abs(pdg) == 111) {
              histLpigeo(L);
              if (chargeNumber != 0) 
                histELSrelpi(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
            }
            if (abs(pdg) == 2212 || abs(pdg) == 2112){
              histLpgeo(L);
              if (chargeNumber != 0) 
                histELSrelp(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
            }
            if (abs(pdg) == 11){
              histLegeo(L);
              if (chargeNumber != 0) 
                histELSrele(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV);
            }
            if (abs(pdg) == 22)
              histLymag(L);

        
        return std::make_tuple(geometry::Trajectory<geometry::Line>(line, min),
                               geometry::Trajectory<geometry::Line>(lineWithB, min),
                               velocity.norm() * min, Steplimit, minIter->second);
      }
      
      template <typename Particle> // was Stack previously, and argument was
                                   // Stack::StackIterator
                                   
      // determine direction of the particle after adding magnetic field
      corsika::geometry::Line MagneticStep(
      Particle const& p, corsika::geometry::Line line, corsika::units::si::LengthType Steplength, bool i) {
        using namespace corsika::units::si;
      
        if (line.GetV0().norm() * 1_s == 0_m) 
          return line;
        
        //charge of the particle
        int chargeNumber;
        if (corsika::particles::IsNucleus(p.GetPID())) {
        	chargeNumber = p.GetNuclearZ();
        } else {
        	chargeNumber = corsika::particles::GetChargeNumber(p.GetPID());
        }
        
        auto const* currentLogicalVolumeNode = p.GetNode();
        auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(p.GetPosition());
        auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (line.GetV0().norm() * p.GetEnergy() * 1_V);
        geometry::Vector<dimensionless_d> direction = line.GetV0().normalized();
        auto position = p.GetPosition();
          
        // First Movement
        // assuming magnetic field does not change during movement
        position = position + direction * Steplength / 2;
        // Change of direction by magnetic field
        direction = direction + direction.cross(magneticfield) * Steplength * k;
        // Second Movement
        position = position + direction * Steplength / 2;
        
        if(i == true) {
		      //if(chargeNumber != 0) {
              L = direction.norm() * Steplength / 2_m + Steplength / 2_m;
          //}
        }
          
        geometry::Vector<LengthType::dimension_type> const distance = position - p.GetPosition();
        if(distance.norm() == 0_m) {
          return line;
        } 
        geometry::Line newLine(p.GetPosition(), distance.normalized() * line.GetV0().norm());
        return newLine;
      }
      
      template <typename Particle> // was Stack previously, and argument was
                                   // Stack::StackIterator
                                   
      // determine direction of the particle after adding magnetic field
      auto MagneticStep(Particle const& p, corsika::units::si::LengthType Steplength) {
        using namespace corsika::units::si;
        
        if (p.GetMomentum().norm() == 0_GeV) {
          return std::make_tuple(p.GetPosition(), p.GetMomentum() / 1_GeV, double(0));
        }
        
        //charge of the particle
        int chargeNumber;
        if (corsika::particles::IsNucleus(p.GetPID())) {
        	chargeNumber = p.GetNuclearZ();
        } else {
        	chargeNumber = corsika::particles::GetChargeNumber(p.GetPID());
        }
        
        auto const* currentLogicalVolumeNode = p.GetNode();
        auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(p.GetPosition());
        geometry::Vector<SpeedType::dimension_type> velocity =
                  p.GetMomentum() / p.GetEnergy() * corsika::units::constants::c;
        auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (velocity.norm() * p.GetEnergy() * 1_V);
        geometry::Vector<dimensionless_d> direction = velocity.normalized();
        auto position = p.GetPosition();

        // First Movement
        // assuming magnetic field does not change during movement
        position = position + direction * Steplength / 2;
        // Change of direction by magnetic field
        direction = direction + direction.cross(magneticfield) * Steplength * k;
        // Second Movement
        position = position + direction * Steplength / 2;
        auto L2 = direction.norm() * Steplength / 2_m + Steplength / 2_m;
        return std::make_tuple(position, direction.normalized(), L2);
      }
    };

  } // namespace tracking_line

} // namespace corsika::process