using System;
using Moserware.Numerics;
namespace Moserware.Skills
{
    /// 
    /// Container for a player's rating.
    /// 
    public class Rating
    {
        private const int ConservativeStandardDeviationMultiplier = 3;
        private readonly double _ConservativeStandardDeviationMultiplier;
        private readonly double _Mean;
        private readonly double _StandardDeviation;
        /// 
        /// Constructs a rating.
        /// 
        /// The statistical mean value of the rating (also known as μ).
        /// The standard deviation of the rating (also known as σ).        
        public Rating(double mean, double standardDeviation)
            : this(mean, standardDeviation, ConservativeStandardDeviationMultiplier)
        {
        }
        /// 
        /// Constructs a rating.
        /// 
        /// The statistical mean value of the rating (also known as μ).
        /// The standard deviation (the spread) of the rating (also known as σ).
        /// The number of s to subtract from the  to achieve a conservative rating.
        public Rating(double mean, double standardDeviation, double conservativeStandardDeviationMultiplier)
        {
            _Mean = mean;
            _StandardDeviation = standardDeviation;
            _ConservativeStandardDeviationMultiplier = conservativeStandardDeviationMultiplier;
        }
        /// 
        /// The statistical mean value of the rating (also known as μ).
        /// 
        public double Mean
        {
            get { return _Mean; }
        }
        /// 
        /// The standard deviation (the spread) of the rating. This is also known as σ.
        /// 
        public double StandardDeviation
        {
            get { return _StandardDeviation; }
        }
        /// 
        /// A conservative estimate of skill based on the mean and standard deviation.
        /// 
        public double ConservativeRating
        {
            get { return _Mean - ConservativeStandardDeviationMultiplier*_StandardDeviation; }
        }
        public static Rating GetPartialUpdate(Rating prior, Rating fullPosterior, double updatePercentage)
        {
            var priorGaussian = new GaussianDistribution(prior.Mean, prior.StandardDeviation);
            var posteriorGaussian = new GaussianDistribution(fullPosterior.Mean, fullPosterior.StandardDeviation);
            // From a clarification email from Ralf Herbrich:
            // "the idea is to compute a linear interpolation between the prior and posterior skills of each player 
            //  ... in the canonical space of parameters"
            double precisionDifference = posteriorGaussian.Precision - priorGaussian.Precision;
            double partialPrecisionDifference = updatePercentage*precisionDifference;
            double precisionMeanDifference = posteriorGaussian.PrecisionMean - priorGaussian.PrecisionMean;
            double partialPrecisionMeanDifference = updatePercentage*precisionMeanDifference;
            GaussianDistribution partialPosteriorGaussion = GaussianDistribution.FromPrecisionMean(
                priorGaussian.PrecisionMean + partialPrecisionMeanDifference,
                priorGaussian.Precision + partialPrecisionDifference);
            return new Rating(partialPosteriorGaussion.Mean, partialPosteriorGaussion.StandardDeviation,
                              prior._ConservativeStandardDeviationMultiplier);
        }
        public override string ToString()
        {
            // As a debug helper, display a localized rating:
            return String.Format(
                "μ={0:0.0000}, σ={1:0.0000}",
                Mean, StandardDeviation);
        }
    }
}