#include "Variant.h"
#include "split.h"
#include "Fasta.h"
#include "gpatInfo.hpp"
#include <getopt.h>
#include <algorithm>
#include <numeric>

using namespace std;
using namespace vcflib;

void printSummary(char** argv) {
    cerr << "usage: " << argv[0] << " [options] <vcf file>" << endl
         << endl
         << "options:" << endl 
         << "    -f, --field         Summarize this field in the INFO column" << endl
         << "    -i, --info          Store the computed statistic in this info field" << endl
         << "    -a, --average       Take the mean for field (default)" << endl
         << "    -m, --median        Use the median" << endl
         << "    -n, --min           Use the min" << endl
         << "    -x, --max           Use the max" << endl
	 << "    -h, --help          Print this message" << endl
	 << "    -v, --version       Print version" << endl
         << endl
         << "Take annotations given in the per-sample fields and add the mean, median, min, or max" << endl
         << "to the site-level INFO." << endl
         << endl;
    exit(0);
}

double median(vector<double> &v)
{
    size_t n = v.size() / 2;
    nth_element(v.begin(), v.begin()+n, v.end());
    return v[n];
}

double mean(vector<double> &v)
{
    double sum = accumulate(v.begin(), v.end(), 0.0);
    return sum / v.size();
}

enum StatType { MEAN, MEDIAN, MIN, MAX };

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

    int c;
    string sitewideField;
    string infoField;
    StatType statType = MEAN; 

    if (argc == 1)
        printSummary(argv);

    while (true) {
        static struct option long_options[] =
            {
                /* These options set a flag. */
                {"help", no_argument, 0, 'h'},
                {"field",  required_argument, 0, 'f'},
                {"info",  required_argument, 0, 'i'},
                {"average", no_argument, 0, 'a'},
                {"median", no_argument, 0, 'm'},
                {"min", no_argument, 0, 'n'},
                {"max", no_argument, 0, 'x'},
		{"max", no_argument, 0, 'v'},
                {0, 0, 0, 0}
            };
        /* getopt_long stores the option index here. */
        int option_index = 0;

        c = getopt_long (argc, argv, "hamnxfv:i:",
                         long_options, &option_index);

        /* Detect the end of the options. */
        if (c == -1)
            break;
 
        switch (c)
        {
        case 0:
            /* If this option set a flag, do nothing else now. */
            if (long_options[option_index].flag != 0)
                break;
            printf ("option %s", long_options[option_index].name);
            if (optarg)
                printf (" with arg %s", optarg);
            printf ("\n");
            break;
	    
	case 'v':
	  {
	    printBasicVersion();
	    exit(0);
	  }
        case 'f':
	  {
            sitewideField = optarg;
            break;
	  }
        case 'i':
	  {
            infoField = optarg;
            break;
	  }
        case 'a':
	  {
            statType = MEAN;
            break;
	  }
        case 'm':
	  {
            statType = MEDIAN;
            break;
	  }
        case 'n':
	  {
            statType = MIN;
            break;
	  }
        case 'x':
	  {
            statType = MAX;
            break;
	  }
        case 'h':
	  {
            printSummary(argv);
            exit(0);
	  }
        case '?':
            /* getopt_long already printed an error message. */
            printSummary(argv);
            exit(1);
            break;
 
        default:
            abort ();
        }
    }

    if (infoField.empty() || sitewideField.empty()) {
        cerr << "Error: both a sample field and an info field are required." << endl;
        return 1;
    }

    VariantCallFile variantFile;
    string inputFilename;
    if (optind == argc - 1) {
        inputFilename = argv[optind];
        variantFile.open(inputFilename);
    } else {
        variantFile.open(std::cin);
    }

    if (!variantFile.is_open()) {
        return 1;
    }

    string statTypeStr;

    switch (statType) {
    case MEAN:
        statTypeStr = "mean";
        break;
    case MEDIAN:
        statTypeStr = "median";
        break;
    case MIN:
        statTypeStr = "min";
        break;
    case MAX:
        statTypeStr = "max";
        break;
    default:
        cerr << "Error: failure to convert stat type to string" << endl;
        return 1;
        break;
    }

    variantFile.addHeaderLine("##INFO=<ID="+infoField+",Number=1,Type=Float,Description=\"Summary statistic generated by"+statTypeStr+" of site-wide values of "+sitewideField+" \">");

    cout << variantFile.header << endl;

    Variant var(variantFile);
    while (variantFile.getNextVariant(var)) {
        vector<double> vals;
        map<string, vector<string> >::iterator i = var.info.find(sitewideField);
        if (i != var.info.end()) {
            for (vector<string>::iterator s = i->second.begin(); s != i->second.end(); ++s) {
                double d;
                convert(*s, d);
                vals.push_back(d);
            }
        }

        double result;
        switch (statType) {
        case MEAN:
            result = mean(vals);
            break;
        case MEDIAN:
            result = median(vals);
            break;
        case MIN:
            result = *min_element(vals.begin(), vals.end());
            break;
        case MAX:
            result = *max_element(vals.begin(), vals.end());
            break;
        default:
            cerr << "Error: unrecognized StatType" << endl;
            return 1;
            break;
        }

        var.info[infoField].clear();
        var.info[infoField].push_back(convert(result));

        cout << var << endl;

    }

    return 0;

}

