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

#include <osgEarth/Common>
#include <osg/Notify>
#include <osg/Timer>
#include <vector>
#include <string>
#include <cstring>

namespace osgEarth
{
    /** set the notify level, overriding the default or the value set by
      * the environmental variable OSGNOTIFYLEVEL.
      */
    extern OSGEARTH_EXPORT void setNotifyLevel(osg::NotifySeverity severity);

    /** get the notify level. */
    extern OSGEARTH_EXPORT osg::NotifySeverity getNotifyLevel();

    /** is notification enabled, given the current setNotifyLevel() setting? */
    extern OSGEARTH_EXPORT bool isNotifyEnabled(osg::NotifySeverity severity);

    /** initialize notify level. */
    extern OSGEARTH_EXPORT bool initNotifyLevel();
  
    extern OSGEARTH_EXPORT std::ostream& notify(const osg::NotifySeverity severity);

    inline std::ostream& notify(void) { return osgEarth::notify(osg::INFO); }

    /**
    * Records the thread's call stack at the time of construction.
    */
    class OSGEARTH_EXPORT CallStack
    {
    public:
        CallStack();
        std::vector<std::string> symbols;
    };

#define OE_NOTIFY( X ) if(osgEarth::isNotifyEnabled( X )) osgEarth::notify( X )

#define OE_CRITICAL OE_NOTIFY(osg::ALWAYS)
#define OE_FATAL OE_NOTIFY(osg::FATAL)
#define OE_WARN OE_NOTIFY(osg::WARN)
#define OE_NOTICE OE_NOTIFY(osg::NOTICE)
#define OE_INFO OE_NOTIFY(osg::INFO)
#define OE_INFO_CONTINUE OE_NOTIFY(osg::INFO)
#define OE_DEBUG OE_NOTIFY(osg::DEBUG_INFO)

#define OE_NULL if(false) osgEarth::notify(osg::ALWAYS)

#define OE_START_TIMER(VAR) osg::Timer_t VAR##_oe_timer = osg::Timer::instance()->tick()
#define OE_STOP_TIMER(VAR) osg::Timer::instance()->delta_s( VAR##_oe_timer, osg::Timer::instance()->tick() )
#define OE_GET_TIMER(VAR) osg::Timer::instance()->delta_s( VAR##_oe_timer, osg::Timer::instance()->tick() )

#if defined(_MSC_VER)
#define OE_FILE (std::strrchr(__FILE__, '\\') ? std::strrchr(__FILE__, '\\') + 1 : __FILE__)
#else
#define OE_FILE (std::strrchr(__FILE__, '/') ? std::strrchr(__FILE__, '/') + 1 : __FILE__)
#endif

#define OE_SOFT_ASSERT(EXPR, ...) if(!(EXPR)) { OE_WARN << "ASSERTION FAILURE (" << __func__ << " @ " << OE_FILE << ":" << __LINE__ << ") " #EXPR " ..." << __VA_ARGS__ "" << std::endl << osgEarth::CallStack(); }
#define OE_SOFT_ASSERT_AND_RETURN(EXPR, RETVAL, ...) if(!(EXPR)) { OE_WARN << "ASSERTION FAILURE (" << __func__ << " @ " << OE_FILE << ":" << __LINE__ << ") " #EXPR " ..." << __VA_ARGS__ "" << std::endl << osgEarth::CallStack(); return RETVAL; }
#define OE_IF_SOFT_ASSERT(EXPR, ...) if(!(EXPR)) { OE_WARN << "ASSERTION FAILURE (" << __func__ << " @ " << OE_FILE << ":" << __LINE__ << ") " #EXPR " ..." << __VA_ARGS__ "" << std::endl; } else
#define OE_HARD_ASSERT(EXPR, ...) if(!(EXPR)) { OE_WARN << "FATAL ASSERTION FAILURE (" << __func__ << " @ " << OE_FILE << ":" << __LINE__ << ") " #EXPR " ..." << __VA_ARGS__ "" << std::endl << osgEarth::CallStack(); abort(); }

extern OSGEARTH_EXPORT void setNotifyHandler(osg::NotifyHandler *handler);

/** Get currrent notification handler. */
extern OSGEARTH_EXPORT osg::NotifyHandler *getNotifyHandler();

}

inline std::ostream& operator << (std::ostream& os, const osgEarth::CallStack& obj)
{
    for (auto& s : obj.symbols) {
        if (s == "main") break;
        if (s != "osgEarth::CallStack::CallStack")
            os << "  " << s << std::endl;
    }
    return os;
}