//----------------------------------------------------------------------------
//
// TSDuck - The MPEG Transport Stream Toolkit
// Copyright (c) 2005-2026, Piotr Serafin
// BSD-2-Clause license, see LICENSE.txt file or https://tsduck.io/license
//
//----------------------------------------------------------------------------
//!
//!  @file
//!  DSM-CC carousel extractor (library-level driver).
//!
//----------------------------------------------------------------------------

#pragma once
#include "tsDSMCCCarousel.h"
#include "tsSectionDemux.h"
#include "tsSectionHandlerInterface.h"
#include "tsTSPacket.h"
#include <filesystem>
#include <optional>

namespace ts {

    //!
    //! Drives a DSM-CC Object Carousel extraction end-to-end on a single PID.
    //!
    //! Holds the section demux, the carousel engine and the output policy
    //! (what to write, where). Callers feed raw TS packets via feedPacket();
    //! the extractor parses sections, assembles modules, resolves BIOP object
    //! paths and writes files to disk. Shared between the `dsmcc` plugin and
    //! the `tsdsmcc` standalone tool.
    //!
    //! @ingroup libtsduck mpeg
    //!
    class TSDUCKDLL DSMCCExtractor : private SectionHandlerInterface
    {
        TS_NOCOPY(DSMCCExtractor);

    public:
        //!
        //! Extraction policy.
        //!
        struct Options {
            UString out_dir {};          //!< Directory where carousel files are written. Required unless list_mode.
            bool    list_mode = false;   //!< If true, parse and report but write nothing.
            bool    dump_modules = false;//!< If true, also dump raw assembled module payloads under out_dir/modules/download_id/.
            bool    data_carousel = false;//!< If true, skip BIOP parsing and write each module as out_dir/module_XXXX.bin.
        };

        //!
        //! Constructor.
        //! @param [in,out] duck TSDuck execution context.
        //! @param [in] options Extraction policy.
        //!
        DSMCCExtractor(DuckContext& duck, const Options& options);

        //!
        //! Attach the section demux to the carousel PID.
        //! @param [in] pid PID carrying the DSM-CC sections.
        //!
        void setPID(PID pid);

        //!
        //! Feed one TS packet through the demux.
        //! @param [in] pkt The packet.
        //!
        void feedPacket(const TSPacket& pkt);

        //!
        //! Flush deferred BIOP objects and, in list_mode, print the summary.
        //!
        void flush();

    private:
        struct ObjectEntry {
            uint32_t    download_id = 0;
            uint16_t    module_id = 0;
            UString     path {};
            std::string kind {};
            size_t      size = 0;
        };

        using ObjectList = std::vector<ObjectEntry>;
        using ObjectByModuleMap = std::map<std::pair<uint32_t, uint16_t>, std::vector<size_t>>;

        DuckContext&        _duck;
        Options             _options;
        DSMCCCarousel       _carousel;
        SectionDemux        _demux;           //!< Section-level demux (no table assembly, avoids DDB inconsistency noise).
        PID                 _pid = PID_NULL;
        ObjectList          _objects {};
        ObjectByModuleMap   _objects_by_module {};

        virtual void handleSection(SectionDemux&, const Section&) override;

        void onModuleCompleted(uint32_t download_id, uint16_t module_id, const ByteBlock& payload);

        void onObjectReady(uint32_t download_id, uint16_t module_id, const UString& name, const BIOPMessage& msg);
        void extractFile(const UString& name, const BIOPFileMessage& file);
        void extractDirectory(const UString& name);

        // Methods for rendering carousel state.
        void printListSummary();
        UString renderCarousels() const;
        UString renderGroupBlock(const DSMCCCarousel::GroupContext& gctx) const;
        UString renderModuleBlock(const DSMCCModuleAssembler::ModuleContext& ctx) const;
        UString renderObjectsForModule(uint32_t download_id, uint16_t module_id) const;
        UString renderDescriptorList(const DescriptorList& descs) const;
        UString renderOneDescriptor(size_t index, const Descriptor& raw) const;
        UString renderCompatibilityDescriptor(const DSMCCCompatibilityDescriptor& compat_desc) const;
        UString renderFileTreeFinal() const;
        UString moduleFilename(uint32_t download_id, uint16_t module_id) const;

        // Compute the on-disk (directory, leaf-name) pair for a completed module.
        std::pair<std::filesystem::path, UString> computeOutputPath(uint32_t download_id, uint16_t module_id) const;

        // Resolve a BIOP object path into the corresponding on-disk path under
        // out_dir/files.
        std::optional<std::filesystem::path> safeOutputPath(const UString& name) const;

        // Create directory (recursively); report and return false on error.
        bool ensureDir(const std::filesystem::path& dir);
    };
}  // namespace ts
