#pragma once

//#define CROW_JSON_NO_ERROR_CHECK
//#define CROW_JSON_USE_MAP

#include <string>
#ifdef CROW_JSON_USE_MAP
#include <map>
#else
#include <unordered_map>
#endif
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <cmath>

#include "crow/utility.h"
#include "crow/settings.h"
#include "crow/returnable.h"
#include "crow/logging.h"

using std::isinf;
using std::isnan;


namespace crow
{
    namespace mustache
    {
        class template_t;
    }

    namespace json
    {
        inline void escape(const std::string& str, std::string& ret)
        {
            ret.reserve(ret.size() + str.size() + str.size() / 4);
            for (auto c : str)
            {
                switch (c)
                {
                    case '"': ret += "\\\""; break;
                    case '\\': ret += "\\\\"; break;
                    case '\n': ret += "\\n"; break;
                    case '\b': ret += "\\b"; break;
                    case '\f': ret += "\\f"; break;
                    case '\r': ret += "\\r"; break;
                    case '\t': ret += "\\t"; break;
                    default:
                        if (c >= 0 && c < 0x20)
                        {
                            ret += "\\u00";
                            auto to_hex = [](char c) {
                                c = c & 0xf;
                                if (c < 10)
                                    return '0' + c;
                                return 'a' + c - 10;
                            };
                            ret += to_hex(c / 16);
                            ret += to_hex(c % 16);
                        }
                        else
                            ret += c;
                        break;
                }
            }
        }
        inline std::string escape(const std::string& str)
        {
            std::string ret;
            escape(str, ret);
            return ret;
        }

        enum class type : char
        {
            Null,
            False,
            True,
            Number,
            String,
            List,
            Object,
            Function
        };

        inline const char* get_type_str(type t)
        {
            switch (t)
            {
                case type::Number: return "Number";
                case type::False: return "False";
                case type::True: return "True";
                case type::List: return "List";
                case type::String: return "String";
                case type::Object: return "Object";
                case type::Function: return "Function";
                default: return "Unknown";
            }
        }

        enum class num_type : char
        {
            Signed_integer,
            Unsigned_integer,
            Floating_point,
            Null
        };

        class rvalue;
        rvalue load(const char* data, size_t size);

        namespace detail
        {
            /// A read string implementation with comparison functionality.
            struct r_string
            {
                r_string(){};
                r_string(char* s, char* e):
                  s_(s), e_(e){};
                ~r_string()
                {
                    if (owned_)
                        delete[] s_;
                }

                r_string(const r_string& r)
                {
                    *this = r;
                }

                r_string(r_string&& r)
                {
                    *this = r;
                }

                r_string& operator=(r_string&& r)
                {
                    s_ = r.s_;
                    e_ = r.e_;
                    owned_ = r.owned_;
                    if (r.owned_)
                        r.owned_ = 0;
                    return *this;
                }

                r_string& operator=(const r_string& r)
                {
                    s_ = r.s_;
                    e_ = r.e_;
                    owned_ = 0;
                    return *this;
                }

                operator std::string() const
                {
                    return std::string(s_, e_);
                }


                const char* begin() const { return s_; }
                const char* end() const { return e_; }
                size_t size() const { return end() - begin(); }

                using iterator = const char*;
                using const_iterator = const char*;

                char* s_;         ///< Start.
                mutable char* e_; ///< End.
                uint8_t owned_{0};
                friend std::ostream& operator<<(std::ostream& os, const r_string& s)
                {
                    os << static_cast<std::string>(s);
                    return os;
                }

            private:
                void force(char* s, uint32_t length)
                {
                    s_ = s;
                    e_ = s_ + length;
                    owned_ = 1;
                }
                friend rvalue crow::json::load(const char* data, size_t size);

                friend bool operator==(const r_string& l, const r_string& r);
                friend bool operator==(const std::string& l, const r_string& r);
                friend bool operator==(const r_string& l, const std::string& r);

                template<typename T, typename U>
                inline static bool equals(const T& l, const U& r)
                {
                    if (l.size() != r.size())
                        return false;

                    for (size_t i = 0; i < l.size(); i++)
                    {
                        if (*(l.begin() + i) != *(r.begin() + i))
                            return false;
                    }

                    return true;
                }
            };

            inline bool operator<(const r_string& l, const r_string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator<(const r_string& l, const std::string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator<(const std::string& l, const r_string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator>(const r_string& l, const r_string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator>(const r_string& l, const std::string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator>(const std::string& l, const r_string& r)
            {
                return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
            }

            inline bool operator==(const r_string& l, const r_string& r)
            {
                return r_string::equals(l, r);
            }

            inline bool operator==(const r_string& l, const std::string& r)
            {
                return r_string::equals(l, r);
            }

            inline bool operator==(const std::string& l, const r_string& r)
            {
                return r_string::equals(l, r);
            }

            inline bool operator!=(const r_string& l, const r_string& r)
            {
                return !(l == r);
            }

            inline bool operator!=(const r_string& l, const std::string& r)
            {
                return !(l == r);
            }

            inline bool operator!=(const std::string& l, const r_string& r)
            {
                return !(l == r);
            }
        } // namespace detail

        /// JSON read value.

        ///
        /// Value can mean any json value, including a JSON object.
        /// Read means this class is used to primarily read strings into a JSON value.
        class rvalue
        {
            static const int cached_bit = 2;
            static const int error_bit = 4;

        public:
            rvalue() noexcept:
              option_{error_bit}
            {
            }
            rvalue(type t) noexcept:
              lsize_{}, lremain_{}, t_{t}
            {
            }
            rvalue(type t, char* s, char* e) noexcept:
              start_{s}, end_{e}, t_{t}
            {
                determine_num_type();
            }

            rvalue(const rvalue& r):
              start_(r.start_), end_(r.end_), key_(r.key_), t_(r.t_), nt_(r.nt_), option_(r.option_)
            {
                copy_l(r);
            }

            rvalue(rvalue&& r) noexcept
            {
                *this = std::move(r);
            }

            rvalue& operator=(const rvalue& r)
            {
                start_ = r.start_;
                end_ = r.end_;
                key_ = r.key_;
                t_ = r.t_;
                nt_ = r.nt_;
                option_ = r.option_;
                copy_l(r);
                return *this;
            }
            rvalue& operator=(rvalue&& r) noexcept
            {
                start_ = r.start_;
                end_ = r.end_;
                key_ = std::move(r.key_);
                l_ = std::move(r.l_);
                lsize_ = r.lsize_;
                lremain_ = r.lremain_;
                t_ = r.t_;
                nt_ = r.nt_;
                option_ = r.option_;
                return *this;
            }

            explicit operator bool() const noexcept
            {
                return (option_ & error_bit) == 0;
            }

            explicit operator int64_t() const
            {
                return i();
            }

            explicit operator uint64_t() const
            {
                return u();
            }

            explicit operator int() const
            {
                return static_cast<int>(i());
            }

            /// Return any json value (not object or list) as a string.
            explicit operator std::string() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() == type::Object || t() == type::List)
                    throw std::runtime_error("json type container");
#endif
                switch (t())
                {
                    case type::String:
                        return std::string(s());
                    case type::Null:
                        return std::string("null");
                    case type::True:
                        return std::string("true");
                    case type::False:
                        return std::string("false");
                    default:
                        return std::string(start_, end_ - start_);
                }
            }

            /// The type of the JSON value.
            type t() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (option_ & error_bit)
                {
                    throw std::runtime_error("invalid json object");
                }
#endif
                return t_;
            }

            /// The number type of the JSON value.
            num_type nt() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (option_ & error_bit)
                {
                    throw std::runtime_error("invalid json object");
                }
#endif
                return nt_;
            }

            /// The integer value.
            int64_t i() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                switch (t())
                {
                    case type::Number:
                    case type::String:
                        return utility::lexical_cast<int64_t>(start_, end_ - start_);
                    default:
                        const std::string msg = "expected number, got: " + std::string(get_type_str(t()));
                        throw std::runtime_error(msg);
                }
#endif
                return utility::lexical_cast<int64_t>(start_, end_ - start_);
            }

            /// The unsigned integer value.
            uint64_t u() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                switch (t())
                {
                    case type::Number:
                    case type::String:
                        return utility::lexical_cast<uint64_t>(start_, end_ - start_);
                    default:
                        throw std::runtime_error(std::string("expected number, got: ") + get_type_str(t()));
                }
#endif
                return utility::lexical_cast<uint64_t>(start_, end_ - start_);
            }

            /// The double precision floating-point number value.
            double d() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Number)
                    throw std::runtime_error("value is not number");
#endif
                return utility::lexical_cast<double>(start_, end_ - start_);
            }

            /// The boolean value.
            bool b() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::True && t() != type::False)
                    throw std::runtime_error("value is not boolean");
#endif
                return t() == type::True;
            }

            /// The string value.
            detail::r_string s() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::String)
                    throw std::runtime_error("value is not string");
#endif
                unescape();
                return detail::r_string{start_, end_};
            }

            /// The list or object value
            std::vector<rvalue> lo()
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object && t() != type::List)
                    throw std::runtime_error("value is not a container");
#endif
                std::vector<rvalue> ret;
                ret.reserve(lsize_);
                for (uint32_t i = 0; i < lsize_; i++)
                {
                    ret.emplace_back(l_[i]);
                }
                return ret;
            }

            /// Convert escaped string character to their original form ("\\n" -> '\n').
            void unescape() const
            {
                if (*(start_ - 1))
                {
                    char* head = start_;
                    char* tail = start_;
                    while (head != end_)
                    {
                        if (*head == '\\')
                        {
                            switch (*++head)
                            {
                                case '"': *tail++ = '"'; break;
                                case '\\': *tail++ = '\\'; break;
                                case '/': *tail++ = '/'; break;
                                case 'b': *tail++ = '\b'; break;
                                case 'f': *tail++ = '\f'; break;
                                case 'n': *tail++ = '\n'; break;
                                case 'r': *tail++ = '\r'; break;
                                case 't': *tail++ = '\t'; break;
                                case 'u':
                                {
                                    auto from_hex = [](char c) {
                                        if (c >= 'a')
                                            return c - 'a' + 10;
                                        if (c >= 'A')
                                            return c - 'A' + 10;
                                        return c - '0';
                                    };
                                    unsigned int code =
                                      (from_hex(head[1]) << 12) +
                                      (from_hex(head[2]) << 8) +
                                      (from_hex(head[3]) << 4) +
                                      from_hex(head[4]);
                                    if (code >= 0x800)
                                    {
                                        *tail++ = 0xE0 | (code >> 12);
                                        *tail++ = 0x80 | ((code >> 6) & 0x3F);
                                        *tail++ = 0x80 | (code & 0x3F);
                                    }
                                    else if (code >= 0x80)
                                    {
                                        *tail++ = 0xC0 | (code >> 6);
                                        *tail++ = 0x80 | (code & 0x3F);
                                    }
                                    else
                                    {
                                        *tail++ = code;
                                    }
                                    head += 4;
                                }
                                break;
                            }
                        }
                        else
                            *tail++ = *head;
                        head++;
                    }
                    end_ = tail;
                    *end_ = 0;
                    *(start_ - 1) = 0;
                }
            }

            /// Check if the json object has the passed string as a key.
            bool has(const char* str) const
            {
                return has(std::string(str));
            }

            bool has(const std::string& str) const
            {
                struct Pred
                {
                    bool operator()(const rvalue& l, const rvalue& r) const
                    {
                        return l.key_ < r.key_;
                    };
                    bool operator()(const rvalue& l, const std::string& r) const
                    {
                        return l.key_ < r;
                    };
                    bool operator()(const std::string& l, const rvalue& r) const
                    {
                        return l < r.key_;
                    };
                };
                if (!is_cached())
                {
                    std::sort(begin(), end(), Pred());
                    set_cached();
                }
                auto it = lower_bound(begin(), end(), str, Pred());
                return it != end() && it->key_ == str;
            }

            int count(const std::string& str)
            {
                return has(str) ? 1 : 0;
            }

            rvalue* begin() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object && t() != type::List)
                    throw std::runtime_error("value is not a container");
#endif
                return l_.get();
            }
            rvalue* end() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object && t() != type::List)
                    throw std::runtime_error("value is not a container");
#endif
                return l_.get() + lsize_;
            }

            const detail::r_string& key() const
            {
                return key_;
            }

            size_t size() const
            {
                if (t() == type::String)
                    return s().size();
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object && t() != type::List)
                    throw std::runtime_error("value is not a container");
#endif
                return lsize_;
            }

            const rvalue& operator[](int index) const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::List)
                    throw std::runtime_error("value is not a list");
                if (index >= static_cast<int>(lsize_) || index < 0)
                    throw std::runtime_error("list out of bound");
#endif
                return l_[index];
            }

            const rvalue& operator[](size_t index) const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::List)
                    throw std::runtime_error("value is not a list");
                if (index >= lsize_)
                    throw std::runtime_error("list out of bound");
#endif
                return l_[index];
            }

            const rvalue& operator[](const char* str) const
            {
                return this->operator[](std::string(str));
            }

            const rvalue& operator[](const std::string& str) const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object)
                    throw std::runtime_error("value is not an object");
#endif
                struct Pred
                {
                    bool operator()(const rvalue& l, const rvalue& r) const
                    {
                        return l.key_ < r.key_;
                    };
                    bool operator()(const rvalue& l, const std::string& r) const
                    {
                        return l.key_ < r;
                    };
                    bool operator()(const std::string& l, const rvalue& r) const
                    {
                        return l < r.key_;
                    };
                };
                if (!is_cached())
                {
                    std::sort(begin(), end(), Pred());
                    set_cached();
                }
                auto it = lower_bound(begin(), end(), str, Pred());
                if (it != end() && it->key_ == str)
                    return *it;
#ifndef CROW_JSON_NO_ERROR_CHECK
                throw std::runtime_error("cannot find key");
#else
                static rvalue nullValue;
                return nullValue;
#endif
            }

            void set_error()
            {
                option_ |= error_bit;
            }

            bool error() const
            {
                return (option_ & error_bit) != 0;
            }

            std::vector<std::string> keys() const
            {
#ifndef CROW_JSON_NO_ERROR_CHECK
                if (t() != type::Object)
                    throw std::runtime_error("value is not an object");
#endif
                std::vector<std::string> ret;
                ret.reserve(lsize_);
                for (uint32_t i = 0; i < lsize_; i++)
                {
                    ret.emplace_back(std::string(l_[i].key()));
                }
                return ret;
            }

        private:
            bool is_cached() const
            {
                return (option_ & cached_bit) != 0;
            }
            void set_cached() const
            {
                option_ |= cached_bit;
            }
            void copy_l(const rvalue& r)
            {
                if (r.t() != type::Object && r.t() != type::List)
                    return;
                lsize_ = r.lsize_;
                lremain_ = 0;
                l_.reset(new rvalue[lsize_]);
                std::copy(r.begin(), r.end(), begin());
            }

            void emplace_back(rvalue&& v)
            {
                if (!lremain_)
                {
                    int new_size = lsize_ + lsize_;
                    if (new_size - lsize_ > 60000)
                        new_size = lsize_ + 60000;
                    if (new_size < 4)
                        new_size = 4;
                    rvalue* p = new rvalue[new_size];
                    rvalue* p2 = p;
                    for (auto& x : *this)
                        *p2++ = std::move(x);
                    l_.reset(p);
                    lremain_ = new_size - lsize_;
                }
                l_[lsize_++] = std::move(v);
                lremain_--;
            }

            /// Determines num_type from the string.
            void determine_num_type()
            {
                if (t_ != type::Number)
                {
                    nt_ = num_type::Null;
                    return;
                }

                const std::size_t len = end_ - start_;
                const bool has_minus = std::memchr(start_, '-', len) != nullptr;
                const bool has_e = std::memchr(start_, 'e', len) != nullptr || std::memchr(start_, 'E', len) != nullptr;
                const bool has_dec_sep = std::memchr(start_, '.', len) != nullptr;
                if (has_dec_sep || has_e)
                    nt_ = num_type::Floating_point;
                else if (has_minus)
                    nt_ = num_type::Signed_integer;
                else
                    nt_ = num_type::Unsigned_integer;
            }

            mutable char* start_;
            mutable char* end_;
            detail::r_string key_;
            std::unique_ptr<rvalue[]> l_;
            uint32_t lsize_;
            uint16_t lremain_;
            type t_;
            num_type nt_{num_type::Null};
            mutable uint8_t option_{0};

            friend rvalue load_nocopy_internal(char* data, size_t size);
            friend rvalue load(const char* data, size_t size);
            friend std::ostream& operator<<(std::ostream& os, const rvalue& r)
            {
                switch (r.t_)
                {

                    case type::Null: os << "null"; break;
                    case type::False: os << "false"; break;
                    case type::True: os << "true"; break;
                    case type::Number:
                    {
                        switch (r.nt())
                        {
                            case num_type::Floating_point: os << r.d(); break;
                            case num_type::Signed_integer: os << r.i(); break;
                            case num_type::Unsigned_integer: os << r.u(); break;
                            case num_type::Null: throw std::runtime_error("Number with num_type Null");
                        }
                    }
                    break;
                    case type::String: os << '"' << r.s() << '"'; break;
                    case type::List:
                    {
                        os << '[';
                        bool first = true;
                        for (auto& x : r)
                        {
                            if (!first)
                                os << ',';
                            first = false;
                            os << x;
                        }
                        os << ']';
                    }
                    break;
                    case type::Object:
                    {
                        os << '{';
                        bool first = true;
                        for (auto& x : r)
                        {
                            if (!first)
                                os << ',';
                            os << '"' << escape(x.key_) << "\":";
                            first = false;
                            os << x;
                        }
                        os << '}';
                    }
                    break;
                    case type::Function: os << "custom function"; break;
                }
                return os;
            }
        };
        namespace detail
        {
        }

        inline bool operator==(const rvalue& l, const std::string& r)
        {
            return l.s() == r;
        }

        inline bool operator==(const std::string& l, const rvalue& r)
        {
            return l == r.s();
        }

        inline bool operator!=(const rvalue& l, const std::string& r)
        {
            return l.s() != r;
        }

        inline bool operator!=(const std::string& l, const rvalue& r)
        {
            return l != r.s();
        }

        inline bool operator==(const rvalue& l, double r)
        {
            return l.d() == r;
        }

        inline bool operator==(double l, const rvalue& r)
        {
            return l == r.d();
        }

        inline bool operator!=(const rvalue& l, double r)
        {
            return l.d() != r;
        }

        inline bool operator!=(double l, const rvalue& r)
        {
            return l != r.d();
        }


        inline rvalue load_nocopy_internal(char* data, size_t size)
        {
            // Defend against excessive recursion
            static constexpr unsigned max_depth = 10000;

            //static const char* escaped = "\"\\/\b\f\n\r\t";
            struct Parser
            {
                Parser(char* data, size_t /*size*/):
                  data(data)
                {
                }

                bool consume(char c)
                {
                    if (CROW_UNLIKELY(*data != c))
                        return false;
                    data++;
                    return true;
                }

                void ws_skip()
                {
                    while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n')
                        ++data;
                };

                rvalue decode_string()
                {
                    if (CROW_UNLIKELY(!consume('"')))
                        return {};
                    char* start = data;
                    uint8_t has_escaping = 0;
                    while (1)
                    {
                        if (CROW_LIKELY(*data != '"' && *data != '\\' && *data != '\0'))
                        {
                            data++;
                        }
                        else if (*data == '"')
                        {
                            *data = 0;
                            *(start - 1) = has_escaping;
                            data++;
                            return {type::String, start, data - 1};
                        }
                        else if (*data == '\\')
                        {
                            has_escaping = 1;
                            data++;
                            switch (*data)
                            {
                                case 'u':
                                {
                                    auto check = [](char c) {
                                        return ('0' <= c && c <= '9') ||
                                               ('a' <= c && c <= 'f') ||
                                               ('A' <= c && c <= 'F');
                                    };
                                    if (!(check(*(data + 1)) &&
                                          check(*(data + 2)) &&
                                          check(*(data + 3)) &&
                                          check(*(data + 4))))
                                        return {};
                                }
                                    data += 5;
                                    break;
                                case '"':
                                case '\\':
                                case '/':
                                case 'b':
                                case 'f':
                                case 'n':
                                case 'r':
                                case 't':
                                    data++;
                                    break;
                                default:
                                    return {};
                            }
                        }
                        else
                            return {};
                    }
                    return {};
                }

                rvalue decode_list(unsigned depth)
                {
                    rvalue ret(type::List);
                    if (CROW_UNLIKELY(!consume('[')) || CROW_UNLIKELY(depth > max_depth))
                    {
                        ret.set_error();
                        return ret;
                    }
                    ws_skip();
                    if (CROW_UNLIKELY(*data == ']'))
                    {
                        data++;
                        return ret;
                    }

                    while (1)
                    {
                        auto v = decode_value(depth + 1);
                        if (CROW_UNLIKELY(!v))
                        {
                            ret.set_error();
                            break;
                        }
                        ws_skip();
                        ret.emplace_back(std::move(v));
                        if (*data == ']')
                        {
                            data++;
                            break;
                        }
                        if (CROW_UNLIKELY(!consume(',')))
                        {
                            ret.set_error();
                            break;
                        }
                        ws_skip();
                    }
                    return ret;
                }

                rvalue decode_number()
                {
                    char* start = data;

                    enum NumberParsingState
                    {
                        Minus,
                        AfterMinus,
                        ZeroFirst,
                        Digits,
                        DigitsAfterPoints,
                        E,
                        DigitsAfterE,
                        Invalid,
                    } state{Minus};
                    while (CROW_LIKELY(state != Invalid))
                    {
                        switch (*data)
                        {
                            case '0':
                                state = static_cast<NumberParsingState>("\2\2\7\3\4\6\6"[state]);
                                /*if (state == NumberParsingState::Minus || state == NumberParsingState::AfterMinus)
                                {
                                    state = NumberParsingState::ZeroFirst;
                                }
                                else if (state == NumberParsingState::Digits ||
                                    state == NumberParsingState::DigitsAfterE ||
                                    state == NumberParsingState::DigitsAfterPoints)
                                {
                                    // ok; pass
                                }
                                else if (state == NumberParsingState::E)
                                {
                                    state = NumberParsingState::DigitsAfterE;
                                }
                                else
                                    return {};*/
                                break;
                            case '1':
                            case '2':
                            case '3':
                            case '4':
                            case '5':
                            case '6':
                            case '7':
                            case '8':
                            case '9':
                                state = static_cast<NumberParsingState>("\3\3\7\3\4\6\6"[state]);
                                while (*(data + 1) >= '0' && *(data + 1) <= '9')
                                    data++;
                                /*if (state == NumberParsingState::Minus || state == NumberParsingState::AfterMinus)
                                {
                                    state = NumberParsingState::Digits;
                                }
                                else if (state == NumberParsingState::Digits ||
                                    state == NumberParsingState::DigitsAfterE ||
                                    state == NumberParsingState::DigitsAfterPoints)
                                {
                                    // ok; pass
                                }
                                else if (state == NumberParsingState::E)
                                {
                                    state = NumberParsingState::DigitsAfterE;
                                }
                                else
                                    return {};*/
                                break;
                            case '.':
                                state = static_cast<NumberParsingState>("\7\7\4\4\7\7\7"[state]);
                                /*
                                if (state == NumberParsingState::Digits || state == NumberParsingState::ZeroFirst)
                                {
                                    state = NumberParsingState::DigitsAfterPoints;
                                }
                                else
                                    return {};
                                */
                                break;
                            case '-':
                                state = static_cast<NumberParsingState>("\1\7\7\7\7\6\7"[state]);
                                /*if (state == NumberParsingState::Minus)
                                {
                                    state = NumberParsingState::AfterMinus;
                                }
                                else if (state == NumberParsingState::E)
                                {
                                    state = NumberParsingState::DigitsAfterE;
                                }
                                else
                                    return {};*/
                                break;
                            case '+':
                                state = static_cast<NumberParsingState>("\7\7\7\7\7\6\7"[state]);
                                /*if (state == NumberParsingState::E)
                                {
                                    state = NumberParsingState::DigitsAfterE;
                                }
                                else
                                    return {};*/
                                break;
                            case 'e':
                            case 'E':
                                state = static_cast<NumberParsingState>("\7\7\7\5\5\7\7"[state]);
                                /*if (state == NumberParsingState::Digits ||
                                    state == NumberParsingState::DigitsAfterPoints)
                                {
                                    state = NumberParsingState::E;
                                }
                                else
                                    return {};*/
                                break;
                            default:
                                if (CROW_LIKELY(state == NumberParsingState::ZeroFirst ||
                                                state == NumberParsingState::Digits ||
                                                state == NumberParsingState::DigitsAfterPoints ||
                                                state == NumberParsingState::DigitsAfterE))
                                    return {type::Number, start, data};
                                else
                                    return {};
                        }
                        data++;
                    }

                    return {};
                }


                rvalue decode_value(unsigned depth)
                {
                    switch (*data)
                    {
                        case '[':
                            return decode_list(depth + 1);
                        case '{':
                            return decode_object(depth + 1);
                        case '"':
                            return decode_string();
                        case 't':
                            if ( //e-data >= 4 &&
                              data[1] == 'r' &&
                              data[2] == 'u' &&
                              data[3] == 'e')
                            {
                                data += 4;
                                return {type::True};
                            }
                            else
                                return {};
                        case 'f':
                            if ( //e-data >= 5 &&
                              data[1] == 'a' &&
                              data[2] == 'l' &&
                              data[3] == 's' &&
                              data[4] == 'e')
                            {
                                data += 5;
                                return {type::False};
                            }
                            else
                                return {};
                        case 'n':
                            if ( //e-data >= 4 &&
                              data[1] == 'u' &&
                              data[2] == 'l' &&
                              data[3] == 'l')
                            {
                                data += 4;
                                return {type::Null};
                            }
                            else
                                return {};
                        //case '1': case '2': case '3':
                        //case '4': case '5': case '6':
                        //case '7': case '8': case '9':
                        //case '0': case '-':
                        default:
                            return decode_number();
                    }
                    return {};
                }

                rvalue decode_object(unsigned depth)
                {
                    rvalue ret(type::Object);
                    if (CROW_UNLIKELY(!consume('{')) || CROW_UNLIKELY(depth > max_depth))
                    {
                        ret.set_error();
                        return ret;
                    }

                    ws_skip();

                    if (CROW_UNLIKELY(*data == '}'))
                    {
                        data++;
                        return ret;
                    }

                    while (1)
                    {
                        auto t = decode_string();
                        if (CROW_UNLIKELY(!t))
                        {
                            ret.set_error();
                            break;
                        }

                        ws_skip();
                        if (CROW_UNLIKELY(!consume(':')))
                        {
                            ret.set_error();
                            break;
                        }

                        // TODO(ipkn) caching key to speed up (flyweight?)
                        // I have no idea how flyweight could apply here, but maybe some speedup can happen if we stopped checking type since decode_string returns a string anyway
                        auto key = t.s();

                        ws_skip();
                        auto v = decode_value(depth + 1);
                        if (CROW_UNLIKELY(!v))
                        {
                            ret.set_error();
                            break;
                        }
                        ws_skip();

                        v.key_ = std::move(key);
                        ret.emplace_back(std::move(v));
                        if (CROW_UNLIKELY(*data == '}'))
                        {
                            data++;
                            break;
                        }
                        if (CROW_UNLIKELY(!consume(',')))
                        {
                            ret.set_error();
                            break;
                        }
                        ws_skip();
                    }
                    return ret;
                }

                rvalue parse()
                {
                    ws_skip();
                    auto ret = decode_value(0); // or decode object?
                    ws_skip();
                    if (ret && *data != '\0')
                        ret.set_error();
                    return ret;
                }

                char* data;
            };
            return Parser(data, size).parse();
        }
        inline rvalue load(const char* data, size_t size)
        {
            char* s = new char[size + 1];
            memcpy(s, data, size);
            s[size] = 0;
            auto ret = load_nocopy_internal(s, size);
            if (ret)
                ret.key_.force(s, size);
            else
                delete[] s;
            return ret;
        }

        inline rvalue load(const char* data)
        {
            return load(data, strlen(data));
        }

        inline rvalue load(const std::string& str)
        {
            return load(str.data(), str.size());
        }

        struct wvalue_reader;

        /// JSON write value.

        ///
        /// Value can mean any json value, including a JSON object.<br>
        /// Write means this class is used to primarily assemble JSON objects using keys and values and export those into a string.
        class wvalue : public returnable
        {
            friend class crow::mustache::template_t;
            friend struct wvalue_reader;

        public:
            using object =
#ifdef CROW_JSON_USE_MAP
              std::map<std::string, wvalue>;
#else
              std::unordered_map<std::string, wvalue>;
#endif

            using list = std::vector<wvalue>;

            type t() const { return t_; }

            /// Create an empty json value (outputs "{}" instead of a "null" string)
            static crow::json::wvalue empty_object() { return crow::json::wvalue(std::move(crow::json::wvalue::object())); }

        private:
            type t_{type::Null};         ///< The type of the value.
            num_type nt{num_type::Null}; ///< The specific type of the number if \ref t_ is a number.
            union number
            {
                double d;
                int64_t si;
                uint64_t ui;

            public:
                constexpr number() noexcept:
                  ui() {} /* default constructor initializes unsigned integer. */
                constexpr number(std::uint64_t value) noexcept:
                  ui(value) {}
                constexpr number(std::int64_t value) noexcept:
                  si(value) {}
                constexpr number(double value) noexcept:
                  d(value) {}
            } num;                                      ///< Value if type is a number.
            std::string s;                              ///< Value if type is a string.
            std::unique_ptr<list> l;                    ///< Value if type is a list.
            std::unique_ptr<object> o;                  ///< Value if type is a JSON object.
            std::function<std::string(std::string&)> f; ///< Value if type is a function (C++ lambda)

        public:
            wvalue():
              returnable("application/json") {}

            wvalue(std::nullptr_t):
              returnable("application/json"), t_(type::Null) {}

            wvalue(bool value):
              returnable("application/json"), t_(value ? type::True : type::False) {}

            wvalue(std::uint8_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast<std::uint64_t>(value)) {}
            wvalue(std::uint16_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast<std::uint64_t>(value)) {}
            wvalue(std::uint32_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast<std::uint64_t>(value)) {}
            wvalue(std::uint64_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast<std::uint64_t>(value)) {}

            wvalue(std::int8_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast<std::int64_t>(value)) {}
            wvalue(std::int16_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast<std::int64_t>(value)) {}
            wvalue(std::int32_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast<std::int64_t>(value)) {}
            wvalue(std::int64_t value):
              returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast<std::int64_t>(value)) {}

            wvalue(float value):
              returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast<double>(value)) {}
            wvalue(double value):
              returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast<double>(value)) {}

            wvalue(char const* value):
              returnable("application/json"), t_(type::String), s(value) {}

            wvalue(std::string const& value):
              returnable("application/json"), t_(type::String), s(value) {}
            wvalue(std::string&& value):
              returnable("application/json"), t_(type::String), s(std::move(value)) {}

            wvalue(std::initializer_list<std::pair<std::string const, wvalue>> initializer_list):
              returnable("application/json"), t_(type::Object), o(new object(initializer_list)) {}

            wvalue(object const& value):
              returnable("application/json"), t_(type::Object), o(new object(value)) {}
            wvalue(object&& value):
              returnable("application/json"), t_(type::Object), o(new object(std::move(value))) {}

            wvalue(const list& r):
              returnable("application/json")
            {
                t_ = type::List;
                l = std::unique_ptr<list>(new list{});
                l->reserve(r.size());
                for (auto it = r.begin(); it != r.end(); ++it)
                    l->emplace_back(*it);
            }
            wvalue(list& r):
              returnable("application/json")
            {
                t_ = type::List;
                l = std::unique_ptr<list>(new list{});
                l->reserve(r.size());
                for (auto it = r.begin(); it != r.end(); ++it)
                    l->emplace_back(*it);
            }

            /// Create a write value from a read value (useful for editing JSON strings).
            wvalue(const rvalue& r):
              returnable("application/json")
            {
                t_ = r.t();
                switch (r.t())
                {
                    case type::Null:
                    case type::False:
                    case type::True:
                    case type::Function:
                        return;
                    case type::Number:
                        nt = r.nt();
                        if (nt == num_type::Floating_point)
                            num.d = r.d();
                        else if (nt == num_type::Signed_integer)
                            num.si = r.i();
                        else
                            num.ui = r.u();
                        return;
                    case type::String:
                        s = r.s();
                        return;
                    case type::List:
                        l = std::unique_ptr<list>(new list{});
                        l->reserve(r.size());
                        for (auto it = r.begin(); it != r.end(); ++it)
                            l->emplace_back(*it);
                        return;
                    case type::Object:
                        o = std::unique_ptr<object>(new object{});
                        for (auto it = r.begin(); it != r.end(); ++it)
                            o->emplace(it->key(), *it);
                        return;
                }
            }

            wvalue(const wvalue& r):
              returnable("application/json")
            {
                t_ = r.t();
                switch (r.t())
                {
                    case type::Null:
                    case type::False:
                    case type::True:
                        return;
                    case type::Number:
                        nt = r.nt;
                        if (nt == num_type::Floating_point)
                            num.d = r.num.d;
                        else if (nt == num_type::Signed_integer)
                            num.si = r.num.si;
                        else
                            num.ui = r.num.ui;
                        return;
                    case type::String:
                        s = r.s;
                        return;
                    case type::List:
                        l = std::unique_ptr<list>(new list{});
                        l->reserve(r.size());
                        for (auto it = r.l->begin(); it != r.l->end(); ++it)
                            l->emplace_back(*it);
                        return;
                    case type::Object:
                        o = std::unique_ptr<object>(new object{});
                        o->insert(r.o->begin(), r.o->end());
                        return;
                    case type::Function:
                        f = r.f;
                }
            }

            wvalue(wvalue&& r):
              returnable("application/json")
            {
                *this = std::move(r);
            }

            wvalue& operator=(wvalue&& r)
            {
                t_ = r.t_;
                nt = r.nt;
                num = r.num;
                s = std::move(r.s);
                l = std::move(r.l);
                o = std::move(r.o);
                return *this;
            }

            /// Used for compatibility, same as \ref reset()
            void clear()
            {
                reset();
            }

            void reset()
            {
                t_ = type::Null;
                l.reset();
                o.reset();
            }

            wvalue& operator=(std::nullptr_t)
            {
                reset();
                return *this;
            }
            wvalue& operator=(bool value)
            {
                reset();
                if (value)
                    t_ = type::True;
                else
                    t_ = type::False;
                return *this;
            }

            wvalue& operator=(double value)
            {
                reset();
                t_ = type::Number;
                num.d = value;
                nt = num_type::Floating_point;
                return *this;
            }

            wvalue& operator=(unsigned short value)
            {
                reset();
                t_ = type::Number;
                num.ui = value;
                nt = num_type::Unsigned_integer;
                return *this;
            }

            wvalue& operator=(short value)
            {
                reset();
                t_ = type::Number;
                num.si = value;
                nt = num_type::Signed_integer;
                return *this;
            }

            wvalue& operator=(long long value)
            {
                reset();
                t_ = type::Number;
                num.si = value;
                nt = num_type::Signed_integer;
                return *this;
            }

            wvalue& operator=(long value)
            {
                reset();
                t_ = type::Number;
                num.si = value;
                nt = num_type::Signed_integer;
                return *this;
            }

            wvalue& operator=(int value)
            {
                reset();
                t_ = type::Number;
                num.si = value;
                nt = num_type::Signed_integer;
                return *this;
            }

            wvalue& operator=(unsigned long long value)
            {
                reset();
                t_ = type::Number;
                num.ui = value;
                nt = num_type::Unsigned_integer;
                return *this;
            }

            wvalue& operator=(unsigned long value)
            {
                reset();
                t_ = type::Number;
                num.ui = value;
                nt = num_type::Unsigned_integer;
                return *this;
            }

            wvalue& operator=(unsigned int value)
            {
                reset();
                t_ = type::Number;
                num.ui = value;
                nt = num_type::Unsigned_integer;
                return *this;
            }

            wvalue& operator=(const char* str)
            {
                reset();
                t_ = type::String;
                s = str;
                return *this;
            }

            wvalue& operator=(const std::string& str)
            {
                reset();
                t_ = type::String;
                s = str;
                return *this;
            }

            wvalue& operator=(list&& v)
            {
                if (t_ != type::List)
                    reset();
                t_ = type::List;
                if (!l)
                    l = std::unique_ptr<list>(new list{});
                l->clear();
                l->resize(v.size());
                size_t idx = 0;
                for (auto& x : v)
                {
                    (*l)[idx++] = std::move(x);
                }
                return *this;
            }

            template<typename T>
            wvalue& operator=(const std::vector<T>& v)
            {
                if (t_ != type::List)
                    reset();
                t_ = type::List;
                if (!l)
                    l = std::unique_ptr<list>(new list{});
                l->clear();
                l->resize(v.size());
                size_t idx = 0;
                for (auto& x : v)
                {
                    (*l)[idx++] = x;
                }
                return *this;
            }

            wvalue& operator=(std::initializer_list<std::pair<std::string const, wvalue>> initializer_list)
            {
                if (t_ != type::Object)
                {
                    reset();
                    t_ = type::Object;
                    o = std::unique_ptr<object>(new object(initializer_list));
                }
                else
                {
#if defined(__APPLE__) || defined(__MACH__) || defined (__FreeBSD__)  || defined (__ANDROID__)
                    o = std::unique_ptr<object>(new object(initializer_list));
#else
                    (*o) = initializer_list;
#endif
                }
                return *this;
            }

            wvalue& operator=(object const& value)
            {
                if (t_ != type::Object)
                {
                    reset();
                    t_ = type::Object;
                    o = std::unique_ptr<object>(new object(value));
                }
                else
                {
#if defined(__APPLE__) || defined(__MACH__) || defined (__FreeBSD__)
                    o = std::unique_ptr<object>(new object(value));
#else
                    (*o) = value;
#endif
                }
                return *this;
            }

            wvalue& operator=(object&& value)
            {
                if (t_ != type::Object)
                {
                    reset();
                    t_ = type::Object;
                    o = std::unique_ptr<object>(new object(std::move(value)));
                }
                else
                {
                    (*o) = std::move(value);
                }
                return *this;
            }

            wvalue& operator=(std::function<std::string(std::string&)>&& func)
            {
                reset();
                t_ = type::Function;
                f = std::move(func);
                return *this;
            }

            wvalue& operator[](unsigned index)
            {
                if (t_ != type::List)
                    reset();
                t_ = type::List;
                if (!l)
                    l = std::unique_ptr<list>(new list{});
                if (l->size() < index + 1)
                    l->resize(index + 1);
                return (*l)[index];
            }

            int count(const std::string& str)
            {
                if (t_ != type::Object)
                    return 0;
                if (!o)
                    return 0;
                return o->count(str);
            }

            wvalue& operator[](const std::string& str)
            {
                if (t_ != type::Object)
                    reset();
                t_ = type::Object;
                if (!o)
                    o = std::unique_ptr<object>(new object{});
                return (*o)[str];
            }

            std::vector<std::string> keys() const
            {
                if (t_ != type::Object)
                    return {};
                std::vector<std::string> result;
                for (auto& kv : *o)
                {
                    result.push_back(kv.first);
                }
                return result;
            }

            std::string execute(std::string txt = "") const //Not using reference because it cannot be used with a default rvalue
            {
                if (t_ != type::Function)
                    return "";
                return f(txt);
            }

            /// If the wvalue is a list, it returns the length of the list, otherwise it returns 1.
            std::size_t size() const
            {
                if (t_ != type::List)
                    return 1;
                return l->size();
            }

            /// Returns an estimated size of the value in bytes.
            size_t estimate_length() const
            {
                switch (t_)
                {
                    case type::Null: return 4;
                    case type::False: return 5;
                    case type::True: return 4;
                    case type::Number: return 30;
                    case type::String: return 2 + s.size() + s.size() / 2;
                    case type::List:
                    {
                        size_t sum{};
                        if (l)
                        {
                            for (auto& x : *l)
                            {
                                sum += 1;
                                sum += x.estimate_length();
                            }
                        }
                        return sum + 2;
                    }
                    case type::Object:
                    {
                        size_t sum{};
                        if (o)
                        {
                            for (auto& kv : *o)
                            {
                                sum += 2;
                                sum += 2 + kv.first.size() + kv.first.size() / 2;
                                sum += kv.second.estimate_length();
                            }
                        }
                        return sum + 2;
                    }
                    case type::Function:
                        return 0;
                }
                return 1;
            }

        private:
            inline void dump_string(const std::string& str, std::string& out) const
            {
                out.push_back('"');
                escape(str, out);
                out.push_back('"');
            }

            inline void dump_internal(const wvalue& v, std::string& out) const
            {
                switch (v.t_)
                {
                    case type::Null: out += "null"; break;
                    case type::False: out += "false"; break;
                    case type::True: out += "true"; break;
                    case type::Number:
                    {
                        if (v.nt == num_type::Floating_point)
                        {
                            if (isnan(v.num.d) || isinf(v.num.d))
                            {
                                out += "null";
                                CROW_LOG_WARNING << "Invalid JSON value detected (" << v.num.d << "), value set to null";
                                break;
                            }
                            enum
                            {
                                start,
                                decp, // Decimal point
                                zero
                            } f_state;
                            char outbuf[128];
#ifdef _MSC_VER
			    sprintf_s(outbuf, sizeof(outbuf), "%f", v.num.d);
#else
			    snprintf(outbuf, sizeof(outbuf), "%f", v.num.d);
#endif
                            char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0
                            f_state = start;
                            while (*p != '\0')
                            {
                                //std::cout << *p << std::endl;
                                char ch = *p;
                                switch (f_state)
                                {
                                    case start: // Loop and lookahead until a decimal point is found
                                        if (ch == '.')
                                        {
                                            char fch = *(p + 1);
                                            // if the first character is 0, leave it be (this is so that "1.00000" becomes "1.0" and not "1.")
                                            if (fch != '\0' && fch == '0') p++;
                                            f_state = decp;
                                        }
                                        p++;
                                        break;
                                    case decp: // Loop until a 0 is found, if found, record its position
                                        if (ch == '0')
                                        {
                                            f_state = zero;
                                            o = p;
                                        }
                                        p++;
                                        break;
                                    case zero: // if a non 0 is found (e.g. 1.00004) remove the earlier recorded 0 position and look for more trailing 0s
                                        if (ch != '0')
                                        {
                                            o = nullptr;
                                            f_state = decp;
                                        }
                                        p++;
                                        break;
                                }
                            }
                            if (o != nullptr) // if any trailing 0s are found, terminate the string where they begin
                                *o = '\0';
                            out += outbuf;
                        }
                        else if (v.nt == num_type::Signed_integer)
                        {
                            out += std::to_string(v.num.si);
                        }
                        else
                        {
                            out += std::to_string(v.num.ui);
                        }
                    }
                    break;
                    case type::String: dump_string(v.s, out); break;
                    case type::List:
                    {
                        out.push_back('[');
                        if (v.l)
                        {
                            bool first = true;
                            for (auto& x : *v.l)
                            {
                                if (!first)
                                {
                                    out.push_back(',');
                                }
                                first = false;
                                dump_internal(x, out);
                            }
                        }
                        out.push_back(']');
                    }
                    break;
                    case type::Object:
                    {
                        out.push_back('{');
                        if (v.o)
                        {
                            bool first = true;
                            for (auto& kv : *v.o)
                            {
                                if (!first)
                                {
                                    out.push_back(',');
                                }
                                first = false;
                                dump_string(kv.first, out);
                                out.push_back(':');
                                dump_internal(kv.second, out);
                            }
                        }
                        out.push_back('}');
                    }
                    break;

                    case type::Function:
                        out += "custom function";
                        break;
                }
            }

        public:
            std::string dump() const
            {
                std::string ret;
                ret.reserve(estimate_length());
                dump_internal(*this, ret);
                return ret;
            }
        };

        // Used for accessing the internals of a wvalue
        struct wvalue_reader
        {
            int64_t get(int64_t fallback)
            {
                if (ref.t() != type::Number || ref.nt == num_type::Floating_point)
                    return fallback;
                return ref.num.si;
            }

            double get(double fallback)
            {
                if (ref.t() != type::Number || ref.nt != num_type::Floating_point)
                    return fallback;
                return ref.num.d;
            }

            bool get(bool fallback)
            {
                if (ref.t() == type::True) return true;
                if (ref.t() == type::False) return false;
                return fallback;
            }

            std::string get(const std::string& fallback)
            {
                if (ref.t() != type::String) return fallback;
                return ref.s;
            }

            const wvalue& ref;
        };

        //std::vector<asio::const_buffer> dump_ref(wvalue& v)
        //{
        //}
    } // namespace json
} // namespace crow
