
#include "StdOpts.h"
#include "options.h"

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <vector>

StdOpts::StdOpts(const char* um)
    : user_msg(um)
      ,options(0)
{
    
    this->zero();
}

StdOpts::StdOpts(int& argc, const char**& argv, const char* um)
    : options(0)
{
    if (um) user_msg = um;
    this->zero();
    this->parse_args(argc,argv);
}

StdOpts::~StdOpts() {}

int StdOpts::usage(const char* msg)
{
//    if (user_msg) cerr << user_msg << endl;
    if (msg) cerr << msg << endl;
    if (options) options->usage(cerr,"");
    exit (1);
}

void StdOpts::zero()
{
    nuosc_params.type = 2;
    nuosc_params.matter = 1;
    nuosc_params.ss2t12 = 0.8;
    nuosc_params.ss2t23 = 1.0;
    nuosc_params.ss2t13 = 0.1;
    nuosc_params.cpphase = 0.0;
    nuosc_params.dm2sol = 5.0e-5;
    nuosc_params.dm2atm = 3.0e-3;
    nuosc_params.baseline = 735.340e5;
    use_log_energy = false;
    min_energy = 0;
    max_energy = 10e9;
    energy_step = 10e6;
    input_filename = 0;
    output_filename = 0;
    antineutrino = false;
    verbose = 0;
}

void StdOpts::parse_args(int& argc, const char**& argv)
{
    const char * optv[] = {
        "t+theta <theta_12 theta_23 theta_13> degrees",
        "S+sin  <sin^2(2theta_12) sin^2(2theta_23) sin^2(2theta_13)>",
        "s:sol <dm2_sol|dm2_21> default = 5.0e-5 eV^2",
        "a:atm <dm2_atm|dm2_32> default = 3.0e-3 eV^2",
        "d:delta <CP phase>, default = 0 degrees",
        "v+neturino-vector <nue numu nutau>, default=0 1 0",
        "1|nue",                // equiv to -v 1 0 0
        "2|numu",               // equiv to -v 0 1 0
        "3|nutau",              // equiv to -v 0 0 1
        "4|antinue",                // equiv to -v -1  0  0
        "5|antinumu",               // equiv to -v  0 -1  0
        "6|antinutau",              // equiv to -v  0  0 -1
        "b:baseline <baseline> default = 735 km",
        "m|use-matter-effects", // default
        "n|no-matter-effects",
        "I+lin <linear energy (eV): min max [step]>",
        "O+log <log10 energy (exp): min max [step], def=\"8 11 0.001\">",
        "i:file <input file>",
        "o:file <output file>",
        "v?verbose <verbosity level>",
        "h|help",
        0
    };
    options = new Options(*argv, optv);
    OptArgvIter optitr(argc-1,argv+1);
    const char* optarg;

    vector<const char*> newargv;
    newargv.push_back(argv[0]);

    char optchar;
    options->ctrls(Options::PARSE_POS);
    while ((optchar = (*options)(optitr,optarg))) {
        switch (optchar) {
        case 't':
            if (optarg) {
                double theta_12 = atof(optarg)*M_PI/180.0;
                double ss = sin(2*theta_12);
                nuosc_params.ss2t12 = ss*ss;
            }
            else usage("Bad theta_12 value");
            optarg = optitr();
            if (optarg) {
                double theta_23 = atof(optarg)*M_PI/180.0;
                double ss = sin(2*theta_23);
                nuosc_params.ss2t23 = ss*ss;
            }
            else usage("Bad theta_23 value");
            optarg = optitr();
            if (optarg) {
                double theta_13 = atof(optarg)*M_PI/180.0;
                double ss = sin(2*theta_13);
                nuosc_params.ss2t13 = ss*ss;
            }
            else usage("Bad theta_13 value");
            break;
        case 'S':
            if (optarg) nuosc_params.ss2t12 = atof(optarg);
            else usage("Bad theta_12 value");
            optarg = optitr();
            if (optarg) nuosc_params.ss2t23 = atof(optarg);
            else usage("Bad theta_23 value");
            optarg = optitr();
            if (optarg) nuosc_params.ss2t13 = atof(optarg);
            else usage("Bad theta_13 value");
            break;
        case 's':
            if (optarg) nuosc_params.dm2sol = atof(optarg);
            else usage("Bad dm2_sol value");
            break;
        case 'a':
            if (optarg) nuosc_params.dm2atm = atof(optarg);
            else usage("Bad dm2_atm value");
            break;
        case 'd':
            if (optarg) nuosc_params.cpphase = atof(optarg)*M_PI/180.0;
            else usage("Bad CP phase value");
            break;
        case '1':               // pure nue
            nuosc_params.type = 1;
            break;
        case '2':               // pure numu
            nuosc_params.type = 2;
            break;
        case '3':               // pure nutau
            nuosc_params.type = 3;
            break;
        case '4':               // pure antinue
            nuosc_params.type = -1;
            break;
        case '5':               // pure antinumu
            nuosc_params.type = -2;
            break;
        case '6':               // pure antinutau
            nuosc_params.type = -3;
            break;
        case 'b':
            if (optarg) nuosc_params.baseline = atof(optarg)*1.0e5;
            else usage("Bad baseline value");
            break;
        case 'm':
            nuosc_params.matter = 1;
            break;
        case 'n':
            nuosc_params.matter = 0;
            break;
        case 'I': case 'O':
            if (optchar == 'I') use_log_energy = false;
            else use_log_energy = true;
            if (optarg) min_energy = atof(optarg);
            else usage("Bad min_energy value");
            optarg = optitr();
            if (optarg) max_energy = atof(optarg);
            else usage("Bad max_energy value");
            optarg = optitr();
            if (optarg) energy_step = atof(optarg);
            else energy_step = (use_log_energy ? 0.001 : 100e6);
            break;
        case 'i':
            input_filename = optarg;
            break;
        case 'o':
            output_filename = optarg;
            break;
        case 'v':
            verbose = 1;
            if (optarg) verbose = atoi(optarg);
            break;
        case Options::POSITIONAL:
            newargv.push_back(optarg);
            break;
        case 'h':
            usage("");
            break;
        default:
            cerr << "optitr.index=" << optitr.index() << endl;
            newargv.push_back(argv[optitr.index()]);
//            usage("Unknown option");
            break;
        }
    }

    argc =  newargv.size();
    for (int i = 0; i < argc; ++i) {
        argv[i] = newargv[i];
    }

} // parse_args()

void StdOpts::Dump()
{
    cout
        << "neutrino type " << nuosc_params.type << endl
        << "theta_12 " << nuosc_params.theta_12() << endl
        << "theta_23 " << nuosc_params.theta_23() << endl
        << "theta_13 " << nuosc_params.theta_13() << endl
        << "sin^2(2theta12) " << nuosc_params.ss2t12 << endl
        << "sin^2(2theta23) " << nuosc_params.ss2t23 << endl
        << "sin^2(2theta13) " << nuosc_params.ss2t13 << endl
        << "cp_phase " << nuosc_params.cpphase << endl
        << "dm2_sol "  << nuosc_params.dm2sol  << endl
        << "dm2_atm "  << nuosc_params.dm2atm  << endl
        << "baseline " << nuosc_params.baseline << endl
        << "input file " << input_filename << endl
        << "output file " << output_filename << endl
        << (antineutrino? "anti" : "" ) << "neutrino\n"
        << "with" << (nuosc_params.matter ? " " : " out ") << "matter effects"
        << endl;
    
}
