#pragma once
#include <drogon/exports.h>
#include <trantor/utils/Logger.h>
#include <trantor/utils/MsgBuffer.h>
#include <sstream>
#include <string>
#include <unordered_map>
#include <stdarg.h>
#include <stdio.h>
#include <type_traits>
#include <any>
#include <string_view>
namespace drogon
{
class DROGON_EXPORT HttpViewData
{
  public:
    template <typename T>
    const T &get(const std::string &key) const
    {
        static const T nullVal = T();
        auto it = viewData_.find(key);
        if (it != viewData_.end())
        {
            if (typeid(T) == it->second.type())
            {
                return *(std::any_cast<T>(&(it->second)));
            }
            else
            {
                LOG_ERROR << "Bad type";
            }
        }
        return nullVal;
    }
    void insert(const std::string &key, std::any &&obj)
    {
        viewData_[key] = std::move(obj);
    }
    void insert(const std::string &key, const std::any &obj)
    {
        viewData_[key] = obj;
    }
    template <typename T>
    void insertAsString(const std::string &key, T &&val)
    {
        std::stringstream ss;
        ss << val;
        viewData_[key] = ss.str();
    }
    void insertFormattedString(const std::string &key, const char *format, ...)
    {
        std::string strBuffer;
        strBuffer.resize(128);
        va_list ap, backup_ap;
        va_start(ap, format);
        va_copy(backup_ap, ap);
        auto result = vsnprintf((char *)strBuffer.data(),
                                strBuffer.size(),
                                format,
                                backup_ap);
        va_end(backup_ap);
        if ((result >= 0) &&
            (static_cast<std::string::size_type>(result) < strBuffer.size()))
        {
            strBuffer.resize(static_cast<std::string::size_type>(result));
        }
        else
        {
            while (true)
            {
                if (result < 0)
                {
                    strBuffer.resize(strBuffer.size() * 2);
                }
                else
                {
                    strBuffer.resize(result + 1);
                }
                va_copy(backup_ap, ap);
                result = vsnprintf((char *)strBuffer.data(),
                                   strBuffer.size(),
                                   format,
                                   backup_ap);
                va_end(backup_ap);
                if ((result >= 0) &&
                    ((std::string::size_type)result < strBuffer.size()))
                {
                    strBuffer.resize(result);
                    break;
                }
            }
        }
        va_end(ap);
        viewData_[key] = std::move(strBuffer);
    }
    std::any &operator[](const std::string &key) const
    {
        return viewData_[key];
    }
    static std::string htmlTranslate(const char *str, size_t length);
    static std::string htmlTranslate(const std::string_view &str)
    {
        return htmlTranslate(str.data(), str.length());
    }
    static bool needTranslation(const std::string_view &str)
    {
        for (auto const &c : str)
        {
            switch (c)
            {
                case '"':
                case '&':
                case '<':
                case '>':
                    return true;
                default:
                    continue;
            }
        }
        return false;
    }
  protected:
    using ViewDataMap = std::unordered_map<std::string, std::any>;
    mutable ViewDataMap viewData_;
};
}  