// 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) 2016 Jonathan Naylor, G4KLX
 *  Copyright (C) 2017-2022,2024,2025 Bryan Biedenkapp, N2PLL
 *  Copyright (c) 2024 Patrick McDonnell, W3AXL
 *  Copyright (c) 2024 Caleb, KO4UYJ
 *
 */
/**
 * @defgroup lookups_peer Peer List Lookups
 * @brief Implementation for peer list lookup tables.
 * @ingroup lookups
 * 
 * @file PeerListLookup.h
 * @ingroup lookups_peer
 * @file PeerListLookup.cpp
 * @ingroup lookups_peer
 */
#if !defined(__PEER_LIST_LOOKUP_H__)
#define __PEER_LIST_LOOKUP_H__

#include "common/Defines.h"
#include "common/lookups/LookupTable.h"
#include "common/network/AdaptiveJitterBuffer.h"

#include <string>
#include <vector>
#include <mutex>

namespace lookups
{
    // ---------------------------------------------------------------------------
    //  Class Declaration
    // ---------------------------------------------------------------------------

    /**
     * @brief Represents an individual entry in the peer ID table.
     * @ingroup lookups_peer
     */
    class HOST_SW_API PeerId {
    public:
        /**
         * @brief Initializes a new instance of the PeerId class.
         */
        PeerId() :
            m_peerId(0U),
            m_peerAlias(),
            m_peerPassword(),
            m_peerReplica(false),
            m_canRequestKeys(false),
            m_canIssueInhibit(false),
            m_hasCallPriority(false),
            m_jitterBufferEnabled(false),
            m_jitterBufferMaxSize(4U),
            m_jitterBufferMaxWait(40000U),
            m_peerDefault(false)
        {
            /* stub */
        }
        /**
         * @brief Initializes a new instance of the PeerId class.
         * @param peerId Peer ID.
         * @param peerAlias Peer alias
         * @param peerPassword Per Peer Password.
         * @param sendConfiguration Flag indicating this peer participates in peer link and should be sent configuration.
         * @param peerDefault Flag indicating this is a "default" (i.e. undefined) peer.
         */
        PeerId(uint32_t peerId, const std::string& peerAlias, const std::string& peerPassword, bool peerDefault) :
            m_peerId(peerId),
            m_peerAlias(peerAlias),
            m_peerPassword(peerPassword),
            m_peerReplica(false),
            m_canRequestKeys(false),
            m_canIssueInhibit(false),
            m_hasCallPriority(false),
            m_jitterBufferEnabled(false),
            m_jitterBufferMaxSize(4U),
            m_jitterBufferMaxWait(40000U),
            m_peerDefault(peerDefault)
        {
            /* stub */
        }

        /**
         * @brief Equals operator. Copies this PeerId to another PeerId.
         * @param data Instance of PeerId to copy.
         */
        PeerId& operator=(const PeerId& data)
        {
            if (this != &data) {
                m_peerId = data.m_peerId;
                m_peerAlias = data.m_peerAlias;
                m_peerPassword = data.m_peerPassword;
                m_peerReplica = data.m_peerReplica;
                m_canRequestKeys = data.m_canRequestKeys;
                m_canIssueInhibit = data.m_canIssueInhibit;
                m_hasCallPriority = data.m_hasCallPriority;
                m_jitterBufferEnabled = data.m_jitterBufferEnabled;
                m_jitterBufferMaxSize = data.m_jitterBufferMaxSize;
                m_jitterBufferMaxWait = data.m_jitterBufferMaxWait;
                m_peerDefault = data.m_peerDefault;
            }

            return *this;
        }

        /**
         * @brief Sets flag values.
         * @param peerId Peer ID.
         * @param peerAlias Peer Alias
         * @param peerPassword Per Peer Password.
         * @param sendConfiguration Flag indicating this peer participates in peer link and should be sent configuration.
         * @param peerDefault Flag indicating this is a "default" (i.e. undefined) peer.
         */
        void set(uint32_t peerId, const std::string& peerAlias, const std::string& peerPassword, bool peerDefault)
        {
            m_peerId = peerId;
            m_peerAlias = peerAlias;
            m_peerPassword =  peerPassword;
            m_peerDefault = peerDefault;
        }

        /**
         * @brief Sets jitter buffer parameters.
         * @param maxWait Maximum wait time in microseconds.
         * @param maxSize Maximum buffer size in frames.
         * @param enabled Jitter buffer enabled flag.
         */
        void setJitterBuffer(uint32_t maxWait, uint16_t maxSize, bool enabled)
        {
            m_jitterBufferMaxWait = maxWait;
            m_jitterBufferMaxSize = maxSize;
            m_jitterBufferEnabled = enabled;

            // clamp jitter buffer parameters
            if (m_jitterBufferMaxSize < MIN_JITTER_MAX_SIZE)
                m_jitterBufferMaxSize = MIN_JITTER_MAX_SIZE;
            if (m_jitterBufferMaxSize > MAX_JITTER_MAX_SIZE)
                m_jitterBufferMaxSize = MAX_JITTER_MAX_SIZE;
            if (m_jitterBufferMaxWait < MIN_JITTER_MAX_WAIT)
                m_jitterBufferMaxWait = MIN_JITTER_MAX_WAIT;
            if (m_jitterBufferMaxWait > MAX_JITTER_MAX_WAIT)
                m_jitterBufferMaxWait = MAX_JITTER_MAX_WAIT;
        }

    public:
        /**
         * @brief Peer ID.
         */
        DECLARE_PROPERTY_PLAIN(uint32_t, peerId);
        /**
         * @brief Peer Alias
         */
        DECLARE_PROPERTY_PLAIN(std::string, peerAlias);
        /**
         * @brief Per Peer Password.
         */
        DECLARE_PROPERTY_PLAIN(std::string, peerPassword);
        /**
         * @brief Flag indicating if the peer participates in peer replication and should be sent configuration.
         */
        DECLARE_PROPERTY_PLAIN(bool, peerReplica);
        /**
         * @brief Flag indicating if the peer can request encryption keys.
         */
        DECLARE_PROPERTY_PLAIN(bool, canRequestKeys);
        /**
         * @brief Flag indicating if the peer can issue inhibit/uninhibit packets.
         */
        DECLARE_PROPERTY_PLAIN(bool, canIssueInhibit);
        /**
         * @brief Flag indicating if the peer has call transmit priority.
         */
        DECLARE_PROPERTY_PLAIN(bool, hasCallPriority);

        /**
         * @brief Jitter buffer enabled flag.
         */
        DECLARE_PROPERTY_PLAIN(bool, jitterBufferEnabled);
        /**
         * @brief Maximum buffer size in frames.
         */
        DECLARE_PROPERTY_PLAIN(uint16_t, jitterBufferMaxSize);
        /**
         * @brief Maximum wait time in microseconds.
         */
        DECLARE_PROPERTY_PLAIN(uint32_t, jitterBufferMaxWait);

        /**
         * @brief Flag indicating if the peer is default.
         */
        DECLARE_RO_PROPERTY_PLAIN(bool, peerDefault);
    };

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

    /**
     * @brief Implements a threading lookup table class that contains peer ID
     *  lookup table.
     * @ingroup lookups_peer
     */
    class HOST_SW_API PeerListLookup : public LookupTable<PeerId> {
    public:
        /**
         * @brief Initializes a new instance of the PeerListLookup class.
         * @param filename Full-path to the list file.
         * @param reloadTime Interval of time to reload the lookup table.
         * @param peerAcl Flag indicating these rules are enabled for enforcing access control.
         * @param verbose Flag indicating if logging should be enabled for this lookup table.
         */
        PeerListLookup(const std::string& filename, uint32_t reloadTime, bool peerAcl, bool verbose = true);

        /**
         * @brief Clears all entries from the list.
         */
        void clear() override;

        /**
         * @brief Adds a new entry to the list.
         * @param id Unique peer ID to add.
         * @param entry Peer ID entry to add.
         */
        void addEntry(uint32_t id, PeerId entry);
        /**
         * @brief Removes an existing entry from the list.
         * @param id Unique peer ID to remove.
         */
        void eraseEntry(uint32_t id);
        /**
         * @brief Finds a table entry in this lookup table.
         * @param id Unique identifier for table entry.
         * @returns PeerId Table entry.
         */
        PeerId find(uint32_t id) override;

        /**
         * @brief Commit the table.
         * @param quiet Disable logging during save operation.
         */
        void commit(bool quiet = false);

        /**
         * @brief Gets whether the lookup is enabled.
         * @returns True, if this lookup table is enabled, otherwise false.
         */
        bool getACL() const;

        /**
         * @brief Checks if a peer ID is in the list.
         * @param id Unique peer ID to check.
         * @returns bool True, if the peer ID is in the list, otherwise false.
         */
        bool isPeerInList(uint32_t id) const;
        /**
         * @brief Checks if a peer ID is allowed based on the mode and enabled flag.
         * @param id Unique peer ID to check.
         * @returns bool True, if the peer ID is allowed, otherwise false.
         */
        bool isPeerAllowed(uint32_t id) const;

        /**
         * @brief Checks if the peer list is empty.
         * @returns bool True, if list is empty, otherwise false.
         */
        bool isPeerListEmpty() const { return m_table.size() == 0U; }

        /**
         * @brief Gets the entire peer ID table.
         * @returns std::unordered_map<uint32_t, PeerId> 
         */
        std::unordered_map<uint32_t, PeerId> table() const { return m_table; }
        /**
         * @brief Gets the entire peer ID table.
         * @returns std::vector<PeerId> 
         */
        std::vector<PeerId> tableAsList() const;

    protected:
        bool m_acl;

        bool m_verbose;

        /**
         * @brief Loads the table from the passed lookup table file.
         * @return True, if lookup table was loaded, otherwise false.
         */
        bool load() override;

        /**
         * @brief Saves the table to the passed lookup table file.
         * @param quiet Disable logging during save operation.
         * @return True, if lookup table was saved, otherwise false.
         */
        bool save(bool quiet = false) override;

    private:
        static std::mutex s_mutex;  //!< Mutex used for change locking.
        static bool s_locked;       //!< Flag used for read locking (prevents find lookups), should be used when atomic operations (add/erase/etc) are being used.
    };
} // namespace lookups

#endif // __PEER_LIST_LOOKUP_H__