/******************************************************************************
** Copyright(c) 2025 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/

//! \file DtCDBGeoSpecificIndirectGeo.h
//! \brief DtCDBGeoSpecificIndirectGeo
//! \ingroup osgdb_osgearth_model_cdb
#pragma once

#include <osgdb_osgearth_model_cdb/DtCDBGroupNode.h>

#include <osgearth_cdb_utils/DtCDBTileKey.h>

#include <vrvOsg/DtOsgThreadingUtils.h>

#include <vrvIndirectRendering/DtModelInfo.h>

#include <osg/Vec3d>
#include <osg/ref_ptr>

#include <tbb/concurrent_hash_map.h>
#include <atomic>

namespace makVrv 
{
   class DtIndirectGeometry;
   class DtIndirectGeometryRegistrar;
   class DtDe;
   namespace oe 
   {
      namespace CDB 
      {
         class CdbGroupNode;
         class CDBTileKey;
         class DtCDBTextureCollectionPolicy;

#define DTCDB_GEO_SPECIFIC_INDIRECT_GEO_DEBUG 0

         //! We have 1 indirect geometry representing all gs 'instances' in each cell
         //! We also have 1 registrar per tile. The (reasonable) assumption here
         //! is that (in general) models in a geo cell are (in general) unique
         class DtCDBGeoSpecificIndirectGeo
         {
         public:
            //! Constructor
            DtCDBGeoSpecificIndirectGeo(DtDe& de, bool supportIntersections);

            //! Constructor when using GS model disk read optimisations
            //! Registrars are shared for an entire tile
            DtCDBGeoSpecificIndirectGeo( DtDe& de, bool supportIntersections, DtIndirectGeometryRegistrar* indirectRegistrar,
               std::unordered_map<std::string, DtModelInfo*>* modelsToModelsInfo, unsigned int* nextModelId );
            //! Destructor
            virtual ~DtCDBGeoSpecificIndirectGeo();

         private:
            //! not allowed
            DtCDBGeoSpecificIndirectGeo() = delete;
            //! not allowed
            DtCDBGeoSpecificIndirectGeo(const DtCDBGeoSpecificIndirectGeo& other) = delete;
            //! not allowed
            DtCDBGeoSpecificIndirectGeo& operator=(const DtCDBGeoSpecificIndirectGeo& other) = delete;

         public:
            //! add a geospecific point feature to the indirect system
            virtual void addGSPoint(
               const osg::Matrixd& instanceXForm,
               const std::string& modelName,
               const std::string& modelFileName,
               osg::ref_ptr<osg::Node> modelNode,
               int lodId,
               osg::ref_ptr<osgDB::Options> dbOptions,
               osg::ref_ptr<CdbGroupNode> groupNode
            );

            //! Set the parent node for this indirect geo cell
            virtual void setParent( osg::ref_ptr<CdbGroupNode> parent);

            //! Get the bounding box for this geo cell which is the extents of all geospecific points added to this cell
            virtual const osg::BoundingBoxd& boundingBox(void) const;

            //! Get the indirect geometry associated with this geo cell.
            virtual DtIndirectGeometry* indirectGeometry(void);

            //! Get the indirect registrar associated with this geo cell.
            virtual DtIndirectGeometryRegistrar* indirectRegistrar(); 
         protected:

            //! increase the size of the bounding box for the geo cell based on a particular model and transform into world space
            virtual void expandBoundingBox( osg::ref_ptr<osg::Node> modelNode, const osg::Matrixd& instanceXForm);

            //! print the information about this geo cell, which is how many lods each model has
            virtual void printInfo(void);

            //! Called by both constructors
            virtual void init();
         protected:
            osg::ref_ptr<DtIndirectGeometry> myIndirectGeometry;
            osg::ref_ptr<DtIndirectGeometryRegistrar> myIndirectGeometryRegistrar;

            osg::ref_ptr<osg::Group> myIndirectGeoIntersectionGroup;

            using ModelsToModelInfo = std::unordered_map<std::string, DtModelInfo*> ;
            ModelsToModelInfo* myModelsToModelInfo;

            unsigned int* myNextModelId;

            osg::BoundingBoxd myBoundingBox;

            mutable Threading::Mutex myBoundingBoxMutex;

            DtDe& myDe;

            std::atomic<int> myNumInstances;

            const bool mySupportIntersections = true;

            bool myOwnsModelInfos;
         };
      }
   }
}