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

#include <osgEarth/Common>
#include <osgEarth/Script>
#include <osgEarth/Config>
#include <osgEarth/Feature>

#ifndef OSGEARTH_ENV_SCRIPT_ENGINE
#define OSGEARTH_ENV_SCRIPT_ENGINE "OSGEARTH_SCRIPT_ENGINE"
#endif

namespace osgEarth {
    namespace Util
    {
        using namespace osgEarth;

        class FilterContext;

        /**
         * Configuration options for a models source.
         */
        class OSGEARTH_EXPORT ScriptEngineOptions : public DriverConfigOptions
        {
        public:
            ScriptEngineOptions(const ConfigOptions& options = ConfigOptions()) :
                DriverConfigOptions(options)
            {
                fromConfig(_conf);
            }

            virtual ~ScriptEngineOptions() {}

        public: // properties

            /** optional script source */
            optional<Script>& script() { return _script; }
            const optional<Script>& script() const { return _script; }

        public:
            virtual Config getConfig() const;

        protected:
            virtual void mergeConfig(const Config& conf);

        private:
            void fromConfig(const Config& conf);

            optional<Script> _script;
        };

        //--------------------------------------------------------------------

        class OSGEARTH_EXPORT ScriptEngine : public osg::Object
        {
        public:
            ScriptEngine(const ScriptEngineOptions& options = ScriptEngineOptions()) :
                _script(options.script()) {
            }

            virtual ~ScriptEngine() {}

            /** Whether the engine supports the specified scripting language */
            virtual bool supported(std::string lang) = 0;

            /** Whether the engine can run the specified script */
            virtual bool supported(Script* script)
            {
                return script ? supported(script->getLanguage()) : false;
            }

            /** Impl-specific profile to set. */
            void setProfile(const std::string& profile) { _profile = profile; }
            const std::string& getProfile() const { return _profile; }

            //! Runs a code snippet against a single feature (or no feature)
            virtual ScriptResult run(
                const std::string& code,
                Feature* feature = nullptr,
                FilterContext const* context = nullptr) = 0;

            //! Runs a code snippet against a list of features
            //! If a feature fails, return its result and abort the iteration
            virtual bool run(
                const std::string& code,
                const FeatureList& features,
                std::vector<ScriptResult>& results,
                FilterContext const* context);

            /** Runs a script */
            virtual ScriptResult run(Script* script, Feature* feature = 0L, FilterContext const* context = 0L)
            {
                OE_SOFT_ASSERT_AND_RETURN(script != nullptr, ScriptResult("Illegal null script", false));
                return run(script->getCode(), feature, context);
            }

        public:
            // META_Object specialization:
            virtual osg::Object* cloneType() const { return 0; } // cloneType() not appropriate
            virtual osg::Object* clone(const osg::CopyOp&) const { return 0; } // clone() not appropriate
            virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const ScriptEngine*>(obj) != NULL; }
            virtual const char* className() const { return "ScriptEngine"; }
            virtual const char* libraryName() const { return "osgEarth"; }

        protected:
            // common script to be included in each call to run()
            optional<Script> _script;
            std::string _profile;
        };

        typedef std::list< osg::ref_ptr<ScriptEngine> > ScriptEngineList;
        typedef std::map<std::string, osg::ref_ptr<ScriptEngine> > ScriptEngineMap;

        //--------------------------------------------------------------------

        class OSGEARTH_EXPORT ScriptEngineDriver : public osgDB::ReaderWriter
        {
        protected:
            const ScriptEngineOptions& getScriptEngineOptions(const osgDB::ReaderWriter::Options* rwOpt) const;
        };

        //--------------------------------------------------------------------

        class OSGEARTH_EXPORT ScriptEngineFactory
        {
        public:
            static ScriptEngineFactory* instance();

            static ScriptEngine* create(const std::string& language, const std::string& engineName = "", bool quiet = false);
            static ScriptEngine* create(const Script& script, const std::string& engineName = "", bool quiet = false);
            static ScriptEngine* createWithProfile(const Script& script, const std::string& profile, const std::string& engineName = "", bool quiet = false);
            static ScriptEngine* create(const ScriptEngineOptions& options, bool quiet = false);

        protected:
            ScriptEngineFactory() = default;

            std::vector<std::string> _failedDrivers;
        };
    }

    extern OSGEARTH_EXPORT std::string evaluateExpression(
        const std::string& expression,
        Util::ScriptEngine* engine);
}
