#include "LocalPoolOwnerBase.h"

#include <catch2/catch_test_macros.hpp>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <unittest/core/CatchDefinitions.h>

TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
    LocalPoolOwnerBase* poolOwner = objectManager->
            get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
    REQUIRE(poolOwner != nullptr);
    REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
    REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
            == retval::CATCH_OK);

    SECTION("BasicTest") {
        // very basic test.
        lp_vec_t<uint16_t, 3> testVector = lp_vec_t<uint16_t, 3>(
                objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
        REQUIRE(testVector.read() == retval::CATCH_OK);
        testVector.value[0] = 5;
        testVector.value[1] = 232;
        testVector.value[2] = 32023;

        REQUIRE(testVector.commit(true) == retval::CATCH_OK);
        CHECK(testVector.isValid());

        testVector.value[0] = 0;
        testVector.value[1] = 0;
        testVector.value[2] = 0;

        CHECK(testVector.read() == retval::CATCH_OK);
        CHECK(testVector.value[0] == 5);
        CHECK(testVector.value[1] == 232);
        CHECK(testVector.value[2] == 32023);

        CHECK(testVector[0] == 5);

        /* This is invalid access, so the last value will be set instead.
        (we can't throw exceptions) */
        testVector[4] = 12;
        CHECK(testVector[2] == 12);
        CHECK(testVector.commit() == retval::CATCH_OK);

        /* Use read-only reference. */
        const lp_vec_t<uint16_t, 3>& roTestVec = testVector;
        uint16_t valueOne = roTestVec[0];
        CHECK(valueOne == 5);

        uint16_t lastVal = roTestVec[25];
        CHECK(lastVal == 12);

        size_t maxSize = testVector.getSerializedSize();
        CHECK(maxSize == 6);

        uint16_t serializedVector[3];
        uint8_t* vecPtr = reinterpret_cast<uint8_t*>(serializedVector);
        size_t serSize = 0;
        REQUIRE(testVector.serialize(&vecPtr, &serSize,
                maxSize, SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);

        CHECK(serSize == 6);
        CHECK(serializedVector[0] == 5);
        CHECK(serializedVector[1] == 232);
        CHECK(serializedVector[2] == 12);

        maxSize = 1;
        REQUIRE(testVector.serialize(&vecPtr, &serSize,
                maxSize, SerializeIF::Endianness::MACHINE) ==
                        static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));

        serializedVector[0] = 16;
        serializedVector[1] = 7832;
        serializedVector[2] = 39232;

        const uint8_t* constVecPtr = reinterpret_cast<const uint8_t*>(
                serializedVector);
        REQUIRE(testVector.deSerialize(&constVecPtr, &serSize,
                SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
        CHECK(testVector[0] == 16);
        CHECK(testVector[1] == 7832);
        CHECK(testVector[2] == 39232);

        serSize = 1;
        REQUIRE(testVector.deSerialize(&constVecPtr, &serSize,
                SerializeIF::Endianness::MACHINE) ==
                        static_cast<int>(SerializeIF::STREAM_TOO_SHORT));
    }

    SECTION("ErrorHandling") {
        /* Now try to use a local pool variable which does not exist */
        lp_vec_t<uint16_t, 3> invalidVector = lp_vec_t<uint16_t, 3>(
                objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
        REQUIRE(invalidVector.read() ==
                static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
        REQUIRE(invalidVector.commit() ==
                static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));

        /* Now try to access with wrong type */
        lp_vec_t<uint32_t, 3> invalidVector2 = lp_vec_t<uint32_t, 3>(
                objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
        REQUIRE(invalidVector2.read() ==
                static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
        REQUIRE(invalidVector2.commit() ==
                static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));

        lp_vec_t<uint16_t, 3> writeOnlyVec = lp_vec_t<uint16_t, 3>(
                objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id,
                nullptr, pool_rwm_t::VAR_WRITE);
        REQUIRE(writeOnlyVec.read() ==
                static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));

        lp_vec_t<uint16_t, 3> readOnlyVec = lp_vec_t<uint16_t, 3>(
                objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id,
                nullptr, pool_rwm_t::VAR_READ);
        REQUIRE(readOnlyVec.commit() ==
                static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
    }
    poolOwner->reset();
}