using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using Core.Abstractions;

namespace DataProviders.Converters {

  public class EnumerationSerializer<T> : IBsonSerializer<T> where T : Enumeration {
    private T Deserialize(IBsonReader reader) => Enumeration.FromValue<T>(BsonSerializer.Deserialize<int>(reader));
    private void Serialize(IBsonWriter writer, T value) => BsonSerializer.Serialize(writer, value.Id);

    public Type ValueType { get => typeof(T); }

    public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader);
    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => Serialize(context.Writer, (T)value);


    T IBsonSerializer<T>.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader);
    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value) => Serialize(context.Writer, value);
  }

  public class EnumerationListSerializer<T> : IBsonSerializer<List<T>>, IBsonArraySerializer where T : Enumeration { 
    private List<T> Deserialize(IBsonReader reader) {
      var type = reader.GetCurrentBsonType();
      var response = new List<T>();

      switch (type) {
        case BsonType.Array:
          reader.ReadStartArray();
          while (reader.ReadBsonType() != BsonType.EndOfDocument) {
            response.Add(Enumeration.FromValue<T>(BsonSerializer.Deserialize<int>(reader)));
          }

          reader.ReadEndArray();
          return response;

        default:
          throw new NotImplementedException($"No implementation to deserialize {type}");
      }
    }
    private void Serialize(IBsonWriter writer, List<T> values) {
      if (values != null) {
        writer.WriteStartArray();

        foreach (var value in values) {
          writer.WriteInt32(value.Id);
        }

        writer.WriteEndArray();
      }

    }

    public Type ValueType { get => typeof(List<T>); }

    public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader);
    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) => Serialize(context.Writer, (List<T>)value);

    List<T> IBsonSerializer<List<T>>.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => Deserialize(context.Reader);
    public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, List<T> value) => Serialize(context.Writer, value);

    public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) {
      string elementName = null;
      var serializer = BsonSerializer.LookupSerializer(typeof(string));
      var nominalType = typeof(string);
      serializationInfo = new BsonSerializationInfo(elementName, serializer, nominalType);
      return true;
    }
  }
}
