using System;

namespace Kit.Helpers.Extension.Entities
{

    public interface IIdTitle<TId, TTitle>
    {
        TId Id { get; set; }
        TTitle Title { get; set; }
    }

    public interface IIdOnly<TId>
    {
        TId Id { get; set; }
    }

    public class IdTitle : IInt32Id, IEquatable<IdTitle>, IIdTitle<int, string>, ICloneable
    {
        public IdTitle() { }

        public IdTitle(int id, string title) : this()
        {
            this.Id = id;
            this.Title = title;
        }

        public virtual int Id { get; set; }
        public virtual string Title { get; set; }

        #region Empty

        public static readonly int EmptyId = -1;
        public static readonly string EmptyTitle = "n/a";

        public static TEntity GetEmpty<TEntity>()
            where TEntity : IdTitle, new()
        {
            return new TEntity
            {
                Id = EmptyId,
                Title = EmptyTitle
            };
        }

        public static IdTitle GetEmpty()
        {
            return GetEmpty<IdTitle>();
        }

        #endregion

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (!(obj is IdTitle)) return false;
            return Equals((IdTitle)obj);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(IdTitle other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return other.Id == Id && Equals(other.Title, Title);
        }

        /// <summary>
        /// Serves as a hash function for a particular type. 
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            unchecked
            {
                return (Id * 397) ^ (Title != null ? Title.GetHashCode() : 0);
            }
        }

        public object Clone()
        {
            return new IdTitle
            {
                Id = Id,
                Title = Title
            };
        }

        public static bool operator ==(IdTitle left, IdTitle right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(IdTitle left, IdTitle right)
        {
            return !Equals(left, right);
        }
    }

    public class IdTitle<TId, TTitle> : IIdTitle<TId, TTitle>
    {
        public IdTitle() { }

        public IdTitle(TId id, TTitle title) : this()
        {
            this.Id = id;
            this.Title = title;
        }
        public IdTitle(IdTitle<TId, TTitle> idTitle) : this()
        {
            this.Id = idTitle.Id;
            this.Title = idTitle.Title;
        }

        public virtual TId Id { get; set; }
        public virtual TTitle Title { get; set; }
    }

    public class IdOnly<TId> : IIdOnly<TId>
        where TId : struct
    {
        public IdOnly() { }

        public IdOnly(TId id) : this()
        {
            this.Id = id;
        }

        public IdOnly(IdOnly<TId> idTitle) : this()
        {
            this.Id = idTitle.Id;
        }

        public virtual TId Id { get; set; }
    }
}