/*
 * Copyright (c) 1999 Johnny C. Lam.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 */

#include "commandline.h"

CommandLine::Arg::~Arg()
{}

template <typename T, typename U, class CA>
class Argument : public CommandLine::Arg {
public:
    ~Argument();
    Argument(T*, U, CA*);

    CA* cmdarg();
    void assign();
    void require();
    bool given() const;
    bool value_given() const;
    void write(std::ostream&) const;

private:    
    T* _arg;
    auto_ptr<CA> _cmdarg;
    std::string _keyword_name;
};

template <typename T, typename U, class CA>
Argument<T,U,CA>::~Argument()
{}

template <typename T, typename U, class CA>
Argument<T,U,CA>::Argument(T* arg_, U default_, CA* cmdarg_)
    : _arg(arg_), _cmdarg(cmdarg_)
{
    if (_arg)
	*_arg = default_;
    _keyword_name = _cmdarg->keyword_name();
}

template <typename T, typename U, class CA>
CA*
Argument<T,U,CA>::cmdarg()
{
    return _cmdarg.get();
}

template <typename T, typename U, class CA>
void
Argument<T,U,CA>::assign()
{
    if (given() && _arg)
	*_arg = *_cmdarg;
}

template <typename T, typename U, class CA>
void
Argument<T,U,CA>::require()
{
    auto_ptr<CA> new_cmdarg(new CA(_cmdarg->value_name(),
				   _cmdarg->description()));
    _cmdarg = new_cmdarg;
}

template <typename T, typename U, class CA>
bool
Argument<T,U,CA>::given() const
{
    return _cmdarg->flags() & CmdArg::GIVEN;
}

template <typename T, typename U, class CA>
bool
Argument<T,U,CA>::value_given() const
{
    return _cmdarg->flags() & CmdArg::VALGIVEN;
}

template <typename T, typename U, class CA>
void
Argument<T,U,CA>::write(std::ostream& ostr) const
{
    ostr << "\t"
	 << _keyword_name << ": "
	 << *_arg << endl;
}

template<>
void
Argument<bool, bool, CmdArgBool>::require()
{}

template <typename T, typename U, class CA>
auto_ptr<CommandLine::Arg>
MakeArgument(T* arg_, U default_, CA* cmdarg_)
{
    return auto_ptr<CommandLine::Arg>(new Argument<T,U,CA>(arg_, default_,
							   cmdarg_));
}

#include <limits>

INITIALIZE_SINGLETON_BASE(CommandLine);

CommandLine::CommandLine()
{
    Initialize();
}

void
CommandLine::Initialize()
{
    /*
     * Used letters: abcdgijlmnoprsvACDGLMNRSX
     */
    _options[VERBOSE]
	= MakeArgument(&_verbose, 0,
		       new CmdArgInt('v', "verbose", "[level]",
				     "Give verbose output, optionally "
				     "specifying level of verbosity."));
    
    _options[SEGMENT]
	= MakeArgument(&_segment, SegmentProbabilityFactory::LONGEST,
		       new CmdArgStr('s', "segment", "method",
				     "Method of computing Pr(segment), "
				     "the probability of a haplotype segment. "
				     "Choices: longest, independent. "
				     "Default: longest"));
    _options[LEFT_MASK]
	= MakeArgument(&_left_mask, 0,
		       new CmdArgInt('a', "left-mask", "number",
				     "Gap number masking left of haplotypes. "
				     "Default: (none)"));
    _options[RIGHT_MASK]
	= MakeArgument(&_right_mask, std::numeric_limits<unsigned int>::max(),
		       new CmdArgInt('b', "right-mask", "number",
				     "Gap number masking right of haplotypes. "
				     "Default: (none)"));
    _options[SSM]
	= MakeArgument(&_ssm, 10.0 / 11.0,
		       new CmdArgFloat('m', "ssm", "number",
				       "Probability of a 1-step mutation "
				       "given a mutation has occurred. "
				       "Default: 10/11"));
    _options[IBD]
	= MakeArgument(&_ibd, 0.5,
		       new CmdArgFloat('i', "ibd", "number",
				       "Relative probability of two haplotype "
				       "fragments being identical-by-descent "
				       "versus identical-by-chance. "
				       "Default: 0.5"));
    _options[P_MUTATION]
	= MakeArgument(&_p_mutation, 0.01,
		       new CmdArgFloat('g', "p-mut", "number",
				       "Probability of a mutation over the "
				       "entire region spanned by the markers. "
				       "Default: 0.01"));
    _options[P_RECOMBINATION]
	= MakeArgument(&_p_recombination, 0.01,
		       new CmdArgFloat('G', "p-recomb", "number",
				       "Probability of a recombination over "
				       "the entire region spanned by the "
				       "markers. "
				       "Default: 0.01"));
    _options[DIST_FNAME]
	= MakeArgument(&_dist_fname, string(),
		       new CmdArgStr('l', "dist-fname", "filename",
				     "File containing distribution parameters "
				     "for mutations per locus. "
				     "Default: (none)"));
    _options[STR_FNAME]
	= MakeArgument(&_str_fname, string(),
		       new CmdArgStr('j', "str-fname", "filename",
				     "File containing sizes of the repeated "
				     "units of the STR marker at each locus. "
				     "Default: (none)"));
    _options[RECOMB_FNAME]
	= MakeArgument(&_recomb_fname, string(),
		       new CmdArgStr('r', "recomb-fname", "filename",
				     "File containing the relative "
				     "probabilities of recombinations in each "
				     "gap. "
				     "Default: (none)"));
    _options[NO_RECOMB_SUM]
	= MakeArgument(&_no_recomb_sum, false,
		       new CmdArgBool('S', "no-recomb-sum",
				      "Set this option to use a more "
				      "conservative estimate of "
				      "P(S-->T|recomb) by not summing "
				      "over gaps between the disease "
				      "and the probable recombination point. "
				      "Default: (unset)"));
    _options[MAX_LATENT_DEPTH]
	= MakeArgument(&_max_latent_depth, 4,
		       new CmdArgInt('L', "max-latent-depth", "number",
				     "Maximum number of latents allowed "
				     "between observed haplotypes. "
				     "Default: 4"));
    _options[PATH_STRATEGY]
	= MakeArgument(&_path_strategy, PathStrategyFactory::NO_LATENT_RECOMB,
		       new CmdArgStr('p', "path-strategy", "method",
				     "Strategy for generating paths with "
				     "latents. "
				     "Choices: no_latents, no_latent_recomb, "
				     "latent_recomb. "
				     "Default: no_latent_recomb."));
    _options[NUM_ODH]
	= MakeArgument(&_num_odh, 1,
		       new CmdArgInt('o', "num-odh", "number",
				     "Number of ODHs on which to condition. "
				     "Default: 1"));
    _options[CRITERION]
	= MakeArgument(&_criterion, PathCriterionFactory::AVERAGE,
		       new CmdArgStr('c', "criterion", "method",
				     "Method of computing a priority for a "
				     "pair of paths during tree construction. "
				     "Choices: average, min, max. "
				     "Default: average"));
    _options[COMPONENTS_FNAME]
	= MakeArgument(&_components_fname, string(),
		       new CmdArgStr('C', "components-fname", "filename",
				     "Name of output file for all of the "
				     "possible rooted trees per component. "
				     "Default: (none)"));
    _options[ALLELE_FREQ]
	= MakeArgument(&_allele_freq, false,
		       new CmdArgBool('A', "allele-freq",
				      "Set this option if the file of normal "
				      "haplotypes contains allele frequencies "
				      "instead of haplotypes. "
				      "Default: (unset)"));
    _options[NUM_NORMALS]
	= MakeArgument(&_num_normals, 0,
		       new CmdArgInt('n', "num-normals", "number",
				     "Number of normal haplotypes. If the "
				     "file of normal haplotypes contains "
				     "allele frequencies, then this needs "
				     "to be specified. Otherwise, this sets "
				     "the number of normal haplotypes to read "
				     "from the file of normal haplotypes. "
				     "If set to 0, then all of the normal "
				     "haplotypes are used. "
				     "Default: 0"));
    _options[ROOT_PRIOR_FNAME]
	= MakeArgument(&_root_prior_fname, string(),
		       new CmdArgStr('R', "root-prior", "filename",
				     "Name of file containing prior for "
				     "P(S is root) for all observed diseased "
				     "haplotypes S. "
				     "Default: (none)"));
    _options[NO_CLADES]
	= MakeArgument(&_no_clades, false,
		       new CmdArgBool('M', "no-clades",
				      "Set this option to put each disease "
				      "haplotype into its own clade. "
				      "Default: (unset)"));
    _options[EXCLUDE_NORMALS]
	= MakeArgument(&_exclude_normals, false,
		       new CmdArgBool('X', "exclude-normals",
				      "Set this option to exclude disease "
				      "haplotypes also present in the normals "
				      "from the analysis. "
				      "Default: (unset)"));
    _options[DISEASE]
	= MakeArgument(&_disease, 0,
		       new CmdArgInt('d', "disease", "number",
				     "An integer from 1 to L-1 (inclusive), "
				     "where L is the length of a haplotype, "
				     "representing the disease location."));
    _options[DISEASED_FNAME]
	= MakeArgument(&_diseased_fname, string(),
		       new CmdArgStr('D', "diseased-fname", "filename",
				     "File containing diseased haplotypes. "
				     "Haplotypes may be observed or latent."));
    _options[NORMAL_FNAME]
	= MakeArgument(&_normal_fname, string(),
		       new CmdArgStr('N', "normal-fname", "filename",
				     "File containing normal haplotypes. "
				     "This is used in computing "
				     "Pr(segment)."));
}

unsigned int
CommandLine::Verbose() const
{
    return _verbose;
}

unsigned int
CommandLine::NumOdh() const
{
    return _num_odh;
}

const string&
CommandLine::DistFname() const
{
    return _dist_fname;
}

const string&
CommandLine::StrFname() const
{
    return _str_fname;
}

const string&
CommandLine::RecombFname() const
{
    return _recomb_fname;
}

bool
CommandLine::NoRecombSum() const
{
    return _no_recomb_sum;
}

unsigned int
CommandLine::LeftMask() const
{
    return _left_mask;
}

unsigned int
CommandLine::RightMask() const
{
    return _right_mask;
}

unsigned int
CommandLine::MaxLatentDepth() const
{
    return _max_latent_depth;
}

CommandLine::criterion_type
CommandLine::Criterion() const
{
    return _criterion;
}

double
CommandLine::Ibd() const
{
    return _ibd;
}

double
CommandLine::PMutation() const
{
    return _p_mutation;
}

double
CommandLine::PRecombination() const
{
    return _p_recombination;
}

double
CommandLine::Ssm() const
{
    return _ssm;
}

CommandLine::segment_type
CommandLine::Segment() const
{
    return _segment;
}

CommandLine::path_strategy_type
CommandLine::PathStrategy() const
{
    return _path_strategy;
}

bool
CommandLine::AlleleFreq() const
{
    return _allele_freq;
}

unsigned int
CommandLine::NumNormals() const
{
    return _num_normals;
}

const string&
CommandLine::RootPriorFname() const
{
    return _root_prior_fname;
}

bool
CommandLine::NoClades() const
{
    return _no_clades;
}

bool
CommandLine::ExcludeNormals() const
{
    return _exclude_normals;
}

const string&
CommandLine::ComponentsFname() const
{
    return _components_fname;
}

unsigned int
CommandLine::Disease() const
{
    return _disease;
}

const string&
CommandLine::DiseasedFname() const
{
    return _diseased_fname;
}

const string&
CommandLine::NormalFname() const
{
    return _normal_fname;
}
