// SPDX-License-Identifier: GPL-2.0-only
/*
 * Digital Voice Modem - Common Library
 * GPLv2 Open Source. Use is subject to license terms.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *  Copyright (C) 2017-2026 Bryan Biedenkapp, N2PLL
 *
 */
/**
 * @file Network.h
 * @ingroup network_core
 * @file Network.cpp
 * @ingroup network_core
 */
#if !defined(__NETWORK_H__)
#define __NETWORK_H__

#include "common/Defines.h"
#include "common/network/BaseNetwork.h"
#include "common/network/RTPHeader.h"
#include "common/network/RTPFNEHeader.h"
#include "common/lookups/RadioIdLookup.h"
#include "common/lookups/TalkgroupRulesLookup.h"
#include "common/p25/kmm/KeysetItem.h"

#include <string>
#include <cstdint>
#include <functional>

namespace network
{
    // ---------------------------------------------------------------------------
    //  Structure Declaration
    // ---------------------------------------------------------------------------

    /**
     * @brief This structure represents a peer metadata.
     * @ingroup network_core
     */
    struct PeerMetadata {
        /** @name Identity and Frequency */
        std::string identity;                   //!< Peer Identity
        uint32_t rxFrequency;                   //!< Peer Rx Frequency
        uint32_t txFrequency;                   //!< Peer Tx Frequency
        /** @} */

        /** @name System Info */
        uint32_t power;                         //!< Peer Tx Power (W)
        float latitude;                         //!< Location Latitude (decmial notation)
        float longitude;                        //!< Location Longitude (decmial notation)
        int height;                             //!< Height (M)
        std::string location;                   //!< Textual Location
        /** @} */

        /** @name Channel Data */
        float txOffsetMhz;                      //!< Tx Offset (MHz)
        float chBandwidthKhz;                   //!< Channel Bandwidth (kHz)
        uint8_t channelId;                      //!< Channel ID
        uint32_t channelNo;                     //!< Channel Number
        /** @} */

        /** @name RCON */
        std::string restApiPassword;            //!< REST API Password
        uint16_t restApiPort;                   //!< REST API Port
        /** @} */

        /** @name Flags */
        bool isConventional;                    //!< Flag indicating peer is a conventional peer.
        /** @} */
    };

    // ---------------------------------------------------------------------------
    //  Structure Declaration
    // ---------------------------------------------------------------------------

    /**
     * @brief Represents high availiability IP address data.
     * @ingroup network_core
     */
    struct PeerHAIPEntry {
    public:
        /**
         * @brief Initializes a new instance of the PeerHAIPEntry class
         */
        PeerHAIPEntry() :
            masterAddress(),
            masterPort(TRAFFIC_DEFAULT_PORT)
        {
            /* stub **/
        }

        /**
         * @brief Initializes a new instance of the PeerHAIPEntry class
         * @param address Master IP Address.
         * @param port Master Port.
         */
        PeerHAIPEntry(std::string address, uint16_t port) :
            masterAddress(address),
            masterPort(port)
        {
            /* stub */
        }

        /**
         * @brief Equals operator. Copies this PeerHAIPEntry to another PeerHAIPEntry.
         * @param data Instance of PeerHAIPEntry to copy.
         */
        PeerHAIPEntry& operator=(const PeerHAIPEntry& data)
        {
            if (this != &data) {
                masterAddress = data.masterAddress;
                masterPort = data.masterPort;
            }

            return *this;
        }

    public:
        std::string masterAddress;  //!< Master IP Address.
        uint16_t masterPort;        //!< Master Port.
    };

    // ---------------------------------------------------------------------------
    //  Class Declaration
    // ---------------------------------------------------------------------------

    /**
     * @brief Implements the core peer networking logic.
     * @ingroup network_core
     */
    class HOST_SW_API Network : public BaseNetwork {
    public:
        /**
         * @brief Initializes a new instance of the Network class.
         * @param address Network Hostname/IP address to connect to.
         * @param port Network port number.
         * @param localPort 
         * @param peerId Unique ID on the network.
         * @param password Network authentication password.
         * @param duplex Flag indicating full-duplex operation.
         * @param debug Flag indicating whether network debug is enabled.
         * @param dmr Flag indicating whether DMR is enabled.
         * @param p25 Flag indicating whether P25 is enabled.
         * @param nxdn Flag indicating whether NXDN is enabled.
         * @param analog Flag indicating whether analog is enabled.
         * @param slot1 Flag indicating whether DMR slot 1 is enabled for network traffic.
         * @param slot2 Flag indicating whether DMR slot 2 is enabled for network traffic.
         * @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network.
         * @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network.
         * @param updateLookup Flag indicating that the system will accept radio ID and talkgroup ID lookups from the network.
         */
        Network(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password,
            bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool analog, bool slot1, bool slot2, 
            bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup);
        /**
         * @brief Finalizes a instance of the Network class.
         */
        ~Network() override;

        /**
         * @brief Resets the DMR ring buffer for the given slot.
         * @param slotNo DMR slot ring buffer to reset.
         */
        void resetDMR(uint32_t slotNo) override;
        /**
         * @brief Resets the P25 ring buffer.
         */
        void resetP25() override;
        /**
         * @brief Resets the P25 Phase 2 ring buffer.
         */
        void resetP25P2(uint32_t slotNo) override;
        /**
         * @brief Resets the NXDN ring buffer.
         */
        void resetNXDN() override;
        /**
         * @brief Resets the analog ring buffer.
         */
        void resetAnalog() override;
        
        /**
         * @brief Sets the instances of the Radio ID and Talkgroup ID lookup tables.
         * @param ridLookup Radio ID Lookup Table Instance
         * @param tidLookup Talkgroup Rules Lookup Table Instance
         */
        void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup);
        /**
         * @brief Sets metadata configuration settings from the modem.
         * @param identity Peer Identity.
         * @param rxFrequency Rx Frequency (Informational Only).
         * @param txFrequency Tx Frequency (Informational Only).
         * @param txOffsetMhz Tx Offset in MHz (Informational Only).
         * @param chBandwidthKhz Channel Bandwidth in kHz (Informational Only).
         * @param channelId Channel ID (Informational Only).
         * @param channelNo Channel Number (Informational Only).
         * @param power RF Power Level (Informational Only).
         * @param latitude Geographic Latitude (Informational Only).
         * @param longitude Geographic Longitude (Informational Only).
         * @param height Station Height in Meters (Informational Only).
         * @param location Textual Location (Informational Only).
         */
        void setMetadata(const std::string& identity, uint32_t rxFrequency, uint32_t txFrequency, float txOffsetMhz, float chBandwidthKhz,
            uint8_t channelId, uint32_t channelNo, uint32_t power, float latitude, float longitude, int height, const std::string& location);
        /**
         * @brief Sets REST API configuration settings from the modem.
         * @param password REST API Password.
         * @param port REST API Port.
         */
        void setRESTAPIData(const std::string& password, uint16_t port);
        /**
         * @brief Sets a flag indicating whether the conventional option is sent to the FNE.
         * @param conv Flag indicating conventional operation.
         */
        void setConventional(bool conv) { m_metadata->isConventional = conv; }
        /**
         * @brief Sets endpoint preshared encryption key.
         * @param presharedKey Encryption preshared key for networking.
         */
        void setPresharedKey(const uint8_t* presharedKey);
        /**
         * @brief Sets endpoint preshared encryption key for peer key requets.
         * @param presharedKey Encryption preshared key for networking.
         */
        void setKMFPresharedKey(const uint8_t* presharedKey);

        /**
         * @brief Updates the timer by the passed number of milliseconds.
         * @param ms Number of milliseconds.
         */
        void clock(uint32_t ms) override;

        /**
         * @brief Opens connection to the network.
         * @returns bool True, if networking has started, otherwise false.
         */
        bool open() override;

        /**
         * @brief Closes connection to the network.
         */
        void close() override;

        /**
         * @brief Flag indicating if this network connection enabled.
         * @returns bool Flag indicating if this network connection enabled.
         */
        bool isEnabled() const { return m_enabled; }
        /**
         * @brief Sets flag enabling network communication.
         * @param enabled Flag indicating if this network connection enabled.
         */
        void enable(bool enabled);

        /**
         * @brief Helper to clear the duplicate connection flag.
         */
        void clearDuplicateConnFlag() { m_flaggedDuplicateConn = false; }

        /**
         * @brief Helper to set the peer connected callback.
         * @param callback 
         */
        void setPeerConnectedCallback(std::function<void()>&& callback) { m_peerConnectedCallback = callback; }
        /**
         * @brief Helper to set the peer disconnected callback.
         * @param callback 
         */
        void setPeerDisconnectedCallback(std::function<void()>&& callback) { m_peerDisconnectedCallback = callback; }

        /**
         * @brief Helper to set the DMR In-Call Control callback.
         * @param callback 
         */
        void setDMRICCCallback(std::function<void(NET_ICC::ENUM, uint32_t, uint8_t, uint32_t, uint32_t, uint32_t)>&& callback) { m_dmrInCallCallback = callback; }
        /**
         * @brief Helper to set the P25 In-Call Control callback.
         * @param callback 
         */
        void setP25ICCCallback(std::function<void(NET_ICC::ENUM, uint32_t, uint32_t, uint32_t, uint32_t)>&& callback) { m_p25InCallCallback = callback; }
        /**
         * @brief Helper to set the NXDN In-Call Control callback.
         * @param callback 
         */
        void setNXDNICCCallback(std::function<void(NET_ICC::ENUM, uint32_t, uint32_t, uint32_t, uint32_t)>&& callback) { m_nxdnInCallCallback = callback; }
        /**
         * @brief Helper to set the analog In-Call Control callback.
         * @param callback 
         */
        void setAnalogICCCallback(std::function<void(NET_ICC::ENUM, uint32_t, uint32_t, uint32_t, uint32_t)>&& callback) { m_analogInCallCallback = callback; }

        /**
         * @brief Helper to set the enc. key response callback.
         * @param callback 
         */
        void setKeyResponseCallback(std::function<void(p25::kmm::KeyItem, uint8_t, uint8_t)>&& callback) { m_keyRespCallback = callback; }
        /**
         * @brief Helper to set the LLA encryption key response callback.
         * @param callback 
         */
        void setLLAKeyResponseCallback(std::function<void(uint32_t, p25::kmm::KeyItem, uint8_t)>&& callback) { m_llaKeyRespCallback = callback; }

    public:
        /**
         * @brief Last received RTP sequence number.
         */
        DECLARE_RO_PROPERTY_PLAIN(uint16_t, pktLastSeq);

    protected:
        std::string m_address;
        uint16_t m_port;

        std::string m_configuredAddress;
        uint16_t m_configuredPort;

        std::vector<PeerHAIPEntry> m_haIPs;
        uint8_t m_currentHAIP;

        std::string m_password;

        bool m_enabled;

        bool m_dmrEnabled;
        bool m_p25Enabled;
        bool m_nxdnEnabled;
        bool m_analogEnabled;

        bool m_updateLookup;
        bool m_saveLookup;

        lookups::RadioIdLookup* m_ridLookup;
        lookups::TalkgroupRulesLookup* m_tidLookup;

        uint8_t* m_salt;

        uint8_t* m_kmfPresharedKey;

        Timer m_retryTimer;
        uint8_t m_retryCount;
        uint8_t m_maxRetryCount;
        bool m_flaggedDuplicateConn;
        Timer m_timeoutTimer;

        uint32_t m_pingsReceived;

        uint32_t* m_rxDMRStreamId;
        uint32_t m_rxP25StreamId;
        uint32_t* m_rxP25P2StreamId;
        uint32_t m_rxNXDNStreamId;
        uint32_t m_rxAnalogStreamId;

        uint16_t m_pktSeq;
        uint32_t m_loginStreamId;

        PeerMetadata* m_metadata;
        RTPStreamMultiplex* m_mux;

        uint32_t m_remotePeerId;

        /**
         * @brief Flag indicating this peer will not perform peer ID checking and will process most incoming packets.
         */
        bool m_promiscuousPeer;
        /**
         * @brief Flag indicating this peer will not handle protocol processing internally, and will forward processing
         *  to the defined user packet handler.
         */
        bool m_userHandleProtocol;
        /**
         * @brief Flag indicating this peer will not disable networking services on a master ACL NAK.
         */
        bool m_neverDisableOnACLNAK;
        /**
         * @brief Flag indicating this peer will pass encryption keys without a preshared key set.
         */
        bool m_passKeysWithNoPresharedKey;

        /**
         * @brief Peer Connected Function Callback.
         *  (This is called once this peer connects (or reconnects) to an upstream master.)
         */
        std::function<void()> m_peerConnectedCallback;
        /**
         * @brief Peer Disconnected Function Callback.
         *  (This is called once this peer disconnects from an upstream master.)
         */
        std::function<void()> m_peerDisconnectedCallback;

        /**
         * @brief DMR In-Call Control Function Callback.
         *  (This is called when the master sends a In-Call Control request.)
         */
        std::function<void(NET_ICC::ENUM command, uint32_t dstId, uint8_t slotNo, 
            uint32_t peerId, uint32_t ssrc, uint32_t streamId)> m_dmrInCallCallback;
        /**
         * @brief P25 In-Call Control Function Callback.
         *  (This is called once the master sends a In-Call Control request.)
         */
        std::function<void(NET_ICC::ENUM command, uint32_t dstId, 
            uint32_t peerId, uint32_t ssrc, uint32_t streamId)> m_p25InCallCallback;
        /**
         * @brief NXDN In-Call Control Function Callback.
         *  (This is called once the master sends a In-Call Control request.)
         */
        std::function<void(NET_ICC::ENUM command, uint32_t dstId, 
            uint32_t peerId, uint32_t ssrc, uint32_t streamId)> m_nxdnInCallCallback;
        /**
         * @brief Analog In-Call Control Function Callback.
         *  (This is called once the master sends a In-Call Control request.)
         */
        std::function<void(NET_ICC::ENUM command, uint32_t dstId, 
            uint32_t peerId, uint32_t ssrc, uint32_t streamId)> m_analogInCallCallback;

        /**
         * @brief Encryption Key Response Function Callback.
         *  (This is called once the master responds to a key request.)
         */
        std::function<void(p25::kmm::KeyItem ki, uint8_t algId, uint8_t keyLength)> m_keyRespCallback;
        /**
         * @brief LLA Encryption Key Response Function Callback.
         *  (This is called once the master responds to a key LLA request.)
         */
        std::function<void(uint32_t rsi, p25::kmm::KeyItem ki, uint8_t keyLength)> m_llaKeyRespCallback;

        /**
         * @brief Helper to verify the given RTP sequence for the given RTP stream.
         * @param[out] lastRxSeq Last Received Sequence.
         * @return MULTIPLEX_RET_CODE Return code.
         */
        MULTIPLEX_RET_CODE verifyStream(uint16_t* lastRxSeq);

        /**
         * @brief User overrideable handler that allows user code to process network packets not handled by this class.
         * @param peerId Peer ID.
         * @param opcode FNE network opcode pair.
         * @param[in] data Buffer containing message to send to peer.
         * @param length Length of buffer.
         * @param streamId Stream ID.
         * @param fneHeader RTP FNE Header.
         * @param rtpHeader RTP Header.
         */
        virtual void userPacketHandler(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data = nullptr, uint32_t length = 0U,
            uint32_t streamId = 0U, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader());
        /**
         * @brief User overrideable handler that allows user code to process NAKs received from the master.
         * @param peerId Peer ID.
         * @param reason Reason code.
         * @param fneHeader RTP FNE Header.
         * @param rtpHeader RTP Header.
         */
        virtual bool userNakHandler(uint32_t peerId, uint16_t reason, const frame::RTPFNEHeader& fneHeader = frame::RTPFNEHeader(), const frame::RTPHeader& rtpHeader = frame::RTPHeader());

        /**
         * @brief Writes login request to the network.
         * \code{.unparsed}
         *  Below is the representation of the data layout for the repeater/end point login message.
         *  The message is 8 bytes in length.
         * 
         *  Byte 0               1               2               3
         *  Bit  7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Protocol Tag (RPTL)                                           |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Peer ID                                                       |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         * \endcode
         * @returns bool True, if login request was sent, otherwise false.
         */
        bool writeLogin();
        /**
         * @brief Writes network authentication challenge.
         * \code{.unparsed}
         *  Below is the representation of the data layout for the repeater/end point login message.
         *  The message is 40 bytes in length.
         * 
         *  Byte 0               1               2               3
         *  Bit  7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Protocol Tag (RPTK)                                           |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Peer ID                                                       |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | 30 bytes of SHA-256 ......................................... |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         * \endcode
         * @returns bool True, if authorization response was sent, otherwise false.
         */
        bool writeAuthorisation();
        /**
         * @brief Writes modem configuration to the network.
         * \code{.unparsed}
         *  Below is the representation of the data layout for the repeater/end point login message.
         *  The message is 40 bytes in length.
         * 
         *  Byte 0               1               2               3
         *  Bit  7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Protocol Tag (RPTC)                                           |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Reserved                                                      |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *      | Variable Length JSON Payload ................................ |
         *      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         * 
         * The JSON payload is variable length and looks like this:
         *  {
         *      "identity": "<Peer Identity String>",
         *      "rxFrequency": <Peer Rx Frequency in Hz>,
         *      "txFrequency": <Peer Tx Frequency in Hz>,
         *      "info":
         *      {
         *          "latitude": <Peer Geographical Latitude>,
         *          "longitude": <Peer Geographical Longitude>,
         *          "height": <Peer Height (in meters)>,
         *          "location": "<Textual String Describing Peer Location>"
         *      },
         *      "channel":
         *      {
         *          "txPower": <Peer Transmit Power (in W)>,
         *          "txOffsetMhz": <Peer Transmit Offset (in MHz)>,
         *          "chBandwidthKhz": <Peer Channel Bandwidth (in KHz>,
         *          "channelId": <Channel ID from the IDEN channel bandplan>,
         *          "channelNo": <Channel Number from the IDEN channel bandplan>,
         *      },
         *      "software": "<Textual hardcoded string containing software watermark>",
         *  }
         * 
         * These are extra parameters used in the root of the above JSON.
         *  {
         *      "externalPeer": <Boolean flag indicating whether or not this peer is a linked CFNE peer>,
         *      "masterPeerId": <Linked CFNE peer's FNE master peer ID>,
         * 
         *      "conventionalPeer": <Boolean flag indicating whether or not this is a conventional peer>,
         * 
         *      "sysView": <Boolean flag indicating whether or not this peer is a SysView peer>,
         *  }
         * \endcode
         * @returns bool True, if configuration response was sent, otherwise false.
         */
        virtual bool writeConfig();
        /**
         * @brief Writes a network stay-alive ping.
         * \code{.unparsed}
         *  Below is the representation of the data layout for the repeater/end point login message.
         *  The message is 1 bytes in length.
         * 
         *  Byte 0
         *  Bit  7 6 5 4 3 2 1 0
         *      +-+-+-+-+-+-+-+-+
         *      | Reserved      |
         *      +-+-+-+-+-+-+-+-+
         * \endcode
         * @returns bool True, if stay-alive ping was sent, otherwise false.
         */
        bool writePing();
    };
} // namespace network

#endif // __NETWORK_H__
