#include "Sample/Aggregate/ParticleLayout.h"
#include "Base/Const/Units.h"
#include "Param/Node/NodeUtil.h"
#include "Sample/Aggregate/Interference1DLattice.h"
#include "Sample/Aggregate/InterferenceNone.h"
#include "Sample/HardParticle/Sphere.h"
#include "Sample/Material/MaterialFactoryFuncs.h"
#include "Sample/Particle/Particle.h"
#include "Sample/Scattering/Rotations.h"
#include "Tests/GTestWrapper/google_test.h"

class ParticleLayoutTest : public ::testing::Test {
protected:
    ~ParticleLayoutTest() override = default;
};

TEST_F(ParticleLayoutTest, ParticleLayoutInitial)
{
    ParticleLayout particleDecoration;
    const auto* p_iff = NodeUtil::OnlyChildOfType<IInterference>(particleDecoration);
    auto particles = NodeUtil::ChildNodesOfType<IParticle>(particleDecoration);
    EXPECT_EQ(size_t(0), particles.size());
    EXPECT_EQ(nullptr, p_iff);
}

TEST_F(ParticleLayoutTest, ParticleLayoutAddParticle)
{
    ParticleLayout particleDecoration;

    Sphere ff(5.);

    Particle particle1(Vacuum(), ff);
    Particle particle2(Vacuum(), ff);
    Particle particle3(Vacuum(), ff);
    Particle particle4(Vacuum(), ff);

    RotationZ transform3(45. * Units::deg);
    RotationZ transform4(45. * Units::deg);
    particle3.rotate(transform3);
    particle4.rotate(transform4);

    particleDecoration.addParticle(particle1);
    particleDecoration.addParticle(particle2, 2.2);
    particleDecoration.addParticle(particle3);
    particleDecoration.addParticle(particle4, 4.2);
    auto particles = NodeUtil::ChildNodesOfType<IParticle>(particleDecoration);

    EXPECT_EQ(size_t(4), particles.size());

    const IParticle* p_particle1 = particles[0];
    EXPECT_TRUE(nullptr != p_particle1);
    EXPECT_EQ(1.0, p_particle1->abundance());

    const IParticle* p_particle2 = particles[1];
    EXPECT_TRUE(nullptr != p_particle2);
    EXPECT_EQ(2.2, p_particle2->abundance());

    const IParticle* p_particle3 = particles[2];
    EXPECT_TRUE(nullptr != p_particle3);
    EXPECT_EQ(1.0, p_particle3->abundance());

    const IParticle* p_particle4 = particles[3];
    EXPECT_TRUE(nullptr != p_particle4);
    EXPECT_EQ(4.2, p_particle4->abundance());
}

TEST_F(ParticleLayoutTest, ParticleLayoutAbundanceFraction)
{
    ParticleLayout particleDecoration;

    Sphere ff(5.);

    Particle particle1(Vacuum(), ff);
    Particle particle2(Vacuum(), ff);
    Particle particle3(Vacuum(), ff);
    Particle particle4(Vacuum(), ff);

    RotationY transform3(45. * Units::deg);
    RotationZ transform4(45. * Units::deg);
    particle3.rotate(transform3);
    particle4.rotate(transform4);

    particleDecoration.addParticle(particle1);
    particleDecoration.addParticle(particle2, 2.0);
    particleDecoration.addParticle(particle3);
    particleDecoration.addParticle(particle4, 4.0);
    EXPECT_EQ(8.0, particleDecoration.totalAbundance());
}

TEST_F(ParticleLayoutTest, ParticleLayoutClone)
{
    ParticleLayout particleDecoration;

    Sphere ff(5.);

    Particle particle1(Vacuum(), ff);
    Particle particle2(Vacuum(), ff);
    Particle particle3(Vacuum(), ff);
    Particle particle4(Vacuum(), ff);

    RotationY transform3(45. * Units::deg);
    RotationZ transform4(45. * Units::deg);
    particle3.rotate(transform3);
    particle4.rotate(transform4);

    particleDecoration.addParticle(particle1);
    particleDecoration.addParticle(particle2, 2.0);
    particleDecoration.addParticle(particle3);
    particleDecoration.addParticle(particle4, 4.0);

    Material mat5 = RefractiveMaterial("core", 0, 0);
    Particle particle5(mat5, ff);
    particleDecoration.addParticle(particle5, 0.0);

    InterferenceNone iff_none;
    particleDecoration.setInterference(iff_none);
    particleDecoration.setInterference(iff_none);
    particleDecoration.setInterference(iff_none);

    ParticleLayout* clone = particleDecoration.clone();
    auto particles = NodeUtil::ChildNodesOfType<IParticle>(*clone);

    const IParticle* p_particle1 = particles[0];
    EXPECT_TRUE(nullptr != p_particle1);
    EXPECT_EQ(1.0, p_particle1->abundance());

    const IParticle* p_particle2 = particles[1];
    EXPECT_TRUE(nullptr != p_particle2);
    EXPECT_EQ(2.0, p_particle2->abundance());

    const IParticle* p_particle3 = particles[2];
    EXPECT_TRUE(nullptr != p_particle3);
    EXPECT_EQ(1.0, p_particle3->abundance());

    const IParticle* p_particle4 = particles[3];
    EXPECT_TRUE(nullptr != p_particle4);
    EXPECT_EQ(4.0, p_particle4->abundance());

    const IParticle* p_particle5 = particles[4];
    EXPECT_TRUE(nullptr != p_particle5);
    EXPECT_EQ(0.0, p_particle5->abundance());

    const auto* p_iff = NodeUtil::OnlyChildOfType<IInterference>(*clone);

    EXPECT_TRUE(nullptr != p_iff);
    delete clone;
}

TEST_F(ParticleLayoutTest, ParticleLayoutInterference)
{
    ParticleLayout particleDecoration;

    InterferenceNone iff_none;
    particleDecoration.setInterference(iff_none);
    const auto* p_iff = NodeUtil::OnlyChildOfType<IInterference>(particleDecoration);

    EXPECT_TRUE(nullptr != p_iff);
}

TEST_F(ParticleLayoutTest, nodeChildren)
{
    ParticleLayout layout;
    std::vector<const INode*> children = layout.nodeChildren();
    EXPECT_EQ(children.size(), 0u);

    Sphere ff(5.);

    layout.addParticle(Particle(Vacuum(), ff));
    layout.setInterference(Interference1DLattice(1.0, 2.0));
    children = layout.nodeChildren();
    EXPECT_EQ(children.size(), 2u);
}
