using Newtonsoft.Json;
using Pilz.GeneralEventArgs;
using System.IO;

namespace Pilz.Configuration;

public static class ConfigurationSerializer
{
    public static event GettingJsonSerializerEventHandler GettingJsonSerializer;

    public delegate void GettingJsonSerializerEventHandler(object instance, GetValueEventArgs<JsonSerializer> e);

    private static JsonSerializer GetJsonSerializer(SimpleConfiguration instance)
    {
        var args = new GetValueEventArgs<JsonSerializer>(JsonSerializer.CreateDefault());
        GettingJsonSerializer?.Invoke(instance, args);
        return args.Value;
    }

    /// <summary>
    /// Writes the given instance to a string and return it.
    /// </summary>
    /// <param name="instance">The configuration instance that should be serialized.</param>
    /// <returns>The content of the configuration instance as string.</returns>
    public static string WriteToString(SimpleConfiguration instance)
    {
        var tw = new StringWriter();
        GetJsonSerializer(instance).Serialize(tw, instance);
        string txt = tw.ToString();
        tw.Close();
        return txt;
    }

    /// <summary>
    /// Write the given instance to a given stream.
    /// </summary>
    /// <param name="instance">The configuration instance that should be serialized.</param>
    /// <param name="stream">The stream where the content should be written to.</param>
    public static void WriteToStream(SimpleConfiguration instance, Stream stream)
    {
        var sr = new StreamWriter(stream);
        sr.Write(WriteToString(instance));
    }

    /// <summary>
    /// Writes the given instance to the given filePath as text file.
    /// </summary>
    /// <param name="instance">The configuration instance that should be serialized.</param>
    /// <param name="filePath">The file path where the content should be written to. The file will be created or overwritten.</param>
    public static void WriteToFile(SimpleConfiguration instance, string filePath)
    {
        var fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite);
        WriteToStream(instance, fs);
        fs.Close();
    }

    /// <summary>
    /// Reads a configuratin from the given string and returns an instance of it.
    /// </summary>
    /// <typeparam name="T">The type of the configuration class to instance.</typeparam>
    /// <param name="content">The content of the configuration as string.</param>
    /// <returns></returns>
    public static T ReadFromString<T>(string content) where T : SimpleConfiguration
    {
        var sr = new StringReader(content);
        T instance = (T)GetJsonSerializer(null).Deserialize(sr, typeof(T));
        sr.Close();
        return instance;
    }

    /// <summary>
    /// Read a configuration from the given string and put them to the given instance.
    /// </summary>
    /// <param name="instance">The instance to populate with the configuration.</param>
    /// <param name="content">The content of the configuration as string.</param>
    public static void ReadFromString(SimpleConfiguration instance, string content)
    {
        var sr = new StringReader(content);
        GetJsonSerializer(null).Populate(sr, content);
        sr.Close();
    }

    /// <summary>
    /// Reads a configuratin from the given string and returns an instance of it.
    /// </summary>
    /// <typeparam name="T">The type of the configuration class to instance.</typeparam>
    /// <param name="stream">The stream with the content of the configuration.</param>
    /// <returns></returns>
    public static T ReadFromStream<T>(Stream stream) where T : SimpleConfiguration
    {
        return ReadFromString<T>(GetContentOfStream(stream));
    }

    /// <summary>
    /// Read a configuration from the given string and put them to the given instance.
    /// </summary>
    /// <param name="instance">The instance to populate with the configuration.</param>
    /// <param name="stream">The stream with the content of the configuration.</param>
    public static void ReadFromStream(SimpleConfiguration instance, Stream stream)
    {
        ReadFromString(instance, GetContentOfStream(stream));
    }

    /// <summary>
    /// Reads a configuratin from the given string and returns an instance of it.
    /// </summary>
    /// <typeparam name="T">The type of the configuration class to instance.</typeparam>
    /// <param name="filePath">The path to the file with the content of the configuration.</param>
    /// <returns></returns>
    public static T ReadFromFile<T>(string filePath) where T : SimpleConfiguration
    {
        return ReadFromString<T>(GetContentOfFile(filePath));
    }

    /// <summary>
    /// Read a configuration from the given string and put them to the given instance.
    /// </summary>
    /// <param name="instance">The instance to populate with the configuration.</param>
    /// <param name="filePath">The path to the file with the content of the configuration.</param>
    public static void ReadFromFile(SimpleConfiguration instance, string filePath)
    {
        ReadFromString(instance, GetContentOfFile(filePath));
    }

    private static string GetContentOfStream(Stream stream)
    {
        var sr = new StreamReader(stream);
        return sr.ReadToEnd();
    }

    private static string GetContentOfFile(string filePath)
    {
        var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        string content = GetContentOfStream(fs);
        fs.Close();
        return content;
    }
}