/* osgEarth
* Copyright 2025 Pelican Mapping
* MIT License
*/
#ifndef OSGEARTH_SCREEN_SPACE_LAYOUT_IMPL_H
#define OSGEARTH_SCREEN_SPACE_LAYOUT_IMPL_H 1

#include <osgEarth/ScreenSpaceLayout>
#include <osgEarth/Containers>
#include <osgUtil/RenderBin>

namespace osgEarth { namespace Internal
{
    // Sort wrapper to satisfy the template processor.
    struct SortContainer
    {
        SortContainer(DeclutterSortFunctor& f) : _f(f) { }
        const DeclutterSortFunctor& _f;
        bool operator()(const osgUtil::RenderLeaf* lhs, const osgUtil::RenderLeaf* rhs) const
        {
            return _f(lhs, rhs);
        }
    };

    // Custom sorting functor that sorts drawables front-to-back, and when drawables share the
    // same parent Geode, sorts them in traversal order.
    struct SortFrontToBackPreservingGeodeTraversalOrder
    {
        bool operator()(const osgUtil::RenderLeaf* lhs, const osgUtil::RenderLeaf* rhs) const
        {
            if (lhs->getDrawable()->getNumParents() > 0 &&
                rhs->getDrawable()->getNumParents() > 0 &&
                rhs->getDrawable()->getParent(0) == lhs->getDrawable()->getParent(0))
            {
                const osg::Group* parent = static_cast<const osg::Group*>(lhs->getDrawable()->getParent(0));
                return parent->getChildIndex(lhs->getDrawable()) > parent->getChildIndex(rhs->getDrawable());
            }
            else
            {
                return (lhs->_depth < rhs->_depth);
            }
        }
    };

    // Custom sorting functor that sorts drawables by Priority, and when drawables share the
    // same parent Geode, sorts them in traversal order.
    struct SortByPriorityPreservingGeodeTraversalOrder : public DeclutterSortFunctor
    {
        bool operator()(const osgUtil::RenderLeaf* lhs, const osgUtil::RenderLeaf* rhs) const
        {
            if (lhs->getDrawable()->getNumParents() > 0 &&
                rhs->getDrawable()->getNumParents() > 0 &&
                rhs->getDrawable()->getParent(0) == lhs->getDrawable()->getParent(0))
            {
                const osg::Group* parent = static_cast<const osg::Group*>(lhs->getDrawable()->getParent(0));
                return parent->getChildIndex(lhs->getDrawable()) > parent->getChildIndex(rhs->getDrawable());
            }

            else
            {
                const ScreenSpaceLayoutData* lhsdata = dynamic_cast<const ScreenSpaceLayoutData*>(lhs->getDrawable()->getUserData());
                float lhsPriority = lhsdata ? lhsdata->_priority : 0.0f;

                const ScreenSpaceLayoutData* rhsdata = dynamic_cast<const ScreenSpaceLayoutData*>(rhs->getDrawable()->getUserData());
                float rhsPriority = rhsdata ? rhsdata->_priority : 0.0f;

                float diff = lhsPriority - rhsPriority;

                if (diff != 0.0f)
                    return diff > 0.0f;

                // first fallback on depth:
                diff = lhs->_depth - rhs->_depth;
                if (diff != 0.0f)
                    return diff < 0.0f;

                // then fallback on traversal order.
                diff = float(lhs->_traversalOrderNumber) - float(rhs->_traversalOrderNumber);
                return diff < 0.0f;
            }
        }
    };

    // Data structure shared across entire layout system.
    /*internal*/
    struct ScreenSpaceLayoutContext : public osg::Referenced
    {
        ScreenSpaceLayoutOptions _options;
        bool _debug;

        ScreenSpaceLayoutContext()
        {
            _debug = (::getenv("OSGEARTH_DECLUTTER_DEBUG") != nullptr);
        }
    };

} }


#endif