﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lacjam.Core.Domain;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.Serialization.Serializers;
using MongoRepository;

namespace Lacjam.Core.Infrastructure.Database
{
    /// <summary>
    /// A HashSet with a specific comparer that prevents duplicate Entity Ids
    /// </summary>
    public class EntityHashSet<T> : HashSet<T>, IBsonSerializable where T : LacjamEntity
    {
        private class EntityComparer : IEqualityComparer<T>
        {
            public bool Equals(T x, T y) { return x != null && (y != null && (x.Id != null && x.Id.Equals(y.Id))); }
            public int GetHashCode(T obj)
            {
                if (obj == null) return 0;

                if (string.IsNullOrEmpty(obj.Id)) return 0;

                return obj.Id.GetHashCode();
            }
        }

        public EntityHashSet()
            : base(new EntityComparer())
        {
        }

        public EntityHashSet(IEnumerable<T> values)
            : base(values, new EntityComparer())
        {
        }

        public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
        {
            if (nominalType != typeof(EntityHashSet<T>)) throw new ArgumentException("Cannot deserialize anything but self");
            var ser = new ArraySerializer<T>();
            var type = typeof (T[]);
            var items = ser.Deserialize(bsonReader, type, new ArraySerializationOptions()) as T[];
            return new EntityHashSet<T>(items);

        }

        public bool GetDocumentId(out object id, out Type idNominalType, out IIdGenerator idGenerator)
        {
            id = null;
            idGenerator = null;
            idNominalType = null;
            return false;
        }

        public void Serialize(BsonWriter bsonWriter, Type nominalType, IBsonSerializationOptions options)
        {
            if (nominalType != typeof(EntityHashSet<T>)) throw new ArgumentException("Cannot deserialize anything but self");
            var ser = new ArraySerializer<T>();
            ser.Serialize(bsonWriter, typeof(T[]), this.ToArray(), new ArraySerializationOptions());
        }

        public void SetDocumentId(object id)
        {
            return;
        }
    }
}
