﻿namespace Play.Functional.Haskell.Rules
{
    using System;

    // To be completed.
    public static class Rules_
    {
        #region Monoid Laws

        /// <summary>
        /// First Monoid Law: Zero is a left identity for Plus.
        /// </summary>
        public static bool Monoid_FirstLaw<X>(Proto<X> m)
            => Proto.Zero<X>().Plus(m) == m;

        /// <summary>
        /// Second Monoid Law: Zero is a right identity for Plus.
        /// </summary>
        public static bool Monoid_SecondLaw<X>(Proto<X> m)
            => m.Plus(Proto.Zero<X>()) == m;

        /// <summary>
        /// Third Monoid Law: Plus is associative.
        /// </summary>
        public static bool Monoid_ThirdLaw<X>(Proto<X> a, Proto<X> b, Proto<X> c)
            => a.Plus(b.Plus(c)) == (a.Plus(b)).Plus(c);

        #endregion

        /// <summary>
        /// ReplaceBy is associative, implied by the definition of ReplaceBy and the third monad law.
        /// </summary>
        public static bool ReplaceByIsAssociative<X, Y, Z>(Proto<X> a, Proto<Y> b, Proto<Z> c)
            // (m >> n) >> o = m >> (n >> o)
            => a.ContinueWith(b).ContinueWith(c) == a.ContinueWith(b.ContinueWith(c));

#if !MONAD_DISABLE_ZERO

        /// <summary>
        /// MonadZero: Zero is a left zero for Bind.
        /// </summary>
        public static bool SatisfiesMonadZeroRule<X, Y>(Func<X, Proto<Y>> f)
            // mzero >>= f = mzero
            => Proto.Zero<X>().Bind(f) == Proto.Zero<Y>();

        /// <summary>
        /// MonadMore: Zero is a right zero for Bind or equivalently Zero is a right zero for ReplaceBy.
        /// </summary>
        public static bool SatisfiesMonadMoreRule<X>(Proto<X> m)
            // m >>= (\x -> mzero) = mzero
            => m.Bind(_ => Proto.Zero<X>()) == Proto.Zero<X>();

        /// <summary>
        /// MonadMore: Zero is a right zero for ContinueWith, implied by the definition of ReplaceBy and the MonadMore rule.
        /// </summary>
        public static bool SatisfiesMonadMoreRuleVariant<X>(Proto<X> m)
            // v >> mzero = mzero
            => m.ContinueWith(Proto.Zero<X>()) == Proto.Zero<X>();

#endif

#if !MONAD_DISABLE_ZERO && !MONAD_DISABLE_PLUS

        /// <summary>
        /// MonadPlus: Bind is right distributive over Plus.
        /// </summary>
        public static bool SatisfiesMonadPlusRule<X>(Proto<X> a, Proto<X> b, Func<X, Proto<X>> f)
            // mplus a b >>= f = mplus (a >>= f) (b >>= f)
            => a.Plus(b).Bind(f) == a.Bind(f).Plus(b.Bind(f));

        /// <summary>
        /// MonadOr: Unit is a left zero for Plus.
        /// </summary>
        public static bool SatisfiesMonadOrRule<X>(X a, Proto<X> b)
            // morelse (return a) b ≡ return a
            => Proto.Of(a).Plus(b) == Proto.Of(a);

        /////// <summary>
        /////// Unit is a right zero for Plus.
        /////// </summary>
        ////public static bool Unused_UnitIsRightZeroForPlus<X>(Monad<X> a, X b)
        ////{
        ////    // morelse a (return b) ≡ return b
        ////    return a.Plus(Monad.Return(b)) == Monad.Return(b);
        ////}

#endif
    }
}
