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

#include <osgEarth/Common>
#include <osgEarth/TextSymbol>
#include <osgEarth/Style>
#include <osg/AutoTransform>
#include <osg/CullStack>
#include <osg/Drawable>
#include <osg/Geometry>
#include <osgText/Text>
#include <osgUtil/CullVisitor>

namespace osgEarth
{
    /**
     * Internal tools used by the annotation library.
     */
    struct OSGEARTH_EXPORT AnnotationUtils
    {
        /**
         * Convert a symbology encoding enum to an osgText enum
         */
        static osgText::String::Encoding convertTextSymbolEncoding(
            const TextSymbol::Encoding encoding);

        /**
         * Creates a drawable representing a symbolized text label in pixel space.
         * The "box" is an alignment box; all positioning and alignment of the text
         * is relative to this box. For example, if the TextSymbol contains an align
         * of RIGHT_TOP, that means the text origin is at the upper-right corner of
         * the box.
         */
        static osgText::Text* createTextDrawable(
            const std::string& text,
            const TextSymbol*  symbol,
            const BBoxSymbol*  bbox,
            const osg::BoundingBox& alignmentBox);

        static osgText::Text* createTextDrawable(
            const std::string& text,
            const TextSymbol*  symbol,
            const osg::BoundingBox& alignmentBox)
        {
            return createTextDrawable(text, symbol, 0L, alignmentBox);
        }

        static osg::Drawable* createTextDrawable(
            const std::string& text,
            const TextSymbol*  symbol,
            const osg::Vec3&   pos )
        {
            return createTextDrawable(text, symbol, 0L, osg::BoundingBox(pos,pos));
        }
        static osg::Drawable* createTextDrawable(
            const std::string& text,
            const TextSymbol*  symbol,
            const BBoxSymbol*  bbox,
            const osg::Vec3&   pos)
        {
            return createTextDrawable(text, symbol, bbox, osg::BoundingBox(pos, pos));
        }

        /**
         * Creates the basic geometry to draw an image texture-mapped to
         * a quad in pixel space.
         */
        static osg::Geometry* createImageGeometry(
            osg::Image*       image,
            const osg::Vec2s& pixelOffsets,
            unsigned          textureUnit,
            double            heading,
            double            scale );

        /**
         * Builds a graph on top of the specified that that implements a 2-pass
         * rendering scheme for self-occluding or self-intersecting geometies that
         * would not otherwise properly blend. The scheme renders the back faces
         * first, followed by the front faces, ensuring proper blending.
         */
        static osg::Node* installTwoPassAlpha( osg::Node* );

        /**
         * Checks whether using a style will require transparency blending.
         */
        static bool styleRequiresAlphaBlending( const Style& style );

        /**
         * TODO: DEPRECATE / move to app that uses this (SIMSDK)
         * Builds a sphere geometry.
         * @param r        Radius
         * @param color    Color
         * @param maxAngle Maximum angle between verts (controls tessellation)
         */
        static osg::Node* createSphere( float r, const osg::Vec4& color, float maxAngle =15.0f );

        /**
         * TODO: DEPRECATE / move to app that uses this (SIMSDK)
         * Builds a hemisphere geometry.
         * @param r        Radius
         * @param color    Color
         * @param maxAngle Maximum angle between verts (controls tessellation)
         */
        static osg::Node* createHemisphere( float r, const osg::Vec4& color, float maxAngle =15.0f );

        /**
         * Builds a box geometry
         * @param bb       Bounding box
         * @param color    Color
         */
        static osg::Node* createBoundingBox(const osg::BoundingBox& bb, Color& color);

        /**
         * TODO: DEPRECATE / move to app that uses this (SIMSDK)
         * Builds the geometry for an ellipsoid.
         */
        static osg::Geometry* createEllipsoidGeometry(
            float xr, float yr, float zr, const osg::Vec4& color, float maxAngle =10.0f,
            float minLat =-90.0, float maxLat=90.0, float minLon=-180.0, float maxLon=180.0);

        //! TODO: DEPRECATE / move to app that uses this (SIMSDK)
        static osg::Node* createEllipsoid(
            float xr, float yr, float zr, const osg::Vec4& color, float maxAngle =10.0f,
            float minLat =-90.0, float maxLat=90.0, float minLon=-180.0, float maxLon=180.0);

        struct AltitudePolicy
        {
            AltitudePolicy() : draping(false), sceneClamping(false), gpuClamping(false) { }
            bool draping;
            bool sceneClamping;
            bool gpuClamping;
        };

        static void getAltitudePolicy( const Style& style, AltitudePolicy& b );

        /**
         * If the style requests draping or clamping, creates the appropriate parent
         * group and adds the input node as a child, and returns the new parent.
         * Otherwise, just returns the input node unchanged.
         */
        static osg::Node* installOverlayParent(osg::Node* child, const Style& style);

    private:
        AnnotationUtils() { }
    };


    namespace Util
    {
        /**
         * A bounding sphere calculator that forces the center point
         * to (0,0,0) and adjusts the radius accordingly. This forces the bounding
         * center to the "control point" of the annotation - useful for culling.
         */
        class ControlPointCallback : public osg::Node::ComputeBoundingSphereCallback
        {
        public:
            osg::BoundingSphere computeBound(const osg::Node& node) const
            {
                osg::BoundingSphere nodebs = node.computeBound();
                return osg::BoundingSphere(osg::Vec3(0,0,0), nodebs.center().length() + nodebs.radius());
            }
        };
    }
}

#endif //OSGEARTH_ANNOTATION_ANNOTATION_UTILS_H
