#pragma once

#include "Common.h"
#include "System/String.h"
#include "System/Text/Json/JsonElement.h"
#include "System/Collections/Generic/List.h"
#include "System/Collections/Generic/Dictionary.h"
#include <type_traits>

namespace DotNetDupe {
    namespace System {
        namespace Text {
            namespace Json {

                // Primary template for converters.
                // Users can specialize this template for their own classes.
                template <typename T, typename Enable = void>
                struct JsonConverter;

                class JsonSerializer {
                public:
                    template <typename T>
                    static String Serialize(const T& value) {
                        JsonElement element = JsonConverter<T>::Write(value);
                        return element.ToString();
                    }

                    template <typename T>
                    static T Deserialize(const String& sJson) {
                        JsonElement element = JsonElement::Parse(sJson);
                        return JsonConverter<T>::Read(element);
                    }
                };

                // Specializations for Primitive types
                
                // Int
                template <>
                struct JsonConverter<int> {
                    static JsonElement Write(const int& value) {
                        return JsonElement(static_cast<double>(value));
                    }
                    static int Read(const JsonElement& element) {
                        return element.GetInt32();
                    }
                };

                // Long Long
                template <>
                struct JsonConverter<long long> {
                    static JsonElement Write(const long long& value) {
                        return JsonElement(static_cast<double>(value));
                    }
                    static long long Read(const JsonElement& element) {
                        return element.GetInt64();
                    }
                };

                // Double
                template <>
                struct JsonConverter<double> {
                    static JsonElement Write(const double& value) {
                        return JsonElement(value);
                    }
                    static double Read(const JsonElement& element) {
                        return element.GetDouble();
                    }
                };

                // Float
                template <>
                struct JsonConverter<float> {
                    static JsonElement Write(const float& value) {
                        return JsonElement(static_cast<double>(value));
                    }
                    static float Read(const JsonElement& element) {
                        return static_cast<float>(element.GetDouble());
                    }
                };

                // Bool
                template <>
                struct JsonConverter<bool> {
                    static JsonElement Write(const bool& value) {
                        return JsonElement(value);
                    }
                    static bool Read(const JsonElement& element) {
                        return element.GetBoolean();
                    }
                };

                // String
                template <>
                struct JsonConverter<String> {
                    static JsonElement Write(const String& value) {
                        return JsonElement(value);
                    }
                    static String Read(const JsonElement& element) {
                        return element.GetString();
                    }
                };

                // List<U>
                template <typename U>
                struct JsonConverter<Collections::Generic::List<U>> {
                    static JsonElement Write(const Collections::Generic::List<U>& value) {
                        JsonElement arr(JsonValueKind::Array);
                        for (int i = 0; i < value.GetCount(); ++i) {
                            arr.AddArrayElement(JsonConverter<U>::Write(value[i]));
                        }
                        return arr;
                    }
                    static Collections::Generic::List<U> Read(const JsonElement& element) {
                        Collections::Generic::List<U> lst;
                        int iLen = element.GetArrayLength();
                        for (int i = 0; i < iLen; ++i) {
                            lst.Add(JsonConverter<U>::Read(element.GetArrayElement(i)));
                        }
                        return lst;
                    }
                };

                // Dictionary<String, U>
                template <typename U>
                struct JsonConverter<Collections::Generic::Dictionary<String, U>> {
                    static JsonElement Write(const Collections::Generic::Dictionary<String, U>& value) {
                        JsonElement obj(JsonValueKind::Object);
                        auto keys = value.GetKeys();
                        for (int i = 0; i < keys.GetLength(); ++i) {
                            obj.SetProperty(keys[i], JsonConverter<U>::Write(value[keys[i]]));
                        }
                        return obj;
                    }
                    static Collections::Generic::Dictionary<String, U> Read(const JsonElement& element) {
                        Collections::Generic::Dictionary<String, U> dict;
                        auto keys = element.GetPropertyNames();
                        for (int i = 0; i < keys.GetLength(); ++i) {
                            JsonElement prop;
                            if (element.TryGetProperty(keys[i], prop)) {
                                dict.Add(keys[i], JsonConverter<U>::Read(prop));
                            }
                        }
                        return dict;
                    }
                };

            }
        }
    }
}

Generated by OpenCppCoverage (Version: 0.9.9.0)