/**
 * @file SignalRingBufferGTest.cpp
 * @brief Unit tests for the StreamHub SignalRingBuffer (ReadSince/ReadRange).
 *
 * @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 "SignalRingBuffer.h"

#include <gtest/gtest.h>

using StreamHub::SignalRingBuffer;
using MARTe::float64;
using MARTe::uint32;
using MARTe::uint64;

TEST(SignalRingBufferGTest, TestAllocateZeroFails) {
    SignalRingBuffer rb;
    ASSERT_FALSE(rb.Allocate(0u));
}

TEST(SignalRingBufferGTest, TestReadLastNInOrder) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(8u));
    for (uint32 i = 0u; i < 5u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(10u + i));
    }
    float64 t[10], v[10];
    ASSERT_EQ(5u, rb.Read(t, v, 10u));
    for (uint32 i = 0u; i < 5u; i++) {
        EXPECT_DOUBLE_EQ(static_cast<float64>(i), t[i]);
        EXPECT_DOUBLE_EQ(static_cast<float64>(10u + i), v[i]);
    }
}

TEST(SignalRingBufferGTest, TestWrapOverwritesOldest) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(4u));
    for (uint32 i = 0u; i < 6u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    ASSERT_EQ(4u, rb.Count());
    EXPECT_EQ(static_cast<uint64>(6u), rb.TotalWritten());
    float64 t[4], v[4];
    ASSERT_EQ(4u, rb.Read(t, v, 4u));
    /* Oldest two (t=0,1) were overwritten */
    EXPECT_DOUBLE_EQ(2.0, t[0]);
    EXPECT_DOUBLE_EQ(5.0, t[3]);
}

TEST(SignalRingBufferGTest, TestReadRangeMiddle) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(16u));
    for (uint32 i = 0u; i < 10u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i) * 2.0);
    }
    float64 t[16], v[16];
    /* [2.5, 6.5] → t = 3,4,5,6 */
    ASSERT_EQ(4u, rb.ReadRange(2.5, 6.5, t, v, 16u));
    EXPECT_DOUBLE_EQ(3.0, t[0]);
    EXPECT_DOUBLE_EQ(6.0, t[3]);
    EXPECT_DOUBLE_EQ(12.0, v[3]);
}

TEST(SignalRingBufferGTest, TestReadRangeInclusiveBounds) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(16u));
    for (uint32 i = 0u; i < 10u; i++) {
        rb.Write(static_cast<float64>(i), 0.0);
    }
    float64 t[16], v[16];
    /* Bounds are inclusive on both sides */
    ASSERT_EQ(5u, rb.ReadRange(2.0, 6.0, t, v, 16u));
    EXPECT_DOUBLE_EQ(2.0, t[0]);
    EXPECT_DOUBLE_EQ(6.0, t[4]);
}

TEST(SignalRingBufferGTest, TestReadRangeAfterWrap) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(4u));
    for (uint32 i = 0u; i < 6u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    float64 t[4], v[4];
    /* Whole range: only retained points t=2..5 */
    ASSERT_EQ(4u, rb.ReadRange(0.0, 10.0, t, v, 4u));
    EXPECT_DOUBLE_EQ(2.0, t[0]);
    EXPECT_DOUBLE_EQ(5.0, t[3]);
    /* Narrow range crossing the physical wrap point */
    ASSERT_EQ(1u, rb.ReadRange(3.5, 4.5, t, v, 4u));
    EXPECT_DOUBLE_EQ(4.0, t[0]);
}

TEST(SignalRingBufferGTest, TestReadRangeInvalid) {
    SignalRingBuffer rb;
    float64 t[4], v[4];
    /* Unallocated */
    EXPECT_EQ(0u, rb.ReadRange(0.0, 1.0, t, v, 4u));
    ASSERT_TRUE(rb.Allocate(4u));
    /* Empty */
    EXPECT_EQ(0u, rb.ReadRange(0.0, 1.0, t, v, 4u));
    rb.Write(1.0, 1.0);
    /* Inverted range */
    EXPECT_EQ(0u, rb.ReadRange(2.0, 1.0, t, v, 4u));
    /* Disjoint range */
    EXPECT_EQ(0u, rb.ReadRange(5.0, 9.0, t, v, 4u));
}

TEST(SignalRingBufferGTest, TestReadRangeMaxOutCap) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(16u));
    for (uint32 i = 0u; i < 10u; i++) {
        rb.Write(static_cast<float64>(i), 0.0);
    }
    float64 t[3], v[3];
    ASSERT_EQ(3u, rb.ReadRange(0.0, 9.0, t, v, 3u));
    EXPECT_DOUBLE_EQ(0.0, t[0]);
    EXPECT_DOUBLE_EQ(2.0, t[2]);
}

TEST(SignalRingBufferGTest, TestReadSinceBasic) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(8u));
    uint64 cursor = 0u;
    float64 t[8], v[8];

    for (uint32 i = 0u; i < 3u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    ASSERT_EQ(3u, rb.ReadSince(cursor, t, v, 8u));
    EXPECT_EQ(static_cast<uint64>(3u), cursor);
    EXPECT_DOUBLE_EQ(0.0, t[0]);
    EXPECT_DOUBLE_EQ(2.0, t[2]);

    /* Nothing new */
    EXPECT_EQ(0u, rb.ReadSince(cursor, t, v, 8u));

    /* Two more points: only the new ones are returned */
    rb.Write(3.0, 3.0);
    rb.Write(4.0, 4.0);
    ASSERT_EQ(2u, rb.ReadSince(cursor, t, v, 8u));
    EXPECT_DOUBLE_EQ(3.0, t[0]);
    EXPECT_DOUBLE_EQ(4.0, t[1]);
    EXPECT_EQ(static_cast<uint64>(5u), cursor);
}

TEST(SignalRingBufferGTest, TestReadSinceOverrunClamps) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(4u));
    uint64 cursor = 0u;
    float64 t[8], v[8];

    for (uint32 i = 0u; i < 10u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    /* 10 written, only 4 retained: clamp to oldest available (t=6..9) */
    ASSERT_EQ(4u, rb.ReadSince(cursor, t, v, 8u));
    EXPECT_DOUBLE_EQ(6.0, t[0]);
    EXPECT_DOUBLE_EQ(9.0, t[3]);
    EXPECT_EQ(static_cast<uint64>(10u), cursor);
}

TEST(SignalRingBufferGTest, TestReadSinceMaxOutChunks) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(8u));
    uint64 cursor = 0u;
    float64 t[8], v[8];

    for (uint32 i = 0u; i < 5u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    /* Consume in chunks of 2: cursor must advance only by what was read */
    ASSERT_EQ(2u, rb.ReadSince(cursor, t, v, 2u));
    EXPECT_DOUBLE_EQ(0.0, t[0]);
    EXPECT_DOUBLE_EQ(1.0, t[1]);
    ASSERT_EQ(2u, rb.ReadSince(cursor, t, v, 2u));
    EXPECT_DOUBLE_EQ(2.0, t[0]);
    EXPECT_DOUBLE_EQ(3.0, t[1]);
    ASSERT_EQ(1u, rb.ReadSince(cursor, t, v, 2u));
    EXPECT_DOUBLE_EQ(4.0, t[0]);
    EXPECT_EQ(static_cast<uint64>(5u), cursor);
}

TEST(SignalRingBufferGTest, TestReadSinceAfterReallocate) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(8u));
    uint64 cursor = 0u;
    float64 t[8], v[8];

    for (uint32 i = 0u; i < 5u; i++) {
        rb.Write(static_cast<float64>(i), static_cast<float64>(i));
    }
    ASSERT_EQ(5u, rb.ReadSince(cursor, t, v, 8u));

    /* Re-Allocate resets totalWritten: a stale cursor must not underflow */
    ASSERT_TRUE(rb.Allocate(8u));
    EXPECT_EQ(0u, rb.ReadSince(cursor, t, v, 8u));
    EXPECT_EQ(static_cast<uint64>(0u), cursor);

    rb.Write(100.0, 1.0);
    ASSERT_EQ(1u, rb.ReadSince(cursor, t, v, 8u));
    EXPECT_DOUBLE_EQ(100.0, t[0]);
}

TEST(SignalRingBufferGTest, TestClear) {
    SignalRingBuffer rb;
    ASSERT_TRUE(rb.Allocate(8u));
    rb.Write(1.0, 1.0);
    rb.Write(2.0, 2.0);
    rb.Clear();
    EXPECT_EQ(0u, rb.Count());
    float64 t[8], v[8];
    EXPECT_EQ(0u, rb.Read(t, v, 8u));
}
