/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#ifndef SDV_PS_SUPPORT_INL
#define SDV_PS_SUPPORT_INL

#ifndef SDV_PS_SUPPORT_H
#include "pssup.h"
#endif //! defined SDV_PS_SUPPORT_H

#include "../interfaces/core_ps.h"
#include "../interfaces/serdes/core_ps_serdes.h"
#include "../interfaces/serdes/core_types_serdes.h"
#include "sequence.h"
#include "pointer.h"
#include "crc.h"
#include "component_impl.h"
#include <cassert>
#include <thread>
#include <condition_variable>
#include <map>
#include <functional>
#include <chrono>
#include <queue>
#include <list>

namespace serdes
{
    /**
     * @brief Specialization of serializer/deserializer class for sdv::interface_t.
     */
    template <>
    class CSerdes<sdv::interface_t>
    {
    public:
        /**
         * @brief Calculate the size of the value in serialized form.
         * @remarks Dependable on the size value, padding is added to align data.
         * @param[in] rifc Reference to the interface variable.
         * @param[in, out] rnSize Reference to the variable containing the current size and increased by the size of the value.
         */
        static void CalcSize(const sdv::interface_t& rifc, size_t& rnSize)
        {
            sdv::ser_size(rifc.id(), rnSize);
            sdv::ser_size(sdv::ps::TMarshallID(), rnSize);
        }

        /**
         * @brief Stream the variable into the serializer.
         * @tparam eTargetEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rSerializer Reference to the serializer.
         * @param[in] rifc Reference to the variable.
         * @return Reference to the serializer.
         */
        template <sdv::EEndian eTargetEndianess>
        static sdv::serializer<eTargetEndianess>& Serialize(sdv::serializer<eTargetEndianess>& rSerializer,
            const sdv::interface_t& rifc)
        {
            // Get interface to the component isolation service.
            auto ptrComControl = sdv::core::GetObject("CommunicationControl");
            if (!ptrComControl) throw sdv::ps::XMarshallNotInitialized{};
            sdv::ps::IMarshallAccess* pMarshallAccess = ptrComControl.GetInterface<sdv::ps::IMarshallAccess>();
            if (!pMarshallAccess) throw sdv::ps::XMarshallNotInitialized{};

            // Serialize the interface ID first
            rSerializer << rifc.id();

            // Create and serialize a stub object for the interface.
            sdv::ps::TMarshallID tStubID = pMarshallAccess->GetStub(rifc);
            rSerializer << tStubID;

            return rSerializer;
        }

        /**
         * @brief Stream a variable from the deserializer.
         * @tparam eSourceEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rDeserializer Reference to the deserializer.
         * @param[out] rifc Reference to the variable to be filled.
         * @return Reference to the deserializer.
         */
        template <sdv::EEndian eSourceEndianess>
        static sdv::deserializer<eSourceEndianess>& Deserialize(sdv::deserializer<eSourceEndianess>& rDeserializer,
            sdv::interface_t& rifc)
        {
            // Get interface to the component isolation service.
            auto ptrComControl = sdv::core::GetObject("CommunicationControl");
            if (!ptrComControl) throw sdv::ps::XMarshallNotInitialized{};
            sdv::ps::IMarshallAccess* pMarshallAccess = ptrComControl.GetInterface<sdv::ps::IMarshallAccess>();
            if (!pMarshallAccess) throw sdv::ps::XMarshallNotInitialized{};

            // Get the interface ID
            sdv::interface_id id = 0;
            rDeserializer >> id;

            // Get the stub ID
            sdv::ps::TMarshallID tStubID{};
            rDeserializer >> tStubID;

            // Create the proxy
            if (!tStubID && !id) // In case the ID is zero, an NULL interface was sent.
                rifc = {};
            else
                rifc = pMarshallAccess->GetProxy(tStubID, id);

            return rDeserializer;
        }
    };

    /**
    * @brief Specialization of serializer/deserializer class for sdv::any_t.
    */
    template <>
    class CSerdes<sdv::any_t>
    {
    public:
        /**
         * @brief Calculate the size of the value in serialized form.
         * @remarks Dependable on the size value, padding is added to align data.
         * @param[in] rany Reference to the variable.
         * @param[in, out] rnSize Reference to the variable containing the current size and increased by the size of the value.
         */
        static void CalcSize(const sdv::any_t& rany, size_t& rnSize)
        {
            // The size of the type
            sdv::ser_size(rany.eValType, rnSize);

            // Added with the size of the variable.
            switch (rany.eValType)
            {
            case sdv::any_t::EValType::val_type_bool:           sdv::ser_size(rany.bVal, rnSize);            break;
            case sdv::any_t::EValType::val_type_int8:           sdv::ser_size(rany.i8Val, rnSize);           break;
            case sdv::any_t::EValType::val_type_uint8:          sdv::ser_size(rany.ui8Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_int16:          sdv::ser_size(rany.i16Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_uint16:         sdv::ser_size(rany.ui16Val, rnSize);         break;
            case sdv::any_t::EValType::val_type_int32:          sdv::ser_size(rany.i32Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_uint32:         sdv::ser_size(rany.ui32Val, rnSize);         break;
            case sdv::any_t::EValType::val_type_int64:          sdv::ser_size(rany.i64Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_uint64:         sdv::ser_size(rany.ui64Val, rnSize);         break;
            case sdv::any_t::EValType::val_type_char:           sdv::ser_size(rany.cVal, rnSize);            break;
            case sdv::any_t::EValType::val_type_char16:         sdv::ser_size(rany.c16Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_char32:         sdv::ser_size(rany.c32Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_wchar:          sdv::ser_size(rany.cwVal, rnSize);           break;
            case sdv::any_t::EValType::val_type_float:          sdv::ser_size(rany.fVal, rnSize);            break;
            case sdv::any_t::EValType::val_type_double:         sdv::ser_size(rany.dVal, rnSize);            break;
            case sdv::any_t::EValType::val_type_long_double:    sdv::ser_size(rany.ldVal, rnSize);           break;
            //case sdv::any_t::EValType::val_type_fixed:          sdv::ser_size(rany.fixVal, rnSize);          break;
            case sdv::any_t::EValType::val_type_string:         sdv::ser_size(rany.ssVal, rnSize);           break;
            case sdv::any_t::EValType::val_type_u8string:       sdv::ser_size(rany.ss8Val, rnSize);          break;
            case sdv::any_t::EValType::val_type_u16string:      sdv::ser_size(rany.ss16Val, rnSize);         break;
            case sdv::any_t::EValType::val_type_u32string:      sdv::ser_size(rany.ss32Val, rnSize);         break;
            case sdv::any_t::EValType::val_type_wstring:        sdv::ser_size(rany.sswVal, rnSize);          break;
            case sdv::any_t::EValType::val_type_interface:      sdv::ser_size(rany.ifcVal, rnSize);          break;
            case sdv::any_t::EValType::val_type_interface_id:   sdv::ser_size(rany.idIfcVal, rnSize);        break;
            case sdv::any_t::EValType::val_type_exception_id:   sdv::ser_size(rany.idExceptVal, rnSize);     break;
            default:                                                                                         break;
            }
        }

        /**
         * @brief Stream the variable into the serializer.
         * @tparam eTargetEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rSerializer Reference to the serializer.
         * @param[in] rany Reference to the variable.
         * @return Reference to the serializer.
         */
        template <sdv::EEndian eTargetEndianess>
        static sdv::serializer<eTargetEndianess>& Serialize(sdv::serializer<eTargetEndianess>& rSerializer, const sdv::any_t& rany)
        {
            // Serialize the type first
            rSerializer << rany.eValType;

            // Serialize the data
            switch (rany.eValType)
            {
            case sdv::any_t::EValType::val_type_bool:           rSerializer << rany.bVal;            break;
            case sdv::any_t::EValType::val_type_int8:           rSerializer << rany.i8Val;           break;
            case sdv::any_t::EValType::val_type_uint8:          rSerializer << rany.ui8Val;          break;
            case sdv::any_t::EValType::val_type_int16:          rSerializer << rany.i16Val;          break;
            case sdv::any_t::EValType::val_type_uint16:         rSerializer << rany.ui16Val;         break;
            case sdv::any_t::EValType::val_type_int32:          rSerializer << rany.i32Val;          break;
            case sdv::any_t::EValType::val_type_uint32:         rSerializer << rany.ui32Val;         break;
            case sdv::any_t::EValType::val_type_int64:          rSerializer << rany.i64Val;          break;
            case sdv::any_t::EValType::val_type_uint64:         rSerializer << rany.ui64Val;         break;
            case sdv::any_t::EValType::val_type_char:           rSerializer << rany.cVal;            break;
            case sdv::any_t::EValType::val_type_char16:         rSerializer << rany.c16Val;          break;
            case sdv::any_t::EValType::val_type_char32:         rSerializer << rany.c32Val;          break;
            case sdv::any_t::EValType::val_type_wchar:          rSerializer << rany.cwVal;           break;
            case sdv::any_t::EValType::val_type_float:          rSerializer << rany.fVal;            break;
            case sdv::any_t::EValType::val_type_double:         rSerializer << rany.dVal;            break;
            case sdv::any_t::EValType::val_type_long_double:    rSerializer << rany.ldVal;           break;
                //case sdv::any_t::EValType::val_type_fixed:          rSerializer << rany.fixVal;          break;
            case sdv::any_t::EValType::val_type_string:         rSerializer << rany.ssVal;           break;
            case sdv::any_t::EValType::val_type_u8string:       rSerializer << rany.ss8Val;          break;
            case sdv::any_t::EValType::val_type_u16string:      rSerializer << rany.ss16Val;         break;
            case sdv::any_t::EValType::val_type_u32string:      rSerializer << rany.ss32Val;         break;
            case sdv::any_t::EValType::val_type_wstring:        rSerializer << rany.sswVal;          break;
            case sdv::any_t::EValType::val_type_interface:      rSerializer << rany.ifcVal;          break;
            case sdv::any_t::EValType::val_type_interface_id:   rSerializer << rany.idIfcVal;        break;
            case sdv::any_t::EValType::val_type_exception_id:   rSerializer << rany.idExceptVal;     break;
            default:                                                                     break;
            }

            return rSerializer;
        }

        /**
         * @brief Stream a variable from the deserializer.
         * @tparam eSourceEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rDeserializer Reference to the deserializer.
         * @param[out] rany Reference to the variable to be filled.
         * @return Reference to the deserializer.
         */
        template <sdv::EEndian eSourceEndianess>
        static sdv::deserializer<eSourceEndianess>& Deserialize(sdv::deserializer<eSourceEndianess>& rDeserializer, sdv::any_t& rany)
        {
            rany.clear();

            // Deserialize the type first
            rDeserializer >> rany.eValType;

            // Serialize the data
            switch (rany.eValType)
            {
            case sdv::any_t::EValType::val_type_bool:           rDeserializer >> rany.bVal;         break;
            case sdv::any_t::EValType::val_type_int8:           rDeserializer >> rany.i8Val;        break;
            case sdv::any_t::EValType::val_type_uint8:          rDeserializer >> rany.ui8Val;       break;
            case sdv::any_t::EValType::val_type_int16:          rDeserializer >> rany.i16Val;       break;
            case sdv::any_t::EValType::val_type_uint16:         rDeserializer >> rany.ui16Val;      break;
            case sdv::any_t::EValType::val_type_int32:          rDeserializer >> rany.i32Val;       break;
            case sdv::any_t::EValType::val_type_uint32:         rDeserializer >> rany.ui32Val;      break;
            case sdv::any_t::EValType::val_type_int64:          rDeserializer >> rany.i64Val;       break;
            case sdv::any_t::EValType::val_type_uint64:         rDeserializer >> rany.ui64Val;      break;
            case sdv::any_t::EValType::val_type_char:           rDeserializer >> rany.cVal;         break;
            case sdv::any_t::EValType::val_type_char16:         rDeserializer >> rany.c16Val;       break;
            case sdv::any_t::EValType::val_type_char32:         rDeserializer >> rany.c32Val;       break;
            case sdv::any_t::EValType::val_type_wchar:          rDeserializer >> rany.cwVal;        break;
            case sdv::any_t::EValType::val_type_float:          rDeserializer >> rany.fVal;         break;
            case sdv::any_t::EValType::val_type_double:         rDeserializer >> rany.dVal;         break;
            case sdv::any_t::EValType::val_type_long_double:    rDeserializer >> rany.ldVal;        break;
                //case sdv::any_t::EValType::val_type_fixed:        new (&rany.fixVal) sdv::fixed; rDeserializer >> rany.fixVal;       break;
            case sdv::any_t::EValType::val_type_string:         new (&rany.ssVal) sdv::string; rDeserializer >> rany.ssVal;        break;
            case sdv::any_t::EValType::val_type_u8string:       new (&rany.ss8Val) sdv::u8string; rDeserializer >> rany.ss8Val;    break;
            case sdv::any_t::EValType::val_type_u16string:      new (&rany.ss16Val) sdv::u16string; rDeserializer >> rany.ss16Val; break;
            case sdv::any_t::EValType::val_type_u32string:      new (&rany.ss32Val) sdv::u32string; rDeserializer >> rany.ss32Val; break;
            case sdv::any_t::EValType::val_type_wstring:        new (&rany.sswVal) sdv::wstring; rDeserializer >> rany.sswVal;     break;
            case sdv::any_t::EValType::val_type_interface:      new (&rany.ifcVal) sdv::interface_t; rDeserializer >> rany.ifcVal; break;
            case sdv::any_t::EValType::val_type_interface_id:   rDeserializer >> rany.idIfcVal;     break;
            case sdv::any_t::EValType::val_type_exception_id:   rDeserializer >> rany.idExceptVal;  break;
            default: rany.eValType = sdv::any_t::EValType::val_type_empty;                          break;
            }

            return rDeserializer;
        }
    };

    /**
    * @brief Specialization of serializer/deserializer class for sdv::ps::SMarshallLocal.
    */
    template <>
    class CSerdes<sdv::ps::SMarshallLocal>
    {
    public:
        /**
         * @brief Calculate the size of the value in serialized form.
         * @remarks Dependable on the size value, padding is added to align data.
         * @param[in] rsValue Reference to the variable.
         * @param[in, out] rnSize Reference to the variable containing the current size and increased by the size of the value.
         */
        static void CalcSize(const sdv::ps::SMarshallLocal& rsValue, size_t& rnSize)
        {
            sdv::ser_size(rsValue.eEndian, rnSize);
            sdv::ser_size(rsValue.uiVersion, rnSize);
            sdv::ser_size(rsValue.tIfcId, rnSize);
            sdv::ser_size(rsValue.uiFuncIndex, rnSize);
            sdv::ser_size(rsValue.uiFlags, rnSize);
            sdv::ser_size(rsValue.seqChecksums, rnSize);
            sdv::ser_size(rsValue.uiHdrChecksum, rnSize);
        }

        /**
         * @brief Stream the variable into the serializer.
         * @tparam eTargetEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rSerializer Reference to the serializer.
         * @param[in] rsValue Reference to the variable.
         * @return Reference to the serializer.
         */
        template <sdv::EEndian eTargetEndianess>
        static sdv::serializer<eTargetEndianess>& Serialize(sdv::serializer<eTargetEndianess>& rSerializer,
            sdv::ps::SMarshallLocal& rsValue)
        {
            // Stream the initial parameters.
            rSerializer << rsValue.eEndian;
            rSerializer << rsValue.uiVersion;
            rSerializer << rsValue.tIfcId;
            rSerializer << rsValue.uiFuncIndex;
            rSerializer << rsValue.uiFlags;
            rSerializer << rsValue.seqChecksums;

            // Get the currently calculated checksum and store the checksum.
            rsValue.uiHdrChecksum = rSerializer.checksum();

            // Stream the checksum.
            rSerializer << rsValue.uiHdrChecksum;

            return rSerializer;
        }

        /**
         * @brief Stream a variable from the deserializer.
         * @tparam eSourceEndianess The target endianess detemines whether to swap the bytes before storing them into the buffer.
         * @param[in] rDeserializer Reference to the deserializer.
         * @param[out] rsValue Reference to the variable to be filled.
         * @return Reference to the deserializer.
         */
        template <sdv::EEndian eSourceEndianess>
        static sdv::deserializer<eSourceEndianess>& Deserialize(sdv::deserializer<eSourceEndianess>& rDeserializer,
            sdv::ps::SMarshallLocal& rsValue)
        {
            // Stream the initial parameters.
            rDeserializer >> rsValue.eEndian;
            rDeserializer >> rsValue.uiVersion;

            // Continue only when the version is corresponding with the interface version (otherwise the order might be
            // mixed up).
            if (rsValue.uiVersion == SDVFrameworkInterfaceVersion)
            {
                rDeserializer >> rsValue.tIfcId;
                rDeserializer >> rsValue.uiFuncIndex;
                rDeserializer >> rsValue.uiFlags;
                rDeserializer >> rsValue.seqChecksums;
                rDeserializer >> rsValue.uiHdrChecksum;
            }

            return rDeserializer;
        }
    };
} // namespace serdes

namespace sdv
{
    namespace ps
    {
        inline CRawDataBypass::CRawDataBypass()
        {}

        inline void CRawDataBypass::push(const pointer<uint8_t>& rptrData)
        {
            m_queueBypassData.push(rptrData);
        }

        inline pointer<uint8_t> CRawDataBypass::pop()
        {
            pointer<uint8_t> ptrData = std::move(m_queueBypassData.front());
            m_queueBypassData.pop();
            return ptrData;
        }

        inline bool CRawDataBypass::empty() const
        {
            return m_queueBypassData.empty();
        }

        inline void CRawDataBypass::clear()
        {
            while (!m_queueBypassData.empty())
                m_queueBypassData.pop();
        }

        inline CRawDataBypass& GetRawDataBypass()
        {
#if defined __GNUC__ && defined _WIN32
            // https://dev.azure.com/SW4ZF/AZP-431_DivDI_Vehicle_API/_workitems/edit/610009
            // In MINGW GCC implementation of thread_local, there is an issue with complex classes being used after the destruction.
            // This is caused by an architectural bug (https://github.com/msys2/MINGW-packages/issues/2519) causing the emutls (GCC
            // Emulated TLS) managing the memory allocation to be executed before the __cxa_thread_exit, the thread cleanup callback
            // of Windows. The solution is to use trivial data only (pointer, reference, integers, etc.). A workaround is
            // implemented to use a reference of the permission list and map instead of an instance to the list and map.
            static auto fnCreateBypass = []() -> CRawDataBypass&
            {
                static std::mutex mtx;
                static std::list<CRawDataBypass> lstRawDataBypasses;
                std::unique_lock<std::mutex> lock(mtx);
                lstRawDataBypasses.resize(lstRawDataBypasses.size() + 1);
                return lstRawDataBypasses.back();
            };
            thread_local static CRawDataBypass& bypass = fnCreateBypass();
#else
            thread_local static CRawDataBypass bypass;
#endif
            return bypass;
        }

        template <typename TInterface>
        inline CProxyHandler<TInterface>::CProxyHandler()
        {}

        template <typename TInterface>
        inline CProxyHandler<TInterface>::~CProxyHandler()
        {}

        template <typename TInterface>
        inline void CProxyHandler<TInterface>::SetIdentification(/*in*/ const TMarshallID& tMarshallID)
        {
            m_tProxyID = tMarshallID;
        }

        template <typename TInterface>
        inline void CProxyHandler<TInterface>::Link(/*in*/ IMarshall* pMarshall)
        {
            assert(pMarshall);
            m_pRequest = pMarshall;
        }

        template <typename TInterface>
        inline void CProxyHandler<TInterface>::Unlink()
        {
            m_pRequest = nullptr;
        }

        template <typename TInterface>
        inline ECallResult CProxyHandler<TInterface>::DoCall(uint32_t uiFuncIndex,
            const serializer<GetPlatformEndianess()>& rserInput, deserializer<GetPlatformEndianess()>& rdesOutput)
        {
            // Needs a valid request interface
            if (!m_pRequest) throw XMarshallNotInitialized{};

            // The sequence holding the input data pointers
            sequence<pointer<uint8_t>> seqInputData;

            // Fill the marshall packet
            SMarshallLocal sInputPacket{};
            sInputPacket.eEndian = GetPlatformEndianess();
            sInputPacket.uiVersion = SDVFrameworkInterfaceVersion;
            sInputPacket.tIfcId = TInterface::_id;
            sInputPacket.uiFuncIndex = uiFuncIndex;

            // Are there any parameters?
            if (rserInput.buffer())
            {
                // Add the serialized parameters to the marshall sequence
                seqInputData.push_back(rserInput.buffer());
                sInputPacket.seqChecksums.push_back(rserInput.checksum());

                // Add additional raw data
                while (!GetRawDataBypass().empty())
                {
                    // Calculate the checksum
                    crcCCITT_FALSE crc;
                    pointer<uint8_t> ptrRawData = GetRawDataBypass().pop();
                    crcCCITT_FALSE crcBypass;
                    uint16_t uiChecksum = crcBypass.calc_checksum(ptrRawData.get(), ptrRawData.size());

                    // Add the raw data to the marshall sequence
                    seqInputData.push_back(ptrRawData);
                    sInputPacket.seqChecksums.push_back(uiChecksum);
                }
            }

            // Create an additional stream for the marshall struct.
            serializer serHdr;
            serdes::CSerdes<SMarshallLocal>::Serialize(serHdr, sInputPacket);

            // Add the packet to the top of the pointer sequence.
            seqInputData.insert(seqInputData.begin(), serHdr.buffer());

            // Call the request; this will update the sequence with result information
            sequence<pointer<uint8_t>> seqOutputData = m_pRequest->Call(seqInputData);
            if (seqOutputData.empty()) throw XMarshallMissingData{};

            // The first data pointer in the sequence contains the marshall header
            pointer<uint8_t> ptrHeader = seqOutputData[0];
            if (!ptrHeader) throw ps::XMarshallMissingData{};

            // And the first byte in the data pointer determines the endianess
            EEndian eSourceEndianess = static_cast<EEndian>(ptrHeader[0]);

            // Deserialize the packet
            SMarshallLocal sOutputPacket{};
            if (eSourceEndianess == EEndian::big_endian)
            {
                deserializer<EEndian::big_endian> deserializer;
                deserializer.attach(ptrHeader, 0);  // Do not check checksum...
                serdes::CSerdes<SMarshallLocal>::Deserialize(deserializer, sOutputPacket);
            } else
            {
                deserializer<EEndian::little_endian> deserializer;
                deserializer.attach(ptrHeader, 0);  // Do not check checksum...
                serdes::CSerdes<SMarshallLocal>::Deserialize(deserializer, sOutputPacket);
            }

            // Check the packet version... must fit, otherwise data misalignment might occur.
            if (sOutputPacket.uiVersion != SDVFrameworkInterfaceVersion) throw XMarshallVersion{};

            // The checksum can be calculated by serializing the packet once more.
            uint16_t uiHdrChecksum = sOutputPacket.uiHdrChecksum;
            serializer serializer;
            serdes::CSerdes<SMarshallLocal>::Serialize(serializer, sOutputPacket);
            // Ignore cppcheck suppress warning is always false. The checksum is written into the packet again. If previously it
            // was different, this causes an integrity exception.
            // cppcheck-suppress knownConditionTrueFalse
            if (sOutputPacket.uiHdrChecksum != uiHdrChecksum)
                throw XMarshallIntegrity{};

            // Check whether the result contains an exception
            if (sOutputPacket.uiFlags & static_cast<uint32_t>(sdv::ps::EMarshallFlags::exception_triggered))
            {
                // Check the checksum of the data
                if (seqOutputData.size() != 2) throw XMarshallIntegrity{};
                if (sOutputPacket.seqChecksums.size() != 1) throw XMarshallIntegrity{};
                crcCCITT_FALSE crc;
                if (crc.calc_checksum(seqOutputData[1].get(), seqOutputData[1].size()) != sOutputPacket.seqChecksums[0])
                    throw XMarshallIntegrity{};

                // Attach the data to the deserializer.
                rdesOutput.attach(seqOutputData[1]);

                // Check for system and/or proxy-stub exceptions
                sdv::exception_id id = 0;
                rdesOutput.peek_front(id);
                switch(id)
                {
                case GetExceptionId<XNoInterface>():
                {
                    XNoInterface exception;
                    serdes::CSerdes<XNoInterface>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XIndexOutOfRange>():
                {
                    XIndexOutOfRange exception;
                    serdes::CSerdes<XIndexOutOfRange>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XInvalidIterator>():
                {
                    XInvalidIterator exception;
                    serdes::CSerdes<XInvalidIterator>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XNullPointer>():
                {
                    XNullPointer exception;
                    serdes::CSerdes<XNullPointer>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XInvalidRefCount>():
                {
                    XInvalidRefCount exception;
                    serdes::CSerdes<XInvalidRefCount>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XBufferTooSmall>():
                {
                    XBufferTooSmall exception;
                    serdes::CSerdes<XBufferTooSmall>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XHashNotMatching>():
                {
                    XHashNotMatching exception;
                    serdes::CSerdes<XHashNotMatching>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XOffsetPastBufferSize>():
                {
                    XOffsetPastBufferSize exception;
                    serdes::CSerdes<XOffsetPastBufferSize>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XUnknownException>():
                {
                    XUnknownException exception;
                    serdes::CSerdes<XUnknownException>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XUnhandledException>():
                {
                    XUnhandledException exception;
                    serdes::CSerdes<XUnhandledException>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<core::XNoMemMgr>():
                {
                    core::XNoMemMgr exception;
                    serdes::CSerdes<core::XNoMemMgr>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<core::XAllocFailed>():
                {
                    core::XAllocFailed exception;
                    serdes::CSerdes<core::XAllocFailed>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XMarshallNotInitialized>():
                {
                    XMarshallNotInitialized exception;
                    serdes::CSerdes<XMarshallNotInitialized>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XMarshallTimeout>():
                {
                    XMarshallTimeout exception;
                    serdes::CSerdes<XMarshallTimeout>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XMarshallMissingData>():
                {
                    XMarshallMissingData exception;
                    serdes::CSerdes<XMarshallMissingData>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XMarshallVersion>():
                {
                    XMarshallVersion exception;
                    serdes::CSerdes<XMarshallVersion>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                case GetExceptionId<XMarshallIntegrity>():
                {
                    XMarshallIntegrity exception;
                    serdes::CSerdes<XMarshallIntegrity>::Deserialize(rdesOutput, exception);
                    throw exception;
                }
                default:
                    break;
                }

                // No known exception, let the caller check whether it can handle the exception.
                return ECallResult::result_exception;
            }

            // Check for packet integrity and add the raw data to the raw data bypass
            if (sOutputPacket.seqChecksums.size() != seqOutputData.size() - 1) throw XMarshallIntegrity{};
            size_t nChecksumIndex = 0;
            pointer<uint8_t> ptrParams;
            GetRawDataBypass().clear();
            for (auto itData = seqOutputData.begin() + 1; itData != seqOutputData.end(); itData++)
            {
                crcCCITT_FALSE crc;
                if (crc.calc_checksum(itData->get(), itData->size()) != sOutputPacket.seqChecksums[nChecksumIndex])
                    throw XMarshallIntegrity{};
                if (nChecksumIndex)
                    GetRawDataBypass().push(*itData);
                else
                    ptrParams = *itData;
                nChecksumIndex++;
            }

            // Return the deserialization object.
            if (ptrParams)
                rdesOutput.attach(ptrParams);
            return ECallResult::result_ok;
        }

        template <typename TInterface>
        inline CStubHandler<TInterface>::CStubHandler()
        {}

        template <typename TInterface>
        inline CStubHandler<TInterface>::~CStubHandler()
        {}

        template <typename TInterface>
        inline void CStubHandler<TInterface>::RegisterDispatchFunc(FNDispatch fnDispatch)
        {
            m_vecDispatch.push_back(fnDispatch);
        }

        template <typename TInterface>
        template <typename TExcept>
        inline pointer<uint8_t> CStubHandler<TInterface>::SerializeException(EEndian eEndian, const TExcept& rexcept)
        {
            GetRawDataBypass().clear(); // just in case
            if (eEndian == sdv::EEndian::big_endian)
            {
                sdv::serializer<sdv::EEndian::big_endian> serOutput;
                serdes::CSerdes<TExcept>::Serialize(serOutput, rexcept);
                return serOutput.buffer();
            } else
            {
                sdv::serializer<sdv::EEndian::little_endian> serOutput;
                serdes::CSerdes<TExcept>::Serialize(serOutput, rexcept);
                return serOutput.buffer();
            }
        }

        template <typename TInterface>
        inline void CStubHandler<TInterface>::SetIdentification(/*in*/ const TMarshallID& tMarshallID)
        {
            m_tStubID = tMarshallID;
        }

        template <typename TInterface>
        inline sequence<pointer<uint8_t>> CStubHandler<TInterface>::Call(/*inout*/ sequence<pointer<uint8_t>>& seqInputData)
        {
            if (seqInputData.empty()) throw XMarshallMissingData{};

            // The first data pointer in the sequence contains the marshall header
            pointer<uint8_t> ptrHeader = seqInputData[0];
            if (!ptrHeader) throw ps::XMarshallMissingData{};

            // And the first byte in the data pointer determines the endianess
            EEndian eSourceEndianess = static_cast<EEndian>(ptrHeader[0]);

            // Deserialize the packet
            SMarshallLocal sInputPacket{};
            if (eSourceEndianess == EEndian::big_endian)
            {
                deserializer<EEndian::big_endian> desInput;
                desInput.attach(ptrHeader, 0);  // Do not check checksum...
                serdes::CSerdes<SMarshallLocal>::Deserialize(desInput, sInputPacket);
            } else
            {
                deserializer<EEndian::little_endian> desInput;
                desInput.attach(ptrHeader, 0);  // Do not check checksum...
                serdes::CSerdes<SMarshallLocal>::Deserialize(desInput, sInputPacket);
            }

            // Check the packet version... must fit, otherwise data misalignment might occur.
            if (sInputPacket.uiVersion != SDVFrameworkInterfaceVersion) throw XMarshallVersion{};

            // The checksum can be calculated by serializing the packet once more.
            uint16_t uiHdrChecksum = sInputPacket.uiHdrChecksum;
            serializer rserInput;
            serdes::CSerdes<SMarshallLocal>::Serialize(rserInput, sInputPacket);
            // Ignore cppcheck suppress warning is always false. The checksum is written into the packet again. If previously it
            // was different, this causes an integrity exception.
            // cppcheck-suppress knownConditionTrueFalse
            if (sInputPacket.uiHdrChecksum != uiHdrChecksum) throw XMarshallIntegrity{};

            // Check for packet integrity and add the raw data to the raw data bypass
            if (sInputPacket.seqChecksums.size() != seqInputData.size() - 1) throw XMarshallIntegrity{};
            size_t nChecksumIndex = 0;
            pointer<uint8_t> ptrInputParams;
            GetRawDataBypass().clear();
            for (auto itData = seqInputData.begin() + 1; itData != seqInputData.end(); itData++)
            {
                crcCCITT_FALSE crc;
                if (crc.calc_checksum(itData->get(), itData->size()) != sInputPacket.seqChecksums[nChecksumIndex])
                    throw XMarshallIntegrity{};
                if (nChecksumIndex)
                    GetRawDataBypass().push(*itData);
                else
                    ptrInputParams = *itData;
                nChecksumIndex++;
            }

            // If any exception occured during the input processing, return the buffer without processing. This allows the
            // caller to receive the exception.
            if (sInputPacket.uiFlags & static_cast<uint32_t>(EMarshallFlags::exception_triggered))
                return seqInputData;

            // Call dispatch function
            FNDispatch fnDispatch = m_vecDispatch[sInputPacket.uiFuncIndex];
            if (!fnDispatch) throw XMarshallNotInitialized{};
            pointer<uint8_t> ptrOutputParams;
            ECallResult eResult = ECallResult::result_ok;
            try
            {
                eResult = fnDispatch(eSourceEndianess, ptrInputParams, ptrOutputParams);
            } catch (const XNoInterface& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XIndexOutOfRange& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XInvalidIterator& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XNullPointer& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XInvalidRefCount& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XBufferTooSmall& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XHashNotMatching& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XOffsetPastBufferSize& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XUnknownException& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const XUnhandledException& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const core::XNoMemMgr& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (const core::XAllocFailed& rexcept)
            {
                ptrOutputParams = SerializeException(eSourceEndianess, rexcept);
                eResult = ECallResult::result_exception;
            } catch (...)
            {
                XUnhandledException xUnhandled;
                ptrOutputParams = SerializeException(eSourceEndianess, xUnhandled);
                eResult = ECallResult::result_exception;
            }

            // The sequence holding the output data pointers
            sequence<pointer<uint8_t>> seqOutputData;

            // Fill the marshall packet
            SMarshallLocal sOutputPacket{};
            sOutputPacket.eEndian = eSourceEndianess;
            sOutputPacket.uiVersion = SDVFrameworkInterfaceVersion;
            sOutputPacket.tIfcId = TInterface::_id;
            sOutputPacket.uiFuncIndex = sInputPacket.uiFuncIndex;
            sOutputPacket.uiFlags = static_cast<uint32_t>(EMarshallFlags::direction_output);
            if (eResult == ECallResult::result_exception)
                sOutputPacket.uiFlags |= static_cast<uint32_t>(EMarshallFlags::exception_triggered);

            // Are there any parameters?
            if (ptrOutputParams)
            {
                // Calculate the checksum
                crcCCITT_FALSE crc;
                uint16_t uiChecksum = crc.calc_checksum(ptrOutputParams.get(), ptrOutputParams.size());

                // Add the serialized parameters to the marshall sequence
                seqOutputData.push_back(ptrOutputParams);
                sOutputPacket.seqChecksums.push_back(uiChecksum);

                // Add additional raw data
                while (!GetRawDataBypass().empty())
                {
                    // Calculate the checksum
                    pointer<uint8_t> ptrRawData = GetRawDataBypass().pop();
                    crcCCITT_FALSE crcBypass;
                    uiChecksum = crcBypass.calc_checksum(ptrRawData.get(), ptrRawData.size());

                    // Add the raw data to the marshall sequence
                    seqOutputData.push_back(ptrRawData);
                    sOutputPacket.seqChecksums.push_back(uiChecksum);
                }
            }

            // Create an additional stream for the marshall struct.
            serializer serHdr;
            serdes::CSerdes<SMarshallLocal>::Serialize(serHdr, sOutputPacket);

            // Add the packet to the top of the pointer sequence.
            seqOutputData.insert(seqOutputData.begin(), serHdr.buffer());

            return seqOutputData;
        }
    } // namespace ps
} // namespace sdv

inline bool operator==(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    if (rtID1.uiHostID != rtID2.uiHostID) return false;
    if (rtID1.tProcessID != rtID2.tProcessID) return false;
    if (rtID1.uiIdent != rtID2.uiIdent) return false;
    if (rtID1.uiControl != rtID2.uiControl) return false;
    return true;
}

inline bool operator==(const sdv::ps::TMarshallID& rtID1, size_t nVal)
{
    if (nVal) return false;
    return operator==(rtID1, sdv::ps::TMarshallID{});
}

inline bool operator==(size_t nVal, const sdv::ps::TMarshallID& rtID2)
{
    if (nVal) return false;
    return operator==(sdv::ps::TMarshallID{}, rtID2);
}

inline bool operator!=(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    return !operator==(rtID1, rtID2);
}

inline bool operator!=(const sdv::ps::TMarshallID& rtID1, size_t nVal)
{
    if (nVal) return true;
    return !operator==(rtID1, sdv::ps::TMarshallID{});
}

inline bool operator!=(size_t nVal, const sdv::ps::TMarshallID& rtID2)
{
    if (nVal) return true;
    return !operator==(sdv::ps::TMarshallID{}, rtID2);
}

inline bool operator<(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    if (rtID1.uiHostID < rtID2.uiHostID) return true;
    if (rtID1.uiHostID > rtID2.uiHostID) return false;
    if (rtID1.tProcessID < rtID2.tProcessID) return true;
    if (rtID1.tProcessID > rtID2.tProcessID) return false;
    if (rtID1.uiIdent < rtID2.uiIdent) return true;
    return false;
}

inline bool operator<=(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    if (rtID1.uiHostID < rtID2.uiHostID) return true;
    if (rtID1.uiHostID > rtID2.uiHostID) return false;
    if (rtID1.tProcessID < rtID2.tProcessID) return true;
    if (rtID1.tProcessID > rtID2.tProcessID) return false;
    if (rtID1.uiIdent < rtID2.uiIdent) return true;
    if (rtID1.uiControl == rtID2.uiControl) return true;
    return false;
}

inline bool operator>(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    if (rtID1.uiHostID > rtID2.uiHostID) return true;
    if (rtID1.uiHostID < rtID2.uiHostID) return false;
    if (rtID1.tProcessID > rtID2.tProcessID) return true;
    if (rtID1.tProcessID < rtID2.tProcessID) return false;
    if (rtID1.uiIdent > rtID2.uiIdent) return true;
    return false;
}

inline bool operator>=(const sdv::ps::TMarshallID& rtID1, const sdv::ps::TMarshallID& rtID2)
{
    if (rtID1.uiHostID > rtID2.uiHostID) return true;
    if (rtID1.uiHostID < rtID2.uiHostID) return false;
    if (rtID1.tProcessID > rtID2.tProcessID) return true;
    if (rtID1.tProcessID < rtID2.tProcessID) return false;
    if (rtID1.uiIdent > rtID2.uiIdent) return true;
    if (rtID1.uiControl == rtID2.uiControl) return true;
    return false;
}

inline bool operator!(const sdv::ps::TMarshallID& rtID)
{
    // The control ID is the onl ID which is not allowed to be zero.
    return rtID.uiControl ? false : true;
}

inline bool operator==(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    if (rtID1.uiIdent != rtID2.uiIdent) return false;
    if (rtID1.uiControl != rtID2.uiControl) return false;
    return true;
}

inline bool operator==(const sdv::com::TConnectionID& rtID1, size_t nVal)
{
    if (nVal) return false;
    return operator==(rtID1, sdv::com::TConnectionID{});
}

inline bool operator==(size_t nVal, const sdv::com::TConnectionID& rtID2)
{
    if (nVal) return false;
    return operator==(sdv::com::TConnectionID{}, rtID2);
}

inline bool operator!=(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    return !operator==(rtID1, rtID2);
}

inline bool operator!=(const sdv::com::TConnectionID& rtID1, size_t nVal)
{
    if (nVal) return true;
    return !operator==(rtID1, sdv::com::TConnectionID{});
}

inline bool operator!=(size_t nVal, const sdv::com::TConnectionID& rtID2)
{
    if (nVal) return true;
    return !operator==(sdv::com::TConnectionID{}, rtID2);
}

inline bool operator<(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    if (rtID1.uiIdent < rtID2.uiIdent) return true;
    return false;
}

inline bool operator<=(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    if (rtID1.uiIdent < rtID2.uiIdent) return true;
    if (rtID1.uiControl == rtID2.uiControl) return true;
    return false;
}

inline bool operator>(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    if (rtID1.uiIdent > rtID2.uiIdent) return true;
    return false;
}

inline bool operator>=(const sdv::com::TConnectionID& rtID1, const sdv::com::TConnectionID& rtID2)
{
    if (rtID1.uiIdent > rtID2.uiIdent) return true;
    if (rtID1.uiControl == rtID2.uiControl) return true;
    return false;
}

inline bool operator!(const sdv::com::TConnectionID& rtID)
{
    // The control ID is the onl ID which is not allowed to be zero.
    return rtID.uiControl ? false : true;
}

#endif // !defined(SDV_PS_SUPPORT_INL)
