//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/StandardSample/ParticleDistributionsBuilder.cpp
//! @brief     Implements classes of with different types of particle distributions.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Sample/StandardSample/ParticleDistributionsBuilder.h"
#include "Param/Distrib/Distributions.h"
#include "Sample/Aggregate/ParticleLayout.h"
#include "Sample/HardParticle/Cone.h"
#include "Sample/HardParticle/Cylinder.h"
#include "Sample/HardParticle/Polyhedra.h"
#include "Sample/HardParticle/Sphere.h"
#include "Sample/Multilayer/Layer.h"
#include "Sample/Multilayer/Sample.h"
#include "Sample/Particle/Particle.h"
#include "Sample/StandardSample/ReferenceMaterials.h"

Sample* ExemplarySamples::createCylindersWithSizeDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*
    const double height(5);
    const double radius(5);

    Layer vacuum_layer(refMat::Vacuum);

    ParticleLayout particle_layout;
    // preparing prototype of nano particle
    double sigma = 0.2 * radius;
    Cylinder p_ff_cylinder(radius, height);
    Particle nano_particle(refMat::Particle, p_ff_cylinder);
    // radius of nanoparticles will be sampled with gaussian probability
    int n_samples(100);
    // to get radius_min = average - 2.0*FWHM:
    double n_sigma = 2.0 * 2.0 * std::sqrt(2.0 * std::log(2.0));
    DistributionGaussian gauss(radius, sigma);
    ParameterPattern pattern;
    pattern.add("Particle").add("Cylinder").add("Radius");
    ParameterDistribution par_distr(pattern.toStdString(), gauss, static_cast<size_t>(n_samples),
                                    n_sigma);
    ParticleDistribution particle_collection(nano_particle, par_distr);
    particle_layout.addParticle(particle_collection);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    return sample;
    */
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

Sample* ExemplarySamples::createTwoTypesCylindersDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*

    double m_radius1(5);
    double m_radius2(10);
    double m_height1(5);
    double m_height2(10);
    double m_sigma1_ratio(0.2);
    double m_sigma2_ratio(0.02);

    Layer vacuum_layer(refMat::Vacuum);

    ParticleLayout particle_layout;

    // preparing nano particles prototypes for seeding layer's particle_layout
    Cylinder p_ff_cylinder1(m_radius1, m_height1);
    Particle cylinder1(refMat::Particle, p_ff_cylinder1);

    Cylinder p_ff_cylinder2(m_radius2, m_height2);
    Particle cylinder2(refMat::Particle, p_ff_cylinder2);

    // radius of nanoparticles will be sampled with gaussian probability
    int nbins = 150;
    double sigma1 = m_radius1 * m_sigma1_ratio;
    double sigma2 = m_radius2 * m_sigma2_ratio;
    // to have xmin=average-3*sigma
    double n_sigma = 3.0;
    DistributionGaussian gauss1(m_radius1, sigma1);
    DistributionGaussian gauss2(m_radius2, sigma2);

    // building distribution of nano particles
    ParameterPattern pattern1;
    pattern1.add("Particle").add("Cylinder").add("Radius");
    ParameterDistribution par_distr1(pattern1.toStdString(), gauss1, nbins, n_sigma);
    ParticleDistribution particle_collection1(cylinder1, par_distr1);
    particle_layout.addParticle(particle_collection1, 0.95);
    ParameterPattern pattern2;
    pattern2.add("Particle").add("Cylinder").add("Radius");
    ParameterDistribution par_distr2(pattern2.toStdString(), gauss2, static_cast<size_t>(nbins),
                                     n_sigma);
    ParticleDistribution particle_collection2(cylinder2, par_distr2);
    particle_layout.addParticle(particle_collection2, 0.05);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    return sample;
    */
}

// ----------------------------------------------------------------------------

Sample* ExemplarySamples::createRotatedPyramidsDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*
    double m_length(10);
    double m_height(5);
    double m_alpha(Units::deg2rad(54.73));
    double m_zangle(45. * deg);

    // particle
    Pyramid4 ff_pyramid(m_length, m_height, m_alpha);
    Particle pyramid(refMat::Particle, ff_pyramid);
    pyramid.rotate(RotationZ(m_zangle));

    // particle collection
    DistributionGate gate(35.0 * deg, 55.0 * deg);
    ParameterDistribution parameter_distr("/Particle/ZRotation/Angle", gate, 10, 2.0);

    ParticleDistribution collection(pyramid, parameter_distr);

    ParticleLayout particle_layout;
    particle_layout.addParticle(collection);

    // Multi layer
    Layer vacuum_layer(refMat::Vacuum);
    Layer substrate_layer(refMat::Substrate);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    sample->addLayer(substrate_layer);
    return sample;
    */
}

// ----------------------------------------------------------------------------

Sample* ExemplarySamples::createSpheresWithLimitsDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*
    // particle
    Sphere ff(3.0);
    Particle sphere(refMat::Particle, ff);

    // particle collection
    DistributionGaussian gauss(3.0, 1.0);
    ParameterDistribution parameter_distr("/Particle/Sphere/Radius", gauss, 10, 20.0,
                                          RealLimits::limited(2.0, 4.0));

    ParticleDistribution collection(sphere, parameter_distr);

    ParticleLayout particle_layout;
    particle_layout.addParticle(collection);

    // Multi layer
    Layer vacuum_layer(refMat::Vacuum);
    Layer substrate_layer(refMat::Substrate);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    sample->addLayer(substrate_layer);

    return sample;
    */
}

// ----------------------------------------------------------------------------

Sample* ExemplarySamples::createConesWithLimitsDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*
    // particle
    Cone ff(10.0, 13.0, 60.0 * deg);
    Particle cone(refMat::Particle, ff);

    // particle collection
    DistributionGaussian gauss(60.0 * deg, 6.0 * deg);
    ParameterDistribution parameter_distr(
        "/Particle/Cone/Alpha", gauss, 5, 20.0,
        RealLimits::limited(55.0 * deg, 65.0 * deg));

    ParticleDistribution collection(cone, parameter_distr);

    ParticleLayout particle_layout;
    particle_layout.addParticle(collection);

    // Multi layer
    Layer vacuum_layer(refMat::Vacuum);
    Layer substrate_layer(refMat::Substrate);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    sample->addLayer(substrate_layer);
    return sample;
    */
}

Sample* ExemplarySamples::createLinkedBoxDistribution()
{
    throw std::runtime_error(
        "This sample used ParticleDistribution which is not supported any more.");

    // #baRemoveParticleDistribution The following code is outdated. However, it is still kept,
    // since it may be useful once the new particle distribution approach is implemented.
    /*
    // particle
    Box ff(40.0, 30.0, 10.0);
    Particle box(refMat::Particle, ff);

    // particle collection
    DistributionGate gate(10.0, 70.0);
    ParameterDistribution parameter_distr("/Particle/Box/Length", gate, 3, 0.0,
                                          RealLimits::limited(1.0, 200.0));
    parameter_distr.linkParameter("/Particle/Box/Width").linkParameter("/Particle/Box/Height");

    ParticleDistribution collection(box, parameter_distr);

    ParticleLayout particle_layout;
    particle_layout.addParticle(collection);
    particle_layout.setTotalParticleSurfaceDensity(1e-4);

    // Multi layer
    Layer vacuum_layer(refMat::Vacuum);
    Layer substrate_layer(refMat::Substrate);

    vacuum_layer.addLayout(particle_layout);

    auto* sample = new MultiLayer;
    sample->addLayer(vacuum_layer);
    sample->addLayer(substrate_layer);
    return sample;
    */
}
