/* osgEarth
* Copyright 2025 Pelican Mapping
* MIT License
*/
#pragma once

#include <osgEarth/Common>
#include <osgEarth/IOTypes>
#include <osgEarth/URI>
#include <osgEarth/TextureArena>

namespace osgEarth
{
    namespace Util
    {
        using namespace osgEarth;

        /**
         * Built in utilities for dealing with materials
         */
        class OSGEARTH_EXPORT MaterialUtils
        {
        public:
            //! A Mangler is a function that takes a filename and returns a new filename.
            using Mangler = std::function<std::string(const std::string&)>;

            //! The default filename mangler for normal maps
            static Mangler getDefaultNormalMapNameMangler();

            //! The default filename mangler for PBR maps
            static Mangler getDefaultPBRMapNameMangler();

            //! The default filename mangler for displacement maps
            static Mangler getDefaultDisplacementMapNameMangler();
        };

        /**
         * Loads models and installs material textures alongside
         * the normal albedo textures as directed.
         *
         * Example usage
         *
         * <pre>
         * URI uri(filename);
         * MaterialLoader loader;
         *
         * // set a filename mangler for unit 1:
         * loader.setMangler(1, [](const std::string& filename) { return filename + "_NML.png"; });
         *
         * // set a texture factory for unit 1 (optional):
         * loader.setTextureFactory(1, [](osg::Image* image) { return new osg::Texture2D(image); });
         *
         * // run the loader on a node:
         * node->accept(loader);
         * </pre>
         */
        class OSGEARTH_EXPORT MaterialLoader : public osg::NodeVisitor
        {
        public:
            //! New material loader
            MaterialLoader();

            //! Set the db options to use when reading material textures
            void setOptions(const osgDB::Options* options);

            //! Function used to change the input URI string to
            //! one referencing a material texture
            using Mangler = std::function<std::string(const std::string&)>;

            //! Set a function that will mangle the original texture's
            //! filename into a filename suitable for laoding the material.
            //! Applies to the material to be installed into unit "unit".
            void setMangler(
                int unit,
                Mangler mangler);

            //! Function that creates a Texture from an Image.
            using TextureFactory = std::function<osg::Texture* (osg::Image*)>;

            //! Set a function that will create a Texture from the loaded
            //! material image. Applies to the material to be installed into
            //! unit "unit".
            void setTextureFactory(
                int unit,
                TextureFactory factory);

            //! Sets a context for relative pathnames
            void setReferrer(const std::string& value) {
                _referrer = value;
            }

        public: // osg::NodeVisitor
            void apply(osg::Node&) override;

        protected:
            void apply(osg::StateSet*);

            osg::ref_ptr<const osgDB::Options> _options;
            std::unordered_map<int, Mangler> _manglers;
            std::unordered_map<int, TextureFactory> _factories;
            using Cache = std::unordered_map<std::string, osg::ref_ptr<osg::Texture>>;
            Cache _cache;
            std::string _referrer;
        };
    }
}
