using Newtonsoft.Json;
using System;
using UnityEngine;

public sealed class QuaternionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (objectType == typeof(float[]))
            return true;

        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var values = serializer.Deserialize<float[]>(reader);
        return new Quaternion(
            values.GetOrDefault(0),
            values.GetOrDefault(1),
            values.GetOrDefault(2),
            values.GetOrDefault(3)
        ).normalized;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Quaternion v = (Quaternion)value;

        writer.WriteStartArray();
        writer.WriteValue((decimal)v.x);
        writer.WriteValue((decimal)v.y);
        writer.WriteValue((decimal)v.z);
        writer.WriteValue((decimal)v.w);
        writer.WriteEndArray();
    }
}
public sealed class Vector4Converter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (objectType == typeof(float[]))
            return true;

        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var values = serializer.Deserialize<float[]>(reader);
        return new Vector4(
            values.GetOrDefault(0),
            values.GetOrDefault(1),
            values.GetOrDefault(2),
            values.GetOrDefault(3)
        );
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Vector4 v = (Vector4)value;

        writer.WriteStartArray();
        writer.WriteValue((decimal)v.x);
        writer.WriteValue((decimal)v.y);
        writer.WriteValue((decimal)v.z);
        writer.WriteValue((decimal)v.w);
        writer.WriteEndArray();
    }

    public static float GetValue(float[] self, int idx)
    {
        if (self == null || self.Length < idx || idx < 0)
            return 0;
        return self[idx];
    }
}

public sealed class Vector3Converter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (objectType == typeof(float[]))
            return true;

        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var values = serializer.Deserialize<float[]>(reader);
        return new Vector3(
            values.GetOrDefault(0),
            values.GetOrDefault(1),
            values.GetOrDefault(2)
        );
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Vector3 v = (Vector3)value;

        writer.WriteStartArray();
        writer.WriteValue((decimal)v.x);
        writer.WriteValue((decimal)v.y);
        writer.WriteValue((decimal)v.z);
        writer.WriteEndArray();
    }
}

public sealed class Vector2Converter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (objectType == typeof(Vector2))
        {
            return true;
        }
        return false;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var values = serializer.Deserialize<float[]>(reader);
        return new Vector2 (
            values.GetOrDefault(0),
            values.GetOrDefault(1)
        );
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Vector2 v = (Vector2)value;

        writer.WriteStartArray();
        writer.WriteValue((decimal)v.x);
        writer.WriteValue((decimal)v.y);
        writer.WriteEndArray();
    }
}

public static class ArrayExtention
{
    public static T GetOrDefault<T>(this T[] self, int idx, T defaultValue = default(T))
    {
        if (self == null || self.Length < idx || idx < 0)
            return defaultValue;
        return self[idx];
    }
}