using System;
using Chernobyl.Values;

namespace Chernobyl.Conversion
{
    /// <summary>
    /// Converts a bool to a specified value depending on whether it is true or
    /// false. This type can be implicitly converted to a <see cref="Func{T, TResult}"/>
    /// which is the method to use for bool conversion.
    /// </summary>
    /// <typeparam name="TTo">The type of the conversion.</typeparam>
    public class ConvertBool<TTo>
    {
        /// <summary>
        /// Initializes a new instance of the 
        /// <see cref="ConvertBool{TConversion}"/> class.
        /// </summary>
        /// <param name="trueValue">The value to convert to when a bool is true.</param>
        /// <param name="falseValue">The value to convert to when a bool is false.</param>
        public ConvertBool(TTo trueValue, TTo falseValue)
        {
            _trueValue = trueValue;
            _falseValue = falseValue;
        }

        /// <summary>
        /// Implicitly converets from <see cref="ConvertBool{TConversion}"/> to 
        /// <see cref="Func{T, TResult}"/>. Use this implicit conversion to
        /// get the method that handles the conversion.
        /// </summary>
        /// <param name="converter">The instance that is to be converted to the
        /// <see cref="Func{T}"/>.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Func<bool, TTo>(ConvertBool<TTo> converter)
        {
            return converter.Convert;
        }

        /// <summary>
        /// The method that converts the bool to the value requested.
        /// </summary>
        /// <param name="value">The bool to convert.</param>
        /// <returns>The value of the bool</returns>
        TTo Convert(bool value)
        {
            return value ? _trueValue : _falseValue;
        }

        /// <summary>
        /// The converted value of the bool when it is true.
        /// </summary>
        readonly TTo _trueValue;

        /// <summary>
        /// The converted value of the bool when it is false.
        /// </summary>
        readonly TTo _falseValue;
    }

    /// <summary>
    /// Utility and extensions methods for
    /// <see cref="ConvertBool{TConversion}"/> and its dependencies.
    /// </summary>
    public static class ConvertBoolExtensions
    {
        /// <summary>
        /// Returns an <see cref="IValue{T}"/> that holds and maintains a
        /// converted version of a <see cref="IValue{T}"/> that contains a bool.
        /// </summary>
        /// <typeparam name="TTo">The type to convert the bool to.</typeparam>
        /// <param name="value">The instance whose contained bool is to be
        /// converted.</param>
        /// <param name="trueValue">The value to convert to when a bool is true.</param>
        /// <param name="falseValue">The value to convert to when a bool is false.</param>
        /// <returns>The <see cref="IValue{T}"/> which holds and maintains the
        /// converted value.</returns>
        public static IValue<TTo> Convert<TTo>(
            this IValue<bool> value, TTo trueValue, TTo falseValue)
        {
            return new FromSourceConversion<bool, TTo>(value, new ConvertBool<TTo>(trueValue, falseValue));
        }
    }
}