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

//! \file DtCDBStateSetCache.h
//! \brief DtCDBStateSetCache
//! \ingroup osgdb_osgearth_model_cdb

#include <osgEarth/Common>
#include <osgEarth/Threading>
#include <osg/StateSet>
#include <set>

namespace makVrv
{
   namespace oe
   {
      namespace CDB
      {
         //! Custom implementation of osgEarth::StateSetCache as it is 
         //! not virtually extensible
         class DtCDBStateSetCache : public osg::Referenced
         {
         public:
            /**
            * Constructs a new cache.
            */
            DtCDBStateSetCache();

            /**
            * Caps the size of the cache.
            */
            void setMaxSize( unsigned maxSize );

            /**
            * Check whether a StateSet is eligible for sharing.
            */
            bool eligible( osg::StateSet* stateSet ) const;

            /**
            * Check whether a StateAttribute is eligible for sharing.
            */
            bool eligible( osg::StateAttribute* attr ) const;

            void setOptimizeTextureAttributes( bool optimizeTexAtt );
            bool optimizeTextureAttributes() const;
            void setTraverseProxies( bool traverseProxies );
            bool traverseProxies() const;
            void setProtectAfterCache( bool protect );
            bool protectAfterCache() const;

            /**
            * Scans a graph and combines equivalents state attributes into
            * single shared attribute. Skips any attribute (or stateset)
            * with dynamic data variance
            */
            void consolidateStateAttributes( osg::Node* node );

            /**
            * Scans a graph and combines equivalent statesets into a single
            * shared stateset. Skips any stateset with dynamic data variance.
            */
            void consolidateStateSets( osg::Node* node );

            /**
            * Calls consolidateStateAttributes followed by consolidateStateSets.
            */
            void optimize( osg::Node* node );

            /**
            * Looks in the cache for a stateset matching the input. If found,
            * returns the cached one in output. If not found, stores the input
            * in the cache and returns the same one in output.
            *
            * Must use ref_ptrs for thread safely
            */
            bool share( osg::ref_ptr<osg::StateSet>& input );

            /**
            * Looks in the attribute cache for an attribute matching the input.
            * If found, returns the cached one in output. If not found, stores
            * the input in the cache and returns the same one in output.
            *
            * Must use ref_ptrs for thread safely
            */
            bool share( osg::ref_ptr<osg::StateAttribute>& input );

            //! marks all caches statesets as DYNAMIC so they cannot be
            //! shared again.
            void protect();

            /**
            * Clears out the cache.
            */
            void clear();

            void dumpStats();

            void releaseGLObjects( osg::State* state ) const;

            void resizeGLObjectBuffers( unsigned size );

         protected:

            virtual ~DtCDBStateSetCache();

            struct CompareStateSets
            {
               bool operator()(
                  const osg::ref_ptr<osg::StateSet>& lhs,
                  const osg::ref_ptr<osg::StateSet>& rhs ) const
               {
                  return lhs->compare( *(rhs.get()), true ) < 0;
               }
            };
            typedef std::set< osg::ref_ptr<osg::StateSet>, CompareStateSets> StateSetSet;
            StateSetSet _stateSetCache;

            struct CompareStateAttributes
            {
               bool operator()(
                  const osg::ref_ptr<osg::StateAttribute>& lhs,
                  const osg::ref_ptr<osg::StateAttribute>& rhs ) const
               {
                  return lhs->compare( *rhs.get() ) < 0;
               }
            };
            typedef std::set< osg::ref_ptr<osg::StateAttribute>, CompareStateAttributes> StateAttributeSet;
            StateAttributeSet _stateAttributeCache;

            mutable std::mutex _mutex;

            void prune();
            void pruneIfNecessary();
            unsigned _maxSize;
            unsigned _pruneCount;

            bool _protectAfterCache;
            bool _traverseProxies;
            bool _optimizeTextureState;
            //stats
            unsigned _attrShareAttempts;
            unsigned _attrsIneligible;
            unsigned _attrShareHits;
            unsigned _attrShareMisses;
         };
      }
   }
}
