using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace StardewModdingAPI.Toolkit.Serialization.Converters
{
    /// Handles deserialization of .
    internal class SemanticVersionConverter : JsonConverter
    {
        /*********
        ** Fields
        *********/
        /// Whether to allow non-standard extensions to semantic versioning.
        protected bool AllowNonStandard { get; set; }
        /*********
        ** Accessors
        *********/
        /// Get whether this converter can read JSON.
        public override bool CanRead { get; } = true;
        /// Get whether this converter can write JSON.
        public override bool CanWrite { get; } = true;
        /*********
        ** Public methods
        *********/
        /// Get whether this instance can convert the specified object type.
        /// The object type.
        public override bool CanConvert(Type objectType)
        {
            return typeof(ISemanticVersion).IsAssignableFrom(objectType);
        }
        /// Reads the JSON representation of the object.
        /// The JSON reader.
        /// The object type.
        /// The object being read.
        /// The calling serializer.
        public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        {
            string path = reader.Path;
            switch (reader.TokenType)
            {
                case JsonToken.StartObject:
                    return this.ReadObject(JObject.Load(reader));
                case JsonToken.String:
                    {
                        string? value = JToken.Load(reader).Value();
                        return value is not null
                            ? this.ReadString(value, path)
                            : null;
                    }
                default:
                    throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path}).");
            }
        }
        /// Writes the JSON representation of the object.
        /// The JSON writer.
        /// The value.
        /// The calling serializer.
        public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            writer.WriteValue(value?.ToString());
        }
        /*********
        ** Private methods
        *********/
        /// Read a JSON object.
        /// The JSON object to read.
        private ISemanticVersion ReadObject(JObject obj)
        {
            int major = obj.ValueIgnoreCase(nameof(ISemanticVersion.MajorVersion));
            int minor = obj.ValueIgnoreCase(nameof(ISemanticVersion.MinorVersion));
            int patch = obj.ValueIgnoreCase(nameof(ISemanticVersion.PatchVersion));
            string? prereleaseTag = obj.ValueIgnoreCase(nameof(ISemanticVersion.PrereleaseTag));
            return new SemanticVersion(major, minor, patch, prereleaseTag: prereleaseTag);
        }
        /// Read a JSON string.
        /// The JSON string value.
        /// The path to the current JSON node.
        private ISemanticVersion? ReadString(string str, string path)
        {
            if (string.IsNullOrWhiteSpace(str))
                return null;
            if (!SemanticVersion.TryParse(str, allowNonStandard: this.AllowNonStandard, out ISemanticVersion? version))
                throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path}).");
            return version;
        }
    }
}