#include <string>
#include <iostream>
#include "../tclap/CmdLine.h"
#include "chainmanager.h"
#include <ctime>
#include <typeinfo>
#include <sstream>

template <typename T>
struct type_name
{
	static std::string name() {std::string str = typeid(T).name(); if (str == "i") return "integer"; else if (str == "d") return "double"; else return str;}
};

template <typename T>
struct PositiveNumberConstraint : public TCLAP::Constraint<T> 
{
	std::string shortID () const {return "positive " + std::string(type_name<T>::name());}
	std::string description () const {return "positive " + std::string(type_name<T>::name());}
	bool check (const T &value) const {return value > 0;}
};

template <typename T>
struct NumberGtEqConstraint : public TCLAP::Constraint<T> 
{
	NumberGtEqConstraint(T t) : val(t) {}
	std::string shortID () const {std::stringstream s; s << type_name<T>::name() << " >= " << val; return s.str();}
	std::string description () const {std::stringstream s; s << type_name<T>::name() << " >= " << val; return s.str();}
	bool check (const T &value) const {return value >= val;}
	T val;
};

int main(int argc, char** argv)
{
	try {  

		TCLAP::CmdLine cmd("StableTraits", ' ', "1.4");
		PositiveNumberConstraint<int> posInt;
		PositiveNumberConstraint<double> posDouble;
		NumberGtEqConstraint<int> intGtEqTwo(2);
		NumberGtEqConstraint<double> dblGtEqZero(0);
		
        TCLAP::ValueArg<std::string> tree("t", "tree", "Tree file", true, "", "string", cmd);
		TCLAP::ValueArg<std::string> data("d", "data", "Data file", true, "", "string", cmd);
		TCLAP::ValueArg<std::string> output("o", "output", "Base path for output files", false, "output", "string", cmd);
		TCLAP::ValueArg<int> gens("i", "iterations", "Number of iterations", false, 1000000, &posInt, cmd);
		TCLAP::ValueArg<int> skip("s", "skip", "Skip size (for thinning of mcmc chain)", false, 100, &posInt, cmd);
		TCLAP::ValueArg<long> seed("r", "rseed", "Random seed (set to zero to use current time)", false, 0, "long integer", cmd);
		TCLAP::ValueArg<int> numChains("c", "chains", "Number of parallele markov chains", false, 2, &intGtEqTwo, cmd);
		TCLAP::ValueArg<double> ig_shape("p", "ig_shape", "Shape of the inverse gamma prior on rate of evolution", false, 2.0, &posDouble, cmd);
		TCLAP::ValueArg<double> ig_scale("l", "ig_scale", "Scale of the inverse gamma prior on rate of evolution (set to zero to generate automatically based on Brownian reconstruction)", false, 0, &dblGtEqZero, cmd);	
		TCLAP::SwitchArg brownian("b","brownian","Fit a Brownian model (constrain index of stability to 2.0)", cmd, false);
		TCLAP::SwitchArg brownianstrict("m","brownianstrict","Fit a strict Brownian model (constrain index of stability to 2.0 and variance to the maximum likelihood estimate)", cmd, false);
		TCLAP::ValueArg<int> stages("g", "stages", "Number of stages", false, 40, &posInt, cmd);
		
		cmd.parse( argc, argv );
		
        ChainManager manager((seed.getValue() == 0) ? (long int)time(0) : seed.getValue(),
                             stages.getValue(),
                             numChains.getValue(),
                             tree.getValue(),
                             data.getValue(),
                             output.getValue(),
                             gens.getValue(),
                             skip.getValue(),
                             ig_shape.getValue(),
                             ig_scale.getValue(),
                             brownian.getValue(),
                             brownianstrict.getValue());
							
		if (!manager.go()) {
			std::cerr << "Error: " << manager.error() << std::endl;
			return -1;
		} else {
			std::cout << "Done." << std::endl;
		}
		
	} catch (TCLAP::ArgException &e) { 
		std::cerr << "Error: " << e.error() << " for command line argument " << e.argId() << std::endl;
		return -1;
	}

	return 0;	
}
