#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <vector>

#include "cbeam_class.h"

using namespace std;

    char *help = 
    
    "usage: cba -i inputfile [-o outputfile -p plotfile]\n"
    "   \n"
    "input file format: \n"
    "                   \n"
    "#SPANS lengths of spans  \n"
    "#INERTIA moments of inertia (optional)      \n"
    "#ELASTICITY modulus of elasticity (optional)\n" 
    "#CONSTRAINTS constraints def/rot (optional) \n"  
    "#LOAD load matrix: span type perm live start len\n"
    "#FACTORS load factors perm/live (optional)  \n"
    "#EXAMPLE:                      \n"
    "   SPANS 4.2 5.8               \n"  
    "   INERTIA 3.69e-5 3.69e-5     \n" 
    "   ELASTICITY 2.1e8            \n"
    "   CONSTRAINTS -1 0 -1 0 -1 0  \n"
    "   LOAD 1 1 10.0 5.0 0 0       \n"
    "   LOAD 2 1 10.0 5.0 0 0       \n"
    "   FACTORS 1.35 1.5            \n";          
    

vector<double> dbl_s(string);
string _s(double);
string _s(double, int);
string _s(int);
vector<string> lmSort(vector< vector<double> >);
vector<string> resOut(vector< vector<double> >, vector< vector <double> >);

int main(int argc, char *argv[])
{
    if (argc<2)
    {
        cout << help;
        exit(1);
    }
    
    string infilename;
    string outfilename;
    string plotfilename;
    
    for (int i=1; i<argc; i++)
    {
        string s(argv[i]);
        if (s=="-i" || s=="-I" && argc>i+1) infilename=argv[i+1];
        if (s=="-o" || s=="-O" && argc>i+1) outfilename=argv[i+1];
        if (s=="-p" || s=="-P" && argc>i+1) plotfilename=argv[i+1];
        
    }
    
    if (infilename=="")
    {
        cout << help;
        exit(1);
    }
    
    //------------- reading inputfile -------------
    ifstream inFile(infilename.c_str());
    if (inFile.fail())
    {
        cerr << "unable to open input-file for reading: " << infilename << endl;
        exit(1);
    }
    
    vector<double> L;
    vector<double> E;
    vector<double> I;
    vector<double> R;
    vector<double> F;
    vector<double> ld;
    vector< vector<double> > LM;
 
    int nl=0;
   
    while (inFile.good())
    {
        string s;
        getline(inFile,s,'\n');
        if (s.find("#")==-1)        //ignore comments
        { 
            transform(s.begin(),s.end(),s.begin(),::toupper);       //ignore lowercase
            if (s.find("SPAN")!=-1) L=dbl_s(s);
            if (s.find("INER")!=-1) I=dbl_s(s);
            if (s.find("ELAS")!=-1) E=dbl_s(s);
            if (s.find("CONS")!=-1) R=dbl_s(s);
            if (s.find("FACT")!=-1) F=dbl_s(s);
            if (s.find("LOAD")!=-1) LM.push_back(dbl_s(s));
       }
    }
    
    double Es;
    if (E.size()>=1) Es=E[0];
    
    //------------- construct beam object -------------
    cBeam *bm; 
    bm = new cBeam();  
    
    
    //------------- create output string array -------------
    string line;
    vector<string> output;
    
    
    //------------- set geometry values -------------
    if (!bm->SetGeometry(L,Es,I,R)) 
    {
        cout << "geometry not right" <<endl;
        delete bm;
        exit(1);
    } 
       
    //get geometry values used by program
    vector< vector<double> > geo(4);
    geo=bm->GetGeometry();

    L.clear(); L=geo[0];
    E.clear(); E=geo[1];
    I.clear(); I=geo[2];
    R.clear(); R=geo[3];

    int nf=L.size();  
    
    //resize E to 1
    E.resize(1);
    
    //check if I=const 
    bool c=true;
    for (int i=1; i<I.size(); i++) if (I[i]!=I[i-1]) c=false;
    if (c) I.resize(1);
    
    //create geometry output
    line = "continuous beam "+_s(nf)+" spans"; output.push_back(line);
    
    string sep; for (int i=0; i<nf+3; i++) sep+="--------";
    output.push_back(sep);
    line = "span:"; for (int i=0; i<nf; i++) line+=("\t"+_s(i+1));              output.push_back(line);
    output.push_back(sep);
     
    line = "l:"; for (int i=0; i<L.size(); i++) line+=("\t"+_s(L[i]));          output.push_back(line);
    line = "E:"; for (int i=0; i<E.size(); i++) line+=("\t"+_s(E[i]));          output.push_back(line);
    line = "I:"; for (int i=0; i<I.size(); i++) line+=("\t"+_s(I[i]));          output.push_back(line);
    line = "Rd:"; for (int i=0; i<R.size()/2; i++) line+=("\t"+_s(R[i*2]));     output.push_back(line);
    line = "Rr:"; for (int i=0; i<R.size()/2; i++) line+=("\t"+_s(R[i*2+1]));   output.push_back(line);
    
    //------------- set load values -------------
    if (!bm->SetLoads(LM))
    {
        cout << "no loads" <<endl;
        delete bm;
        exit(1);
    }
   
    vector< vector<double> > LMg(bm->GetLoadsG());      //get back permanent loads
    vector< vector<double> > LMq(bm->GetLoadsQ());      //get back live loads
    
    output.push_back("");
    line = "permanent loads:";           output.push_back(line);
    for (int i=0; i<lmSort(LMg).size(); i++) output.push_back(lmSort(LMg)[i]);
    
    line = "live loads:";           output.push_back(line);
    for (int i=0; i<lmSort(LMq).size(); i++) output.push_back(lmSort(LMq)[i]);
    output.push_back("");
    
    //------------- solve for load factors = 1.0 -------------
    if (!bm->Solve())
    {
        cout << "problem could not be solved" <<endl;
        delete bm;
        exit(1);
    }
    
    //------------- create result output -------------
    vector< vector <double> > res(bm->GetResults());
    vector< vector <double> > max(bm->GetMax());
    vector< vector <double> > sup(bm->GetReaction());
    
    line = "results: characteristic loads (1.0 1.0)";    output.push_back(line);
    output.push_back(sep);
    for (int i=0; i<resOut(max,sup).size(); i++) output.push_back(resOut(max,sup)[i]);
    output.push_back("\n");
    
    //if factors are given
    if (F.size()>=2)
    {
        if (F.size()>=4) bm->SetLoadFactors(F[0],F[1],F[2],F[3]);
        else bm->SetLoadFactors(F[0],F[1]);
        bm->Solve();
    
        max=bm->GetMax();
        sup=bm->GetReaction();
    
        line = "design loads ("+_s(F[0])+" "+_s(F[1]);
        if (F.size()>=4) line+=(" "+_s(F[2])+" "+_s(F[3])+")"); else line+=")";           
        output.push_back(line);
        output.push_back(sep);
        for (int i=0; i<resOut(max,sup).size(); i++) output.push_back(resOut(max,sup)[i]);
        output.push_back("\n");
    }
    
    //console output
    for (int i=0; i<output.size(); i++) cout<<output[i]<<endl;
    
    //if outfile is given
    if (outfilename!="")
    {
        ofstream outfile(outfilename.c_str()); 
        if (outfile)
        {
             outfile.flush(); 
             for (int i=0; i<output.size(); i++) outfile<<output[i]<<endl; 
             outfile.close();
        }
    }
  
    //if plotfile ist given
    if (plotfilename!="")
    {
        //replace with factorized results, keep deflections characteristic
        for (int i=1; i<5; i++)
            res[i]=bm->GetResults().at(i);      
        
        ofstream plotfile(plotfilename.c_str());    

        if(plotfile)
        {
            plotfile.flush();
        
            if (res.size()==7)
            {
                plotfile<<"#x\tMdmax\tMdmin\tVdmax\tVdmin\tdmax\tdmin"<<endl<<endl;
                if (res[1].size()==nf*101)
                {
                    for (int i=0;i<nf*101;i++) 
                    {
                        for (int j=0; j<7; j++)
                            plotfile<<res[j][i]<<"\t";
                        plotfile<<endl;
                    }
                }
            }
            plotfile.close();
        }
    }

    delete bm;
    return 0;
}


//return double values from string
vector<double> dbl_s(string str)
{
    istringstream tokens(str);
    string buf;
    vector<double> v;
 
    tokens>>buf;
    
    while (tokens>>buf)
    {
        stringstream k; k << buf;
        double x; k >> x;
        v.push_back(x);
    }
 
    return v;
}

//return string from values
string _s(double x)
{
    string buf;
    stringstream k(buf);
    k << x;
    k >> buf;
    return buf;
}

string _s(double x, int p)
{
    string buf;
    stringstream k(buf);
    k.precision(p);
    k << fixed << x;
    k >> buf;
    return buf;
}

string _s(int x)
{
    string buf;
    stringstream k(buf);
    k << x;
    k >> buf;
    return buf;
}

//sort loads for output
vector<string> lmSort(vector< vector<double> > LM)
{
    string line;
    vector<string> output;
    
    //find number of loaded spans
    int nf=0;
    for (int i=0; i<LM.size(); i++) if (int(LM[i][0])>nf) nf=int(LM[i][0]);
    
    vector< vector<double> > ldvalue(nf);
    vector< vector<double> > ldstart(nf);
    vector< vector<double> > ldend(nf);
    
    for (int lt=1; lt<=6; lt++)
    {
        for (int i=0; i<nf; i++)
        {
            ldvalue[i].clear();
            ldstart[i].clear();
            ldend[i].clear();
            for (int j=0; j<LM.size(); j++)
                if (int(LM[j][0])==i+1 && int(LM[j][1])==lt) 
                {
                    ldvalue[i].push_back(LM[j][2]);
                    ldstart[i].push_back(LM[j][3]);
                    ldend[i].push_back(LM[j][4]);
                }
        }
        int max=0; 
        for (int i=0; i<nf; i++) if (ldvalue[i].size()>max) max=ldvalue[i].size();
        for (int k=0; k<max; k++)
        {
            switch (lt)
            {
                case 1: line = "p:"; break;     //load type 1 - udl
                case 2: line = "P:"; break;     //load type 2 - point load
                case 3: line = "p\':"; break;   //load type 3 - partial udl
                case 4: line = "M:"; break;     //load type 4 - moment load
                case 5: line = "tz:"; break;    //load type 5 - trapezoidalload
                case 6: line = "tr\':"; break;  //load type 6 - partial triangular load
            }
            
            for (int i=0; i<nf; i++)
            {
                if (ldvalue[i].size()>k) line+=("\t"+_s(ldvalue[i][k]));
                else line+=("\t");
            }
            output.push_back(line);
            
            if (lt>1)
            {
                line = " a=";
                for (int i=0; i<nf; i++)
                {
                    if (ldstart[i].size()>k) line+=("\t"+_s(ldstart[i][k]));
                    else line+=("\t");
                }
                output.push_back(line);
            }
            
            if (lt==3 || lt>=5)
            {
                line = " l\'=";
                for (int i=0; i<nf; i++)
                {
                    if (ldend[i].size()>k) line+=("\t"+_s(ldend[i][k]));
                    else line+=("\t");
                }
                output.push_back(line);
            }
        }
    }
  
    return output;
}

//prepare max/min results for output
vector<string> resOut(vector< vector<double> > maxValues, vector< vector <double> > reactions) 
{
    string line;
    vector<string> output;
    
    //results
    if (maxValues.size()==7)
    {
        for (int j=1; j<7; j++)     //skip x (Mmax)
        {
            switch (j)
            {
                case 1: line = "Mmax:\t"; break;
                case 2: line = "Mmin:\t"; break;
                case 3: line = "Vmax:\t"; break;
                case 4: line = "Vmin:\t"; break;
                case 5: line = "dmax:\t"; break;
                case 6: line = "dmin:\t"; break;
            }
            for (int i=0; i<maxValues[j].size(); i++)
            {
                int prec;       //set output precision
                if (j<=4) prec=2; else prec=3;
                line+=_s(maxValues[j][i],prec)+ "\t";
            }

            output.push_back(line);
        }
    }
    
    //support reactions
    if (reactions.size()==2)
    {
        for (int j=0; j<2; j++)     
        {
            switch (j)
            {
                case 0: line = "Rmax:\t"; break;
                case 1: line = "Rmin:\t"; break;
            }
            for (int i=0; i<reactions[j].size(); i++)
                line+=_s(reactions[j][i],2)+ "\t";

            output.push_back(line);
        }
    }
    
    return output;
}
    
    
    
    
    
    
    
    
