using System;

namespace EasyDevCore.Common
{
    /// <summary>
    /// 
    /// </summary>
    public static class NullHelper
    {
        /// <summary>
        /// Default value of DBNull
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static object DefaultValue(Type type)
        {
            if (type.Equals(typeof(byte)) || type.Equals(typeof(Int16)) || type.Equals(typeof(int)) || type.Equals(typeof(Int64))
                || type.Equals(typeof(ushort)) || type.Equals(typeof(uint)) || type.Equals(typeof(UInt16)) || type.Equals(typeof(UInt32)) || type.Equals(typeof(UInt64)))
            {
                return (byte)0;
            }
            else if (type.Equals(typeof(Single)))
            {
                return (Single)0;
            }
            else if (type.Equals(typeof(double)))
            {
                return (double)0;
            }
            else if (type.Equals(typeof(decimal)))
            {
                return (decimal)0;
            }
            else if (type.Equals(typeof(DateTime)))
            {
                return DateTime.MinValue;
            }
            else if (type.Equals(typeof(string)))
            {
                return string.Empty;
            }
            else if (type.Equals(typeof(bool)))
            {
                return false;
            }
            else if (type.Equals(typeof(Guid)))
            {
                return Guid.Empty;
            }
            else if(type.IsNullable())
            {
                return null;
            }
            else
            {
                throw new NotSupportedException("Type is not supported");
            }
        }

        /// <summary>
        /// Replaces NULL with the specified replacement value.
        /// </summary>
        /// <param name="checkValue">Is the expression to be checked for NULL</param>
        /// <param name="replaceValue">Is the expression to be returned if check_value is NULL. replacement_value must have the same type as check_value.</param>
        /// <returns>The value of check_value is returned if it is not NULL; otherwise, replacement_value is returned</returns>
        public static T IsNull<T>(T checkValue, T replaceValue)
        {
            if (checkValue == null || Convert.IsDBNull(checkValue))
            {
                return (T)replaceValue;
            }
            return checkValue;
        }

        /// <summary>
        /// Return first value is not null
        /// </summary>
        /// <param name="args">values</param>
        /// <returns></returns>
        public static T Coalesce<T>(params T[] args)
        {
            foreach (T value in args)
            {
                if (value != null && !Convert.IsDBNull(value))
                {
                    return value;
                }
            }
            return default;
        }

        /// <summary>
        /// Convert Null value to default value
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T NullToDefault<T>(T value)
        {
#pragma warning disable CS8604 // Possible null reference argument.
            return (T)IsNull(value, DefaultValue(typeof(T)));
#pragma warning restore CS8604 // Possible null reference argument.
        }


        /// <summary>
        /// Values if null or empty.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input">The input.</param>
        /// <param name="defaultIfNullOrEmpty">The default if null or empty.</param>
        /// <returns></returns>
        public static T ValueIfNullOrEmpty<T>(this T input, T defaultIfNullOrEmpty)
        {
            if (input is string)
            {
                return string.IsNullOrWhiteSpace(input.ToString()) ? defaultIfNullOrEmpty : input;
            }
            else
            {
                return (input is null ? defaultIfNullOrEmpty : input);
            }
        }


        /// <summary>
        /// Nulls if value equals checkvalue.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value">The value.</param>
        /// <param name="checkValue">The check value.</param>
        /// <returns>null if equals else return value</returns>
        public static T NullIf<T>(T value, T checkValue)
        {
            if (value is null || checkValue is null || object.Equals(value, checkValue))
            {
                return default(T);
            }
            return value;
        }
    }
}
