/**
 * @file LTTBGTest.cpp
 * @brief Unit tests for the StreamHub LTTB decimation.
 *
 * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and
 * the Development of Fusion Energy ('Fusion for Energy').
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence")
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl
 *
 * @warning Unless required by applicable law or agreed to in writing,
 * software distributed under the Licence is distributed on an "AS IS"
 * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the Licence permissions and limitations under the Licence.
 */

#include "LTTB.h"

#include <gtest/gtest.h>
#include <math.h>

using StreamHub::LTTBDecimate;
using MARTe::float64;
using MARTe::uint32;

TEST(LTTBGTest, TestEmptyInput) {
    float64 t[4] = {0.0};
    float64 v[4] = {0.0};
    EXPECT_EQ(0u, LTTBDecimate(t, v, 0u, t, v, 4u));
}

TEST(LTTBGTest, TestPassthroughWhenSmall) {
    float64 tIn[5]  = {0.0, 1.0, 2.0, 3.0, 4.0};
    float64 vIn[5]  = {5.0, 6.0, 7.0, 8.0, 9.0};
    float64 tOut[5], vOut[5];
    ASSERT_EQ(5u, LTTBDecimate(tIn, vIn, 5u, tOut, vOut, 10u));
    for (uint32 i = 0u; i < 5u; i++) {
        EXPECT_DOUBLE_EQ(tIn[i], tOut[i]);
        EXPECT_DOUBLE_EQ(vIn[i], vOut[i]);
    }
}

TEST(LTTBGTest, TestDecimateCountAndEndpoints) {
    const uint32 n = 1000u;
    static float64 tIn[1000], vIn[1000];
    for (uint32 i = 0u; i < n; i++) {
        tIn[i] = static_cast<float64>(i) * 0.001;
        vIn[i] = sin(tIn[i] * 50.0);
    }
    static float64 tOut[100], vOut[100];
    ASSERT_EQ(100u, LTTBDecimate(tIn, vIn, n, tOut, vOut, 100u));
    /* First and last points preserved */
    EXPECT_DOUBLE_EQ(tIn[0], tOut[0]);
    EXPECT_DOUBLE_EQ(tIn[n - 1u], tOut[99]);
    /* Output time strictly increasing */
    for (uint32 i = 1u; i < 100u; i++) {
        EXPECT_LT(tOut[i - 1u], tOut[i]);
    }
}

TEST(LTTBGTest, TestSpikePreserved) {
    const uint32 n = 500u;
    static float64 tIn[500], vIn[500];
    for (uint32 i = 0u; i < n; i++) {
        tIn[i] = static_cast<float64>(i);
        vIn[i] = 0.0;
    }
    vIn[250] = 100.0;  /* single spike in a flat signal */
    static float64 tOut[50], vOut[50];
    const uint32 nOut = LTTBDecimate(tIn, vIn, n, tOut, vOut, 50u);
    ASSERT_EQ(50u, nOut);
    bool spikeFound = false;
    for (uint32 i = 0u; i < nOut; i++) {
        if (vOut[i] == 100.0) { spikeFound = true; }
    }
    EXPECT_TRUE(spikeFound);
}

TEST(LTTBGTest, TestOutputPointsExistInInput) {
    const uint32 n = 200u;
    static float64 tIn[200], vIn[200];
    for (uint32 i = 0u; i < n; i++) {
        tIn[i] = static_cast<float64>(i);
        vIn[i] = cos(static_cast<float64>(i) * 0.3);
    }
    static float64 tOut[20], vOut[20];
    const uint32 nOut = LTTBDecimate(tIn, vIn, n, tOut, vOut, 20u);
    ASSERT_EQ(20u, nOut);
    /* Every output sample must be an actual input sample (no interpolation) */
    for (uint32 i = 0u; i < nOut; i++) {
        const uint32 idx = static_cast<uint32>(tOut[i]);
        EXPECT_DOUBLE_EQ(tIn[idx], tOut[i]);
        EXPECT_DOUBLE_EQ(vIn[idx], vOut[i]);
    }
}

TEST(LTTBGTest, TestThresholdBelowThreeCopiesAll) {
    float64 tIn[10], vIn[10];
    for (uint32 i = 0u; i < 10u; i++) {
        tIn[i] = static_cast<float64>(i);
        vIn[i] = static_cast<float64>(i);
    }
    float64 tOut[10], vOut[10];
    /* threshold < 3 → fast path copies everything */
    EXPECT_EQ(10u, LTTBDecimate(tIn, vIn, 10u, tOut, vOut, 2u));
}
