﻿namespace Dike.Core.Linq.Extensions
{
    using Conditions;
    using System;
    using System.Linq;
    using System.Linq.Expressions;

    /// <summary>
    /// Defines extension methods for <see cref="IQueryable{T}"/>.
    /// </summary>
    public static class QueryableExtensions
    {
        #region Methods

        /// <summary>
        /// Can be used to return a paged query result.
        /// </summary>
        /// <typeparam name="T">Type of items in the query.</typeparam>
        /// <param name="query">The query to paginate.</param>
        /// <param name="skipCount">The skip count.</param>
        /// <param name="maxResultCount">The maximum result count.</param>
        /// <returns>The paged IQueryable.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="query"/> can not be <c>null</c>.</exception>
        public static IQueryable<T> PageBy<T>(this IQueryable<T> query, int skipCount, int maxResultCount)
        {
            query.Requires(nameof(query)).IsNotNull();

            return query.Skip(skipCount).Take(maxResultCount);
        }

        /// <summary>
        /// Can be used to return a paged query result for a <see cref="Application.Services.Dto.IPagedResultRequest"/>.
        /// </summary>
        /// <typeparam name="T">Type of items in the query.</typeparam>
        /// <param name="query">The query to paginate.</param>
        /// <param name="pagedResultRequest">The paged result request.</param>
        /// <returns>The paged IQueryable.</returns>
        public static IQueryable<T> PageBy<T>(this IQueryable<T> query, Application.Services.Dto.IPagedResultRequest pagedResultRequest)
            => query.PageBy(pagedResultRequest.SkipCount, pagedResultRequest.MaxResultCount);

        /// <summary>
        /// Filters a <see cref="IQueryable{T}"/> by a given predicate if the given condition evaluates to <c>true</c>.
        /// </summary>
        /// <typeparam name="T">Type of items in the query.</typeparam>
        /// <param name="query">The query to filter.</param>
        /// <param name="condition">Condition to evaluate prior to apply the predicate.</param>
        /// <param name="predicate">The predicate to apply if <paramref name="condition"/> evaluates to <c>true</c>.</param>
        /// <returns>The filtered or unfiltered IQueryable.</returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> predicate)
            => condition ? query.Where(predicate) : query;

        /// <summary>
        /// Filters a <see cref="IQueryable{T}"/> by a given predicate if the given condition evaluates to <c>true</c>.
        /// </summary>
        /// <typeparam name="T">Type of items in the query.</typeparam>
        /// <param name="query">The query to filter.</param>
        /// <param name="condition">Condition to evaluate prior to apply the predicate.</param>
        /// <param name="predicate">The predicate to apply if <paramref name="condition"/> evaluates to <c>true</c>.</param>
        /// <returns>The filtered or unfiltered IQueryable.</returns>
        public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, int, bool>> predicate)
            => condition ? query.Where(predicate) : query;

        #endregion Methods
    }
}