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

//! \file DtCDBPager.h
//! \brief Manage the CDB scene graph node creation for CDB models (Geospecific and Geotypical)
//! \ingroup osgdb_osgearth_model_cdb
#pragma once

#include <Cee_CdbDB.h>

#include <osgEarth/MapNodeObserver>
#include <osgEarth/Map>

#include <osgdb_osgearth_model_cdb/DtCDBGroupNode.h>
#include <osgdb_osgearth_model_cdb/DtCDBModelRangeTable.h>
#include <osgdb_osgearth_model_cdb/DtCDBModelOptions.h>

#include <osgdb_osgearth_model_cdb/DtCDBVectorModel.h>

#include <osgearth_cdb_utils/DtCDBTileCache.h>
#include <osgearth_cdb_utils/DtCDBTile.h>
#include <osgearth_cdb_utils/DtCDBDatabase.h>
#include <osgearth_cdb_utils/DtCDBDbfConfig.h>
#include <osgearth_cdb_utils/DtCDBUtils.h>

CEE_NAMESPACE_BEGIN_DECL(CEE_NAMESPACE_CDBAPI)
class VectFile;
class VectAttribute;
class VectFeature;
CEE_NAMESPACE_END_DECL(CEE_NAMESPACE_CDBAPI)

namespace osgDB
{
   class Options;
}

namespace makVrv 
{
   class DtDe;

namespace oe { namespace CDB
{
   class CDBTileKey;
   class CDBTileVerbose;
   class CDBModelOptions;
   class DtCDBClassIndex;
   
   //! \brief Create the CDB scene graph nodes 
   //! One instance of this class will be instantiate for every CDB model 
   //! dataset entry in .earth file (Created by the CDBModelSource class)
   //! \ingroup osgdb_osgearth_model_cdb
   class CDBPager : public osg::Group, public osgEarth::MapNodeObserver
   {
   public:

      //! \brief  CTOR
      CDBPager( const osgEarth::Map* map, const CDBModelOptions& CdbModelOptions, const osgDB::Options* dbOptions,
         osgEarth::SceneGraphCallbacks* sgCallbacks, DtDe& de );

      //! \brief  DTOR
      virtual ~CDBPager();

      //! \brief  Build the root nodes for every CDB model dataset 
      //! may be called a second 
      void build();

      //! \brief  Initialized every CDB model dataset (once)
      void initialize();

      //! \brief  Gets the map node from its observer reference
      virtual osgEarth::MapNode* getMapNode();

      //! \brief  Set the map node of the CDB pager 
      //! (not set by osgearth anymore)
      virtual void setMapNode( osgEarth::MapNode* mapNode );

      //virtual void setMap(const osgEarth::Map* map);

      //! \brief   Get the CDB min LOD
      //int getMinLevel() const { return myMinLevel; }

      //! \brief   Set the CDB min LOD
      //void setMinLevel(int minLevel) { myMinLevel = minLevel; }

      //! \brief   Get the CDB max LOD
      //int getMaxLevel() const { return myMaxLevel; }

      //! \brief   Set the CDB max LOD
      //void setMaxLevel(int maxLevel) { myMaxLevel = maxLevel; }

      //! \brief   TODO
      //float getPriorityScale() const { return myPriorityScale; }

      //! \brief   TODO
      //void setPriorityScale(float value) { myPriorityScale = value; }

      //! \brief   TODO
      //float getPriorityOffset() const { return myPriorityOffset; }

      //! \brief   TODO
      //void setPriorityOffset(float value) { myPriorityOffset = value; }

      //! \brief   Set the file location callback (needed for speed tree and light points)
      void setFileLocationCallback( osgDB::FileLocationCallback* cb )
      {
         myFileLocationCallback = cb;
      }

      //! \brief   Get the file location callback 
      osgDB::FileLocationCallback* getFileLocationCallback() const
      {
         return myFileLocationCallback.get();
      }

      //! \brief   Enable or disable the cancellation of cdb tiles by osg earth while they are getting built
      void setEnableCancellation( bool value );

      //! \brief   return the cancellation of cdb tiles flag
      bool getEnableCancellation() const;

      //! \brief   Creates a node for the given TileKey.  Doesn't do anything with paging, just gets the raw data.
      //!  Subclasses override this method to provide their data for TileKeys
      virtual osg::ref_ptr<CdbGroupNode> createCDBGroupNode( const CDBTileKey& key, DtCdbGroupNodeProgress* progress, CdbGroupNode* parentGroup, bool& CdbtileFound, bool& hasChildren );

      //! \brief   Callback structure for pager events
      struct Callback : public osg::Referenced
      {
         //! \brief   Called after a new tile is created, but before it's merged into the live
         //! scene graph. Runs in a pager thread.
         virtual void onCreateNode( const CDBTileKey& key, osg::Node* node )
         {
         }
      };

      //! \brief   Adds a pager callback. 
      void addCallback( Callback* callback );

      //! \brief   Removes a pager callback. 
      void removeCallback( Callback* callback );

      //! \brief   traverse function
      void traverse( osg::NodeVisitor& nv );

   public:
      //! \brief   Loads the PagedLOD hierarchy for this key.
      osg::Node* loadKey( CDBTileKey key, DtPagedLodFrameNumberCallback* progress, CdbGroupNode* parentGroup );

   protected:

      //! \brief   The root node is a special node.
      osg::Node* buildRootNode();

      //! \brief   Creates a PagedLOD for the given TileKEy
      osg::PagedLOD* createPagedNode( const CDBTileKey& key, DtCdbGroupNodeProgress* progress, CdbGroupNode* parentGroup );

      //! \brief   Read the CDB tile shapefiles and load the models
      bool loadCdbTileModels( const CDBTileKey& key, DtCdbGroupNodeProgress* progress, CdbGroupNode* groupNode, bool& CdbtileFound ) const;

      //! \brief Check is the LOD 0 of the dataset exist (because some CDB database exist,
      //! that are not CDB compliant and the LC folder does not exist. We still want to 
      //! try to load these CDB)
      inline bool doesLod0Exist( const CDBTileVerbose& verboseKey, int CdbDatasetCode, int CdbComponentSelector1, int CdbComponentSelector2 ) const;

      //! \brief Return the tile quadrant based on the key uref and rref 
      inline Quadrant getQuadrant( const CDBTileKey& key ) const;

      //! \brief write cdb tile to cache
      bool writeTileToCache( const std::string& cacheKey, CdbGroupNode* node );

      //! \brief read cdb tile from cache
      CdbGroupNode* readTileFromCache( const CDBTileKey& key, const std::string& cacheKey );

      //! \brief  Initialize vector pointer for current dataset in a thread safe matter
      void initializeVector( const std::string& instanceshp, DtCDBInstanceIndex& instanceAttrIndexes,
         const std::string& classdbf, DtCDBClassIndex& classAttrIndexes,
         const std::string& extendeddbf ) const;

      //! \brief   CDB min lod (-10)
      int myMinLevel;

      //! \brief   CDB max lod (23)
      //! Could eventually set to limit max lod
      int myMaxLevel;

      //! \brief   Main thread framestamp tracker
      osg::ref_ptr< DtSceneGraphFrameNumberCallback > mySceneGraphFrameNumberCallback;

      //! \brief   File location callback (needed for speed tree and light points)
      osg::ref_ptr< osgDB::FileLocationCallback > myFileLocationCallback;

      //! \brief   Osg Paged Lod priority scale value
      float myPriorityScale;

      //! \brief   Osg Paged Lod priority offset value
      float myPriorityOffset;

      //! \brief   TODO
      bool myCanCancel;

      //! \brief   Spatial reference pointer geocentric
      osg::ref_ptr< osgEarth::SpatialReference > myWgs84;

      //! \brief Spatial reference pointer (flat earth projection)
      osg::ref_ptr< osgEarth::SpatialReference >  mySrs;

      //! \brief Mutex object to allow thread safety
      mutable osgEarth::Threading::Mutex myMutex;

      //! \brief   Callback ref pointer vector typedef
      typedef std::vector< osg::ref_ptr<Callback> > Callbacks;

      //! \brief   callback vector
      Callbacks myCallbacks;

      //! \brief   Model scene graph callback 
      osg::ref_ptr< osgEarth::SceneGraphCallbacks > mySGCallbacks;

      //! \brief   observer pointer on the map node
      osg::observer_ptr<osgEarth::MapNode> myMapNode;
      //osg::observer_ptr<const osgEarth::Map> myMap;

      //! \brief create node based on CDB key
      void fire_onCreateNode( const CDBTileKey& key, osg::Node* node );

      //! \brief CDB Tile cache object
      osg::ref_ptr<CDBTileCache>    myTileCache;

      //! \brief CDB option class
      const CDBModelOptions myCdbModelOptions;

      //! \brief  Database option
      osg::ref_ptr<osgDB::Options>  myDbOptions;

      //! \brief Table with LOd switch paging distance 
      DtCDBModelRangeTable myDefaultModelRangeTable;

      //! \brief Possible list of map that can contain specific paging values for specific datasets
      DatasetsModelRangeTable myDatasetsModelRangeTable;

      //! \brief Current CDB Dataset of this pager
      DatasetInfo myCurrentDataset;

      //! \brief CDB Tile utility object
      CDBTile* myCdbTilePtr;

      //! \brief CDB database object
      mutable osg::ref_ptr<DtCDBDatabase> myCdbDatabase;

      //! \brief CDB Dataset information manager
      CDBDatasets* myCDBDatasets;

      //! \brief Keep the black list filename number so that 
      //! osgearth does not try to re-create the non existing CDB tile
      unsigned  int myLastNumBlacklistedFilenames;

      //! \brief Cache path for this dataset
      std::string myCachePath;

      //! \brief appData path set by cmdline or env variable
      std::string myAppDataPath;

      //! \brief Manage the processing (read dbf, shp, flt and textures) of shapefiles
      bool processShapeFile( VectFile* shpVector
         , const std::string& instanceShp
         , DtCDBInstanceIndex& instanceAttrIndexes
         , const std::string& classdbf
         , DtCDBClassIndex& classAttrIndexes
         , CdbGroupNode* groupNode
         , DtCdbGroupNodeProgress* progress
         , bool& wasModelFound ) const;


      //! \brief Static class with predefine information
      static const DtCDBDbfConfig myConfig;
      //! \brief Number of cache reads
      Threading::Atomic myCacheReads;
      //! \brief Number of cache hits
      Threading::Atomic myCacheHits;
      //! \brief Pointer to cache object that read/write the nodes
      osg::ref_ptr<CacheBin> myCacheBin;
      //! \brief Cache policy set in the earth file
      optional<CachePolicy> myPolicy;
      //! \brief pointer to manage different types of vector object
      osg::ref_ptr<DtCDBVectorModel> myVector;

      DtDe& myDe;

      bool myMapNodeRequired;
   };

} } } // namespace makVrv::oe::CDB