/********************************************************************************
 * 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 COM_CHANNEL_H
#define COM_CHANNEL_H

#include <support/pssup.h>
#include <interfaces/ipc.h>
#include "../../global/scheduler/scheduler.h"

// Forward declaration
class CCommunicationControl;
class CMarshallObject;

/**
 * @brief Communication channel connector (endpoint).
 */
class CChannelConnector : public sdv::IInterfaceAccess, public sdv::ipc::IConnectEventCallback,
    public sdv::ipc::IDataReceiveCallback
{
public:
    /**
     * @brief Constructor for establishing the server connection.
     * @param[in] rcontrol Reference to the communication control class.
     * @param[in] uiIndex The current index of this connection (used to create the connection ID).
     * @param[in] pChannelEndpoint Interface pointer to the channel.
     */
    CChannelConnector(CCommunicationControl& rcontrol, uint32_t uiIndex, sdv::IInterfaceAccess* pChannelEndpoint);

    /**
     * @brief Destructor
     */
    ~CChannelConnector();

    // Interface map
    BEGIN_SDV_INTERFACE_MAP()
        SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback)
        SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback)
    END_SDV_INTERFACE_MAP()

    /**
     * @brief Connect as server (attaching the channel to the target object using a stub).
     * @param[in] pObject Pointer to the object to attach.
     * @param[in] bAllowReconnect Allow a disconnect and re-connect (disconnect doesn't trigger a channel removal).
     * @return Returns 'true' when the attachment succeeds; 'false' when not.
     */
    bool ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect);

    /**
     * @brief Connect as client (connecting to an existing server and creating a proxy).
     * @param[in] uiTimeoutMs The timeout time trying to connect.
     * @return Returns a pointer to the proxy object representing the object at the other end of the channel. Or NULL when a
     * timeout occurred.
     */
    sdv::IInterfaceAccess* ClientConnect(uint32_t uiTimeoutMs);

    /**
     * @brief Is the communication channel currently connected?
     * @return Returns whether the connector has an active connection.
     */
    bool IsConnected() const;

    /**
     * @brief Set the current connect statue. Overload of sdv::ipc::IConnectEventCallback::SetConnectState.
     * @param[in] eConnectState The connection state.
     */
    virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override;

    /**
     * @brief Callback to be called by the IPC connection when receiving a data packet. Overload of
     * sdv::ipc::IDataReceiveCallback::ReceiveData.
     * @param[inout] seqData Sequence of data buffers to received. The sequence might be changed to optimize the communication
     * without having to copy the data.
     */
    virtual void ReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData) override;

    /**
     * @brief Decoupled receive callback to be called by the scheduler when receiving a data packet.
     * @param[inout] seqData Sequence of data buffers to received. The sequence might be changed to optimize the communication
     * without having to copy the data.
     */
    void DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointer<uint8_t>>& seqData);

    /**
    * @brief Sends data consisting of multiple data chunks via the IPC connection.
    * @param[in] tProxyID Marshall ID of the proxy (source).
    * @param[in] tStubID Marshall ID of the stub (target).
    * @param[in] rseqInputData Sequence of data buffers to be sent. May be altered during processing to add/change the sequence
    * content without having to copy the data.
    * @return Returns the results of the call or throws a marshall exception.
    */
    sdv::sequence<sdv::pointer<uint8_t>> MakeCall(sdv::ps::TMarshallID tProxyID, sdv::ps::TMarshallID tStubID,
        sdv::sequence<sdv::pointer<uint8_t>>& rseqInputData);

    /**
    * @brief Get a proxy for the interface connection to the stub.
    * @param[in] id The ID of the interface this object marshalls the calls for.
    * @param[in] tStubID The stub ID this proxy is communicating to.
    * @return Returns a shared pointer to the proxy object.
    */
    std::shared_ptr<CMarshallObject> GetOrCreateProxy(sdv::interface_id id, sdv::ps::TMarshallID tStubID);

    /**
     * @brief Get the connection ID of this connector.
     * @return The connection ID.
     */
    sdv::com::TConnectionID GetConnectionID() const;

private:
    /**
     * @brief Endpoint type the channel connector
     */
    enum class EEndpointType {server, client};

    /**
     * @brief Call entry structure that is defined for a call to wait for the result.
     */
    struct SCallEntry
    {
        /**
         * @brief Call entry state.
         */
        enum EState
        {
            initialized,
            processing,
            processed,
            canceled,
        }                                       eState = EState::initialized;   ///< Data processing state.
        sdv::sequence<sdv::pointer<uint8_t>>    seqResult;                      ///< The result data.
        std::mutex                              mtxWaitForResult;               ///< Mutex to protect result processing.
        std::condition_variable                 cvWaitForResult;                ///< Condition var to trigger result processing.
    };

    CCommunicationControl&              m_rcontrol;                     ///< Reference to the communication control class.
    sdv::TObjectPtr                     m_ptrChannelEndpoint;           ///< Managed pointer to the channel endpoint.
    uint64_t                            m_uiConnectStateCookie = 0;     ///< Connection state cookie (received after registration).
    std::shared_ptr<CMarshallObject>    m_ptrInitialMarshallObject;     ///< Initial marshall object used after a connect event.
    sdv::ipc::EConnectState             m_eConnectState = sdv::ipc::EConnectState::uninitialized;     ///< Current connection state.
    bool                                m_bAllowReconnect = false;      ///< When set, allow reconnection of the server.
    EEndpointType                       m_eEndpointType = EEndpointType::client;    ///< Endpoint type of this connector.
    std::recursive_mutex                m_mtxMarshallObjects;           ///< Synchronize access to the marshall object vector.
    std::map<sdv::ps::TMarshallID, std::shared_ptr<CMarshallObject>> m_mapProxyObjects;    ///< Map of stub IDs to proxy objects
    sdv::com::TConnectionID             m_tConnectionID{};              ///< Connection ID for this connector.
    sdv::ipc::IDataSend*                m_pDataSend = nullptr;          ///< Pointer to the send interface.
    std::mutex                          m_mtxCalls;                     ///< Call map protection.
    std::map<uint64_t, SCallEntry&>     m_mapCalls;                     ///< call map.
    CTaskScheduler<sdv::core::secure_thread> m_scheduler;               ///< Scheduler to process incoming calls.
    sdv::core::IPermissionControl*      m_pPermissionControl = nullptr; ///< Pointer to the permission control interface of the
                                                                        ///< target system.
};

#endif // !defined COM_CHANNEL_H