#include "Base/Axis/MakeScale.h"

#include "Base/Axis/Scale.h"
#include "Tests/GTestWrapper/google_test.h"
#include <memory>

TEST(PointwiseAxis, Construction)
{
    std::unique_ptr<Scale>(ListScan("length", std::vector<double>{1.0, 0.0}));
    std::unique_ptr<Scale>(ListScan("length", std::vector<double>{0.0, 1.0, 0.5}));
    EXPECT_THROW(ListScan("length", std::vector<double>{0.0, 1.0, 1.0}), std::runtime_error);
    Scale a1 = *std::unique_ptr<Scale>(ListScan("length", std::vector<double>{0.0, 1.0}));
    std::vector<double> coordinates{0.0, 1.0};
    Scale a2 = *std::unique_ptr<Scale>(ListScan("length", coordinates));
    EXPECT_TRUE(a1 == a2);
}

TEST(PointwiseAxis, BasicProperties)
{
    std::vector<double> coordinates{0.0, 1.0, 4.0, 8.0};
    Scale a1 = *std::unique_ptr<Scale>(ListScan("length", coordinates));
    EXPECT_EQ(4u, a1.size());
    EXPECT_EQ(0.0, a1.min());
    EXPECT_EQ(8.0, a1.max());
    EXPECT_EQ(0.0, a1.binCenter(0));
    EXPECT_EQ(1.0, a1.binCenter(1));
    EXPECT_EQ(4.0, a1.binCenter(2));
    EXPECT_EQ(8.0, a1.binCenter(3));
    EXPECT_EQ(a1.binCenter(0), a1.binCenter(0));
    EXPECT_EQ(a1.binCenter(1), a1.binCenter(1));
    EXPECT_EQ(a1.binCenter(2), a1.binCenter(2));
    EXPECT_EQ(a1.binCenter(3), a1.binCenter(3));
    EXPECT_THROW(a1.binCenter(4), std::exception);
    EXPECT_THROW(a1.binCenter(4), std::exception);
    EXPECT_TRUE(coordinates == a1.binCenters());
}

TEST(PointwiseAxis, FindClosestIndex)
{
    Scale v1 = *std::unique_ptr<Scale>(ListScan("name", std::vector<double>{0.0, 1.0, 4.0, 8.0}));
    EXPECT_EQ(4u, v1.size());
    EXPECT_EQ(v1.closestIndex(-1.0), 0u);
    EXPECT_EQ(v1.closestIndex(0.0), 0u);
    EXPECT_EQ(v1.closestIndex(0.25), 0u);
    EXPECT_EQ(1u, v1.closestIndex(0.5));
    EXPECT_EQ(1u, v1.closestIndex(0.6));
    EXPECT_EQ(1u, v1.closestIndex(2.49));
    EXPECT_EQ(2u, v1.closestIndex(2.5));
    EXPECT_EQ(3u, v1.closestIndex(8.0));
    EXPECT_EQ(3u, v1.closestIndex(11.0));

    Scale v2 = *std::unique_ptr<Scale>(ListScan("name", std::vector<double>{-2.0, -1.0}));
    EXPECT_EQ(2u, v2.size());
    EXPECT_EQ(v2.closestIndex(-3.0), 0u);
    EXPECT_EQ(v2.closestIndex(-2.0), 0u);
    EXPECT_EQ(1u, v2.closestIndex(-1.5));
    EXPECT_EQ(1u, v2.closestIndex(-1.0));
    EXPECT_EQ(1u, v2.closestIndex(0.0));
    EXPECT_EQ(1u, v2.closestIndex(1.0));
}

TEST(PointwiseAxis, CheckEquality)
{
    Scale b1 = *std::unique_ptr<Scale>(ListScan("axis", std::vector<double>{1.0, 2.0, 5.0}));
    Scale b2 = *std::unique_ptr<Scale>(ListScan("axis", std::vector<double>{1.0, 2.0, 5.0}));
    EXPECT_TRUE(b1 == b2);
    Scale b3 = *std::unique_ptr<Scale>(ListScan("axissss", std::vector<double>{1.0, 2.0, 5.0}));
    Scale b4 = *std::unique_ptr<Scale>(ListScan("axis", std::vector<double>{1.0, 2.0, 6.0}));
    Scale b6 = *std::unique_ptr<Scale>(ListScan("axiss", std::vector<double>{1.5, 2.0, 5.0}));
    EXPECT_FALSE(b1 == b3);
    EXPECT_FALSE(b1 == b4);
    EXPECT_FALSE(b1 == b6);
}

TEST(PointwiseAxis, CheckClone)
{
    Scale a1 = *std::unique_ptr<Scale>(ListScan("axis", std::vector<double>{1.0, 2.0, 5.0}));
    Scale* clone = a1.clone();
    EXPECT_TRUE(a1 == *clone);
}

TEST(PointwiseAxis, ClippedAxis)
{
    Scale axis =
        *std::unique_ptr<Scale>(ListScan("name", std::vector<double>{1.0, 2.0, 2.5, 2.7, 5.0}));

    Scale clip1 = axis.clipped(0.99, 5.1);
    EXPECT_TRUE(clip1 == axis);

    Scale clip2 = axis.clipped(1, 5.0);
    EXPECT_TRUE(clip2 == axis);

    Scale clip3 = axis.clipped(1.5, 2.5);
    EXPECT_TRUE(!(clip3 == axis));
    EXPECT_EQ(clip3.size(), 2u);
    EXPECT_EQ(clip3.binCenter(0), 2.0);
    EXPECT_EQ(clip3.binCenter(1), 2.5);

    // TODO axis restore EXPECT_THROW(axis.clone()->clip(1.4, 1.6), std::runtime_error);
    EXPECT_THROW(axis.clipped(5.0, 1.0), std::runtime_error);
}
