#pragma once
#include <stdio.h>
#include <streambuf>
#include <ostream>
#include <set>
#include <config.h>
#include <vector>
#include <variant>
#include <map>
#include <mutex>
#include <stack>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include <functional>
#include <fmt/core.h>
#include <fmt/format.h>
namespace Looper::Log {
    struct LogStream {
        std::set<FILE *> outputs;
        static std::set<FILE *> global_outputs;
        int my_log_level;
        std::set<LogStream*> streams;
        bool nested;
        bool need_prefix;
        std::vector<std::string> names;
        std::set<FILE*> get_used_outputs();

#ifdef __ANDROID__
        std::set<android_LogPriority> android_outputs;
#endif
        std::string line;
        LogStream(std::initializer_list<std::string> names, int log_level, bool nested, void* discriminator);
        std::map<std::string, LogStream*> substreams;
        public:
        virtual void _writec(const char chr);
        typedef std::lock_guard<std::recursive_mutex> log_mutex_guard;
        static int log_level;
        virtual void writeprefix();
        void write_level_prefix(size_t level);
        void write_level(size_t level, const char *msg);
        void write_level(size_t level, std::string msg);
        void writef_level(size_t level, const char *msg, ...);
        void vwritef_level(size_t level, const char *msg, va_list args);
        void writeln(const char *msg);
        void writeln_n(const char *msg, size_t n);
        void writeln(std::string msg);
        void writes(const char *msg);
        void writesn(const char *msg, size_t n);
        void writes(std::string msg);
        virtual void writec(const char chr);
        inline virtual ~LogStream() { }
        void vwritef(const char *fmt, va_list args);
        void writef(const char *fmt, ...);
        void vwritefln(const char *fmt, va_list args);
        void writefln(const char *fmt, ...);
        static const std::locale c_locale;
        inline void vwritef2(fmt::string_view fmt, fmt::format_args args) {
            return writes(fmt::vformat(c_locale, fmt, args));
        }
        template <typename... T>
        inline void writef2(fmt::format_string<T...> fmt, T&&... args) {
            return vwritef2(fmt::string_view(fmt), fmt::make_format_args(args...));
        }
        inline void vwritefln2(fmt::string_view fmt, fmt::format_args args) {
            return writeln(fmt::vformat(c_locale, fmt, args));
        }
        template <typename... T>
        inline void writefln2(fmt::format_string<T...> fmt, T&&... args) {
            return vwritefln2(fmt::string_view(fmt), fmt::make_format_args(args...));
        }
        LogStream &substream(std::string name);
        LogStream(std::initializer_list<std::string> names, std::initializer_list<LogStream*> streams, int log_level = 0);

#ifdef __ANDROID__
        LogStream(std::initializer_list<std::string> names, std::initializer_list<std::variant<FILE*, android_LogPriority>> outputs, int log_level = 0);
#else
        LogStream(std::initializer_list<std::string> names, std::initializer_list<FILE*> outputs, int log_level = 0);
#endif
        protected:
    };
    std::string get_log_name_by_idx(int idx);
    void init_logging();
    void init_logging_subprocess();
    typedef std::function<LogStream*(int)> log_stream_creation_function_t;
    void init_logging_custom(log_stream_creation_function_t fn);
    LogStream &get_log_stream_by_level(int level);
    #define LOG(level) (Looper::Log::get_log_stream_by_level(level))
    #define TRACE LOG(-2)
    #define DEBUG LOG(-1)
    #define INFO LOG(0)
    #define WARNING LOG(1)
    #define ERROR LOG(2)
    void pop_log_streams();
    class Context {
        std::function<void()> end;
        public:
        inline Context(std::function<void()> start, std::function<void()> end) {
            start();
            this->end = end;
        }
        inline ~Context() {
            end();
        }
    };
    class LogContext : public Context {
        public:
        inline LogContext(std::function<void()> push_log_stream_fn) : Context(push_log_stream_fn, pop_log_streams) {

        }
    };
    typedef std::map<int, std::stack<LogStream*>> LogStreamMap;
}
extern "C" {
    void write_log(int level, const char *log);
    void write_logln(int level, const char *log);
}