//----------------------------------------------------------------------------
//
// TSDuck - The MPEG Transport Stream Toolkit
// Copyright (c) 2025-2026, Piotr Serafin
// BSD-2-Clause license, see LICENSE.txt file or https://tsduck.io/license
//
//----------------------------------------------------------------------------
//!
//!  @file
//!  Representation of a DSM-CC User-to-Network Message Table.
//!
//----------------------------------------------------------------------------

#pragma once
#include "tsAbstractLongTable.h"
#include "tsDSMCC.h"
#include "tsDSMCCTap.h"
#include "tsDSMCCCompatibilityDescriptor.h"
#include "tsDSMCCIOR.h"
#include "tsDSMCCGroupInfoIndication.h"

namespace ts {
    //!
    //! Representation of a DSM-CC User-to-Network Message Table (DownloadServerInitiate, DownloadInfoIndication).
    //! @see ISO/IEC 13818-6, 9.2.2, 7.3.2, 7.3.6
    //! @see ETSI TR 101 202, A.1, A.3, A.4, B.
    //! @ingroup libtsduck table
    //!
    class TSDUCKDLL DSMCCUserToNetworkMessage: public AbstractLongTable
    {
    public:
        //!
        //! Representation of DSM-CC Message Header structure
        //! @see ETSI TR 101 202 V1.2.1 (2003-01), A.1
        //!
        class TSDUCKDLL MessageHeader
        {
            TS_DEFAULT_COPY_MOVE(MessageHeader);

        public:
            // DSMCCUserToNetworkMessage public members:
            uint8_t protocol_discriminator = DSMCC_PROTOCOL_DISCRIMINATOR;  //!< Indicates that the message is MPEG-2 DSM-CC message.
            uint8_t dsmcc_type = DSMCC_TYPE_DOWNLOAD_MESSAGE;               //!< Indicates type of MPEG-2 DSM-CC message.
            uint16_t message_id = 0;                                        //!< Indicates type of message which is being passed.
            uint32_t transaction_id = 0;                                    //!< Used for session integrity and error processing.

            //!
            //! Default constructor.
            //!
            MessageHeader() = default;

            //!
            //! Clear values.
            //!
            void clear();
        };

        //  *****************************************
        //  *** DownloadServerInitiate Structure  ***
        //  *****************************************
        //!
        //! DSM-CC DownloadServerInitiate (DSI) message body.
        //!
        struct TSDUCKDLL DownloadServerInitiate {
            ByteBlock server_id {};                 //!< Field shall be set to 20 bytes with the value 0xFF.
            DSMCCIOR ior {};                        //!< IOR structure (object carousel; populated when type_id == "srg").
            DSMCCGroupInfoIndication group_info {}; //!< GroupInfoIndication (data carousel; populated otherwise).

            //!
            //! Reset the DSI body to its default state.
            //!
            void clear()
            {
                server_id.clear();
                ior.clear();
                group_info.clear();
            }
        };

        //  *****************************************
        //  *** DownloadInfoIndication Structure  ***
        //  *****************************************
        //!
        //! DSM-CC DownloadInfoIndication (DII) message body.
        //!
        struct TSDUCKDLL DownloadInfoIndication {

            uint32_t download_id = 0;  //!< Same value as the downloadId field of the DownloadDataBlock() messages which carry the Blocks of the Module.
            uint16_t block_size = 0;   //!< Block size of all the DownloadDataBlock() messages which convey the Blocks of the Modules.

            //!
            //! Representation of BIOP::ModuleInfo structure
            //! @see ETSI TR 101 202 V1.2.1 (2003-01), Table 4.14
            //!
            class TSDUCKDLL Module: public EntryWithDescriptors
            {
                TS_NO_DEFAULT_CONSTRUCTORS(Module);
                TS_DEFAULT_ASSIGMENTS(Module);

            public:
                uint16_t module_id = 0;       //!< Identifies the module.
                uint32_t module_size = 0;     //!< Length of the module in bytes.
                uint8_t module_version = 0;   //!< Identifies the version of the module.
                uint32_t module_timeout = 0;  //!< Time out value in microseconds that may be used to time out the acquisition of all Blocks of the Module.
                uint32_t block_timeout = 0;   //!< Time out value in microseconds that may be used to time out the reception of the next Block after a Block has been acquired.
                uint32_t min_block_time = 0;  //!< Indicates the minimum time period that exists between the delivery of two subsequent Blocks of the Module.
                std::list<DSMCCTap> taps {};  //!< List of Taps.

                //!
                //! Constructor.
                //! @param [in] table Parent DSMCCUserToNetworkMessage Table.
                //!
                explicit Module(const AbstractTable* table);
            };

            //!
            //! List of Modules
            //!
            using ModuleList = AttachedEntryList<Module>;

            ModuleList modules;  //!< List of modules structures.

            //!
            //! Constructor.
            //! @param [in] parent Parent DSMCCUserToNetworkMessage table.
            //!
            explicit DownloadInfoIndication(DSMCCUserToNetworkMessage* parent) : modules(parent) {}
            DownloadInfoIndication(const DownloadInfoIndication&) = delete;
            DownloadInfoIndication& operator=(const DownloadInfoIndication&) = delete;

            //!
            //! Reset the DII body to its default state.
            //!
            void clear()
            {
                download_id = 0;
                block_size = 0;
                modules.clear();
            }
        };

        //!
        //! Discriminated union holding either a DSI or a DII body.
        //!
        using MessageBody = std::variant<std::monostate, DownloadServerInitiate, DownloadInfoIndication>;

        MessageHeader header {};                                   //!< DSM-CC Message Header.
        DSMCCCompatibilityDescriptor compatibility_descriptor {};  //!< DSM-CC compatibilityDescriptor.
        MessageBody body;                                          //!< Message body, either DSI or DII.


        //! @return True if the body is a DSI.
        bool isDSI() const { return std::holds_alternative<DownloadServerInitiate>(body); }
        //! @return True if the body is a DII.
        bool isDII() const { return std::holds_alternative<DownloadInfoIndication>(body); }

        //! @return Pointer to the DSI body, or null if the body is not a DSI.
        DownloadServerInitiate* toDSI() { return std::get_if<DownloadServerInitiate>(&body); }
        //! @return Const pointer to the DSI body, or null if the body is not a DSI.
        const DownloadServerInitiate* toDSI() const { return std::get_if<DownloadServerInitiate>(&body); }

        //! @return Pointer to the DII body, or null if the body is not a DII.
        DownloadInfoIndication* toDII() { return std::get_if<DownloadInfoIndication>(&body); }
        //! @return Const pointer to the DII body, or null if the body is not a DII.
        const DownloadInfoIndication* toDII() const { return std::get_if<DownloadInfoIndication>(&body); }

        //!
        //! Default constructor.
        //! @param [in] vers Table version number.
        //! @param [in] cur True if table is current, false if table is next.
        //!
        DSMCCUserToNetworkMessage(uint8_t vers = 0, bool cur = true);

        //!
        //! Copy constructor.
        //! @param [in] other Other table to copy.
        //!
        DSMCCUserToNetworkMessage(const DSMCCUserToNetworkMessage& other);

        //!
        //! Assignment operator.
        //! @param [in] other Other table to copy.
        //! @return A reference to this object.
        //!
        DSMCCUserToNetworkMessage& operator=(const DSMCCUserToNetworkMessage& other);

        //!
        //! Constructor from a binary table.
        //! @param [in,out] duck TSDuck execution context.
        //! @param [in] table Binary table to deserialize.
        //!
        DSMCCUserToNetworkMessage(DuckContext& duck, const BinaryTable& table);

        // Inherited methods
        DeclareDisplaySection();
        virtual bool isPrivate() const override;
        virtual uint16_t tableIdExtension() const override;

    protected:
        // Inherited methods
        virtual void clearContent() override;
        virtual size_t maxPayloadSize() const override;
        virtual void serializePayload(BinaryTable&, PSIBuffer&) const override;
        virtual void deserializePayload(PSIBuffer&, const Section&) override;
        virtual void buildXML(DuckContext&, xml::Element*) const override;
        virtual bool analyzeXML(DuckContext& duck, const xml::Element* element) override;

    private:
        static constexpr size_t MESSAGE_HEADER_SIZE = 12;  //!< DSM-CC Message Header size w/o adaptation header.
    };
}  // namespace ts
