#include "Base/Axis/Bin.h"

#include "Base/Const/Units.h"
#include "Device/Mask/Ellipse.h"
#include "Device/Mask/Line.h"
#include "Device/Mask/Rectangle.h"
#include "Tests/GTestWrapper/google_test.h"
#include <memory>

TEST(Shape2D, Rectangle)
{
    Rectangle rect(-4.0, -2.0, 4.0, 2.0);
    EXPECT_DOUBLE_EQ(32.0, rect.area());
    EXPECT_TRUE(rect.contains(0.0, 0.0));
    EXPECT_TRUE(rect.contains(4.0, 2.0));
    EXPECT_TRUE(rect.contains(-4.0, -2.0));
    EXPECT_TRUE(rect.contains(-4.0, -2.0));
    EXPECT_FALSE(rect.contains(0.0, 2.01));
    EXPECT_FALSE(rect.contains(4.0, -2.01));

    Bin1D binx1 = Bin1D::FromTo(3.9, 5);
    Bin1D biny1 = Bin1D::FromTo(1.5, 2.5);
    EXPECT_TRUE(rect.contains(binx1, biny1));

    Bin1D binx2 = Bin1D::FromTo(3.5, 4.6);
    Bin1D biny2 = Bin1D::FromTo(2.0, 7);
    EXPECT_FALSE(rect.contains(binx2, biny2));

    std::unique_ptr<Rectangle> clone(rect.clone());
    EXPECT_DOUBLE_EQ(32.0, clone->area());
    EXPECT_TRUE(clone->contains(0.0, 0.0));
    EXPECT_TRUE(clone->contains(4.0, 2.0));
    EXPECT_TRUE(clone->contains(-4.0, -2.0));
    EXPECT_TRUE(clone->contains(-4.0, -2.0));
    EXPECT_FALSE(clone->contains(0.0, 2.01));
    EXPECT_FALSE(clone->contains(4.0, -2.01));
    EXPECT_TRUE(clone->contains(binx1, biny1));
    EXPECT_FALSE(clone->contains(binx2, biny2));
}

TEST(Shape2D, RectangleInverted)
{
    Rectangle rect(-4.0, -2.0, 4.0, 2.0, true);
    EXPECT_DOUBLE_EQ(32.0, rect.area());
    EXPECT_FALSE(rect.contains(0.0, 0.0));
    EXPECT_FALSE(rect.contains(4.0, 2.0));
    EXPECT_FALSE(rect.contains(-4.0, -2.0));
    EXPECT_FALSE(rect.contains(-4.0, -2.0));
    EXPECT_TRUE(rect.contains(0.0, 2.01));
    EXPECT_TRUE(rect.contains(4.0, -2.01));

    Bin1D binx1 = Bin1D::FromTo(3.5, 4.5);
    Bin1D biny1 = Bin1D::FromTo(1.5, 2.5);
    EXPECT_FALSE(rect.contains(binx1, biny1));

    Bin1D binx2 = Bin1D::FromTo(4.0, 4.6);
    Bin1D biny2 = Bin1D::FromTo(1.5, 2.6);
    EXPECT_TRUE(rect.contains(binx2, biny2));

    std::unique_ptr<Rectangle> clone(rect.clone());
    EXPECT_DOUBLE_EQ(32.0, clone->area());
    EXPECT_FALSE(clone->contains(0.0, 0.0));
    EXPECT_FALSE(clone->contains(4.0, 2.0));
    EXPECT_FALSE(clone->contains(-4.0, -2.0));
    EXPECT_FALSE(clone->contains(-4.0, -2.0));
    EXPECT_TRUE(clone->contains(0.0, 2.01));
    EXPECT_TRUE(clone->contains(4.0, -2.01));
    EXPECT_FALSE(clone->contains(binx1, biny1));
    EXPECT_TRUE(clone->contains(binx2, biny2));
}

TEST(Shape2D, Ellipse)
{
    Ellipse ellipse(10.0, 1.0, 8.0, 4.0);
    EXPECT_TRUE(ellipse.contains(10.0, 1.0));
    EXPECT_TRUE(ellipse.contains(18.0, 1.0));
    EXPECT_FALSE(ellipse.contains(18.01, 1.0));

    EXPECT_TRUE(ellipse.contains(10.0, -3.0));
    EXPECT_FALSE(ellipse.contains(10.0, -3.01));

    EXPECT_FALSE(ellipse.contains(4.0, -2.0));
    EXPECT_TRUE(ellipse.contains(6.0, -2.0));
}

TEST(Shape2D, Line)
{
    Line line(0.0, 0.0, 1.0, 0.0);
    EXPECT_TRUE(line.contains(0.0, 0.0));
    EXPECT_TRUE(line.contains(0.5, 0.0));
    EXPECT_TRUE(line.contains(1.0, 0.0));
    EXPECT_FALSE(line.contains(1.01, 0.0));

    Line line2(0.0, 0.0, 1.0, 1.0);
    EXPECT_TRUE(line2.contains(Bin1D::FromTo(0.5, 1.0), Bin1D::FromTo(0.0, 0.5)));
    EXPECT_FALSE(line2.contains(Bin1D::FromTo(0.51, 1.0), Bin1D::FromTo(0.0, 0.49)));

    std::unique_ptr<Line> clone(line2.clone());
    EXPECT_TRUE(clone->contains(Bin1D::FromTo(0.5, 1.0), Bin1D::FromTo(0.0, 0.5)));
    EXPECT_FALSE(clone->contains(Bin1D::FromTo(0.51, 1.0), Bin1D::FromTo(0.0, 0.49)));
}

TEST(Shape2D, VerticalLine)
{
    VerticalLine line(1.0);
    EXPECT_TRUE(line.contains(1.0, 0.0));
    EXPECT_FALSE(line.contains(1.01, 0.0));

    EXPECT_TRUE(line.contains(Bin1D::FromTo(0.5, 1.5), Bin1D::FromTo(0.0, 1.0)));
    EXPECT_FALSE(line.contains(Bin1D::FromTo(1.01, 2.0), Bin1D::FromTo(0.0, 1.0)));
}

TEST(Shape2D, HorizontalLine)
{
    HorizontalLine line(1.0);
    EXPECT_TRUE(line.contains(0.0, 1.0));
    EXPECT_FALSE(line.contains(0.0, 1.01));

    EXPECT_TRUE(line.contains(Bin1D::FromTo(0.0, 1.0), Bin1D::FromTo(0.5, 1.5)));
    EXPECT_FALSE(line.contains(Bin1D::FromTo(0.0, 1.0), Bin1D::FromTo(1.01, 2.0)));
}
