#ifndef INCLUDED_DISTRIBUTION_
#define INCLUDED_DISTRIBUTION_

#include <iosfwd>

#include "../typedefs/typedefs.h"

class Distribution
{
    friend std::istream &operator>>(std::istream &in, Distribution &dist);
    friend std::ostream &operator<<(std::ostream &out, 
                                             Distribution const &dist);

    double      d_value = 0;                // used as defaults
    DistType    d_type = N_DISTRIBUTIONS;   // with, e.g., vectors

    double (Distribution::*d_vary)(double orgValue) const = 
                                                &Distribution::varyOK;

    static StringVect s_name;

    public:
        Distribution() = default;
  
        double     value() const;
        DistType   type() const;

        double vary(double orgValue) const; // obtain varied value or
                                            // throw an exception

        void setVary(VaryType type);
                                                // returns ABSENT if
                                                // an undefined name is used
        static DistType find(std::string const &distName);  

                                                // must succeed or err
        static DistType xlat(LineInfo const &lineInfo,      
                         std::string const &distName);

        static std::string const &name(DistType dist);

    private:
        std::istream &extract(std::istream &in);
        std::ostream &insert(std::ostream &out) const;

        void prepareVary();

        double varyOK(double orgValue) const;   // these members are 
        double varyPos(double orgValue) const;  // called via d_vary
        double varyProb(double orgValue) const;
};
       
// static 
inline std::string const &Distribution::name(DistType dist) 
{
    return s_name[dist];
}

inline double Distribution::vary(double orgValue) const
{
    return (this->*d_vary)(orgValue);
}

inline std::istream &operator>>(std::istream &in, Distribution &dist)
{
    return dist.extract(in);
}

inline std::ostream &operator<<(std::ostream &out, Distribution const &dist)
{
    return dist.insert(out);
}

inline double Distribution::value() const
{
    return d_value;
}

inline DistType Distribution::type() const
{
    return d_type;
}

#endif
