#include "Prob.h"
#include "DBInt.h"
#include "NuoscParam.h"
#include "InterSplineFunc.h"

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#define ERROR cerr << "Prob:" << __LINE__ << ": "

static const char* data_table = "prob";
static const char* desc_table = "params";

Prob::Prob()
{
}
Prob::~Prob()
{
}

bool Prob::LoadDataFile(int id, NuoscParam& params, const char* filename)
{
    ifstream fstr(filename);

    if (!fstr) {
        ERROR << "Can't open \"" << filename << "\"\n";
        return false;
    }
    fId = id;
    fParams = params;
    double energy;
    while (fstr >> energy) {
        double pnue, pnumu, pnutau;
        fstr >> pnue;
        fstr >> pnumu;
        fstr >> pnutau;

        E.push_back(energy);
        Pnue.push_back(pnue);
        Pnumu.push_back(pnumu);
        Pnutau.push_back(pnutau);
    }
    return true;
}

bool Prob::LoadDataDB(int id, const char* db_host, 
                    const char* db_user, const char* db_pass)
{
    DBInt dbi;
    if (! dbi.Init("nuosc",db_host,db_user,db_pass)) {
        ERROR << "Can't connect to db \"nuosc\"\n";
        return false;
    }

    // Get data
    vector<vector<string> > data;
    if (! dbi.GetData(id,data_table,"energy,pnue,pnumu,pnutau","energy",data))
        return false;
    int siz = data.size();
    for (int i=0; i<siz; ++i) {
        E.push_back(atof(data[i][0].c_str()));
        Pnue.push_back(atof(data[i][1].c_str()));
        Pnumu.push_back(atof(data[i][2].c_str()));
        Pnutau.push_back(atof(data[i][3].c_str()));
    }

    // Get description
    vector<string> vs;
    if (! dbi.GetNthData(id,0,desc_table,
                         "type,matter,baseline,ss2t12,ss2t23,ss2t13,cpphase,"
                         "dm2sol,dm2atm","",vs)) return false;
    fParams.type     = atoi(vs[0].c_str());
    fParams.matter   = atoi(vs[1].c_str());
    fParams.baseline = atof(vs[2].c_str());
    fParams.ss2t12   = atof(vs[3].c_str());
    fParams.ss2t23   = atof(vs[4].c_str());
    fParams.ss2t13   = atof(vs[5].c_str());
    fParams.cpphase  = atof(vs[6].c_str());
    fParams.dm2sol   = atof(vs[7].c_str());
    fParams.dm2atm   = atof(vs[8].c_str());
        
    fId = id;
    return true;
}

void Prob::GetData(int& id, NuoscParam& params,
             vector<double>& evec, 
             vector<double>& pnue,
             vector<double>& pnumu,
             vector<double>& pnutau)
{
    id = fId;
    params = fParams;
    evec = E;
    pnue = Pnue;
    pnumu = Pnumu;
    pnutau = Pnutau;
}


void Prob::SetData(int id, NuoscParam& params,
             vector<double>& evec, 
             vector<double>& pnue,
             vector<double>& pnumu,
             vector<double>& pnutau)
{
    fId = id;
    fParams = params;
    E = evec;
    Pnue = pnue;
    Pnumu = pnumu;
    Pnutau = pnutau;
}


bool Prob::StoreDataFile(const char* filename)
{
    if (!filename) filename = "/dev/stdout";
    string minus("-");
    if (minus == filename) filename = "/dev/stdout";
    ofstream fstr(filename);
    if (!fstr) {
        ERROR << "Can't open \"" << filename << "\" for writing\n";
        return false;
    }

    int siz = E.size();
    for (int i = 0; i < siz; ++i) {
        fstr << E[i] << " "
             << Pnue[i] << " "
             << Pnumu[i] << " "
             << Pnutau[i] << endl;
    }
    return true;
    
}


bool Prob::StoreDataDB(const char* db_host, const char* db_user, 
                     const char* db_pass)
{
    DBInt dbi;
    if (! dbi.Init("nuosc",db_host,db_user,db_pass)) {
        ERROR << "Can't connect to " << db_user << "@" << db_host
              << ":nuosc\n";
        return false;
    }

    // The data
    stringstream ss;
    vector<string> data;
    int siz = E.size();
    for (int i=0; i<siz; ++i) {
        ss.str("");
        ss << E[i] << "," 
           << Pnue[i] << ","
           << Pnumu[i] << ","
           << Pnutau[i];
        data.push_back(ss.str());
    }
    if (! dbi.SetData(fId,data_table,"energy,pnue,pnumu,pnutau",data)) {
        ERROR << "Failed to SetData for id=" << fId
              << " data_table = " << data_table << endl;
        return false;
    }


    // The description
    data.erase(data.begin(), data.end());
    ss.str("");
    ss << fParams.type << ","
       << fParams.matter << ","
       << fParams.baseline << ","
       << fParams.ss2t12 << ","
       << fParams.ss2t23 << ","
       << fParams.ss2t13 << ","
       << fParams.cpphase << ","
       << fParams.dm2sol << ","
       << fParams.dm2atm;
    data.push_back(ss.str());

    if (! dbi.SetData(fId,desc_table,
                      "type,matter,baseline,ss2t12,ss2t23,ss2t13,cpphase,"
                      "dm2sol,dm2atm",data))  {
        cerr << "Failed to SetData on id = " << fId
             << " desc_table = " << desc_table << endl;
        return false;
    }
    return true;
}


InterFunc* Prob::CreatePnueFunc()
{
    InterSplineFunc* isf =
        new InterSplineFunc(E,Pnue,InterSplineFunc::zero_runout);
    isf->SetMin(0.0);
    isf->SetMax(1.0);
    return isf;
}
InterFunc* Prob::CreatePnumuFunc()
{
    InterSplineFunc* isf =
        new InterSplineFunc(E,Pnumu,InterSplineFunc::zero_runout);
    isf->SetMin(0.0);
    isf->SetMax(1.0);
    return isf;
}
InterFunc* Prob::CreatePnutauFunc()
{
    InterSplineFunc* isf =
        new InterSplineFunc(E,Pnutau,InterSplineFunc::zero_runout);
    isf->SetMin(0.0);
    isf->SetMax(1.0);
    return isf;
}

bool Prob::GetAvailable(const char* db_host, 
                        const char* db_user, const char* db_pass,
                        vector<int>& id, vector<NuoscParam>& params)
{
    DBInt dbi;
    if (! dbi.Init("nuosc",db_host,db_user,db_pass)) {
        ERROR << "Can't connect to db \"nuosc\"\n";
        return false;
    }

    // Get data
    vector<vector<string> > data;
    if (! dbi.GetData(-1,desc_table,
                      "type,matter,baseline,ss2t12,ss2t23,ss2t13,cpphase,"
                      "dm2sol,dm2atm,id",
                      "baseline,ss2t12,ss2t23,ss2t13,dm2sol,dm2atm",data))
        return false;

    int siz = data.size();
    for (int i=0; i<siz; ++i) {
        NuoscParam np;

        np.type     = atoi(data[i][0].c_str());
        np.matter   = atoi(data[i][1].c_str());
        np.baseline = atof(data[i][2].c_str());
        np.ss2t12   = atof(data[i][3].c_str());
        np.ss2t23   = atof(data[i][4].c_str());
        np.ss2t13   = atof(data[i][5].c_str());
        np.cpphase  = atof(data[i][6].c_str());
        np.dm2sol   = atof(data[i][7].c_str());
        np.dm2atm   = atof(data[i][8].c_str());
        params.push_back(np);

        id.push_back(atoi(data[i][9].c_str()));
    }

    return true;
}


int Prob::GetId(NuoscParam& np, const char* db_host,
                const char* db_user, const char* db_pass)
{
    DBInt dbi;
    if (! dbi.Init("nuosc",db_host,db_user,db_pass)) {
        ERROR << "Can't connect to db \"nuosc\"\n";
        return false;
    }

    stringstream ss;
    ss << "type = " << np.type
       << " and "
       << " matter = " << np.matter
       << " and "
       << bracket_double("baseline",np.baseline)
       << " and "
       << bracket_double("ss2t12",np.ss2t12)
       << " and "
       << bracket_double("ss2t23",np.ss2t23)
       << " and "
       << bracket_double("ss2t13",np.ss2t13)
       << " and "
       << bracket_double("cpphase",np.cpphase)
       << " and "
       << bracket_double("dm2sol",np.dm2sol)
       << " and "
       << bracket_double("dm2atm",np.dm2atm);

    return dbi.GetId(desc_table,ss.str());
}
