//cbaGUI_drawings.cpp

#include "cbaGUI_drawings.h"


//---------------- GraphFrame -------------------------
BEGIN_EVENT_TABLE(GraphFrame, wxScrolledWindow)
    EVT_PAINT(GraphFrame::OnPaint)
    EVT_SIZE(GraphFrame::OnSize)

END_EVENT_TABLE()

void GraphFrame::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(this);
    dc.Clear();

    //get window dimensions
    wxSize sz = GetClientSize();
    wxRect bounds(0, 0, sz.x, sz.y);

    //set a frame
    int marg = int(bounds.width*0.05);
    bounds.x+=marg;
    bounds.y+=marg;
    bounds.width-=2*marg;
    bounds.height-=2*marg;

    //set font
    int fs = int(6+bounds.height/100);
    wxFont txtFont(*wxNORMAL_FONT);
    txtFont.SetPointSize(fs);
    dc.SetFont(txtFont);
    dc.SetPen(wxPen(wxColour(*wxBLACK),1));

    // draw
    switch (type)
    {
        case 0 : DrawSystem(dc, bounds, true); break;
        case 1 : DrawResults(dc, bounds, 3); break;
        case 2 : DrawStress(dc, bounds); break;
    }

}


void GraphFrame::OnSize(wxSizeEvent& event)
{
    Refresh();
}


void GraphFrame::SetSystem(vector<double> L, vector<double> R, vector< vector<double> > LMg, vector< vector<double> > LMq)
{
    Store.L = L;
    Store.R = R;
    Store.LMg = LMg;
    Store.LMq = LMq;
    Refresh();
}


void GraphFrame::SetResults(vector< vector<double> > results)
{
    Store.results = results;
    Refresh();
}


void GraphFrame::SetType(int inp) 
{ 
    type=inp; 
    Refresh();
}


vector< vector<double> > GraphFrame::GetResults()
{
    return Store.results;
}


void GraphFrame::Clear() 
{ 
    Store.results.clear();
    Store.LMg.clear();  
    Store.LMq.clear();
    Refresh();
} 
    

int GraphFrame::DrawSystem (wxDC& dc, wxRect bounds, bool dim)
{
    
    int ratio(0);  //return page usage for printing

    //get span numbers
    int nf(Store.L.size());

    if (nf>=1)
    {
        //set x scale factor for spans
        double x_max(0); for (int l=0; l<nf; l++) x_max+=Store.L[l];
        double x_scale=bounds.width/x_max;

        //set y scale factor for loads
        int max = GetLoadLines();
        
        wxCoord dy=int(bounds.height/10);
        if ((max+2)*dy>bounds.height) dy=int(bounds.height/(max+2));

        //set origin
        wxCoord x0(bounds.x);
        wxCoord y0(bounds.y+max*dy);
        wxRect area (x0, y0, 0, dy);
        
        //return ratio for printout
        ratio=int(max*dy*100/bounds.height);
 
        //draw beam
        DrawBeam (dc, bounds, y0, dim);
        
        //get max/min load for scaling
        vector<double> max_ld;
        for (int lt=1; lt<=6; lt++)
        {
            vector<double> vec;
            for (int i=0; i<int(Store.LMg.size()); i++) if (int(Store.LMg[i][1])==lt) vec.push_back(Store.LMg[i][2]);
            for (int i=0; i<int(Store.LMq.size()); i++) if (int(Store.LMq[i][1])==lt) vec.push_back(Store.LMq[i][2]);
            
            //set a different scale factor for lt2/4
            double m = GetVecPeak(vec);
            if (lt<3 || lt==4) max_ld.push_back(m);
            else if (m>max_ld[0]) max_ld[0]=m;
        }
        
        //draw loads for each span
        double l(0);
        for (int i=0; i<nf; i++)
        {
            area.x = int(x0+l*x_scale); l+=Store.L[i];
            area.width = int(Store.L[i]*x_scale);
            wxCoord y(y0);
            
            //check for self weight
            bool sw(Store.SW);
 
            //separate by load type
            for (int lt=1; lt<=6; lt++)
            {
                for (int lc=0; lc<2; lc++)
                {
                    vector< vector<double> > LM;
                    switch (lc)
                    {
                        case 0: LM=Store.LMg;         //draw permanent loads
                        dc.SetPen(wxPen(wxColour(*wxBLUE),1));
                        dc.SetBrush(wxBrush(wxColour(*wxBLUE), wxVERTICAL_HATCH));
                        break;

                        case 1: LM=Store.LMq;         //draw live loads
                        dc.SetPen(wxPen(wxColour(*wxRED),1));
                        dc.SetBrush(wxBrush(wxColour(*wxRED), wxVERTICAL_HATCH));
                        break;
                    }

                    for (int j=0; j<int(LM.size()); j++)
                    {
                        
                        int span=int(LM[j][0]);
                        int type=int(LM[j][1]);
                        if (span==i+1 && type==lt)
                        {
                            area.y = y;
                            
                            if (sw && lt==1) 
                            { 
                                //set a different colour for self weight
                                dc.SetPen(wxPen(wxColour(*wxLIGHT_GREY),1));
                                dc.SetBrush(wxBrush(wxColour(*wxLIGHT_GREY), wxVERTICAL_HATCH));
                                DrawLoad(dc, area, LM[j], max_ld[0]);
                                //start with the next load on top    
                                wxCoord d0 = int(dy/2 + dy/2*(LM[j][2]/max_ld[0]));     
                                y-=d0; 
                                //set back pen     
                                dc.SetPen(wxPen(wxColour(*wxBLUE),1));
                                dc.SetBrush(wxBrush(wxColour(*wxBLUE), wxVERTICAL_HATCH));
                                sw=false; 
                            }
                            else 
                            {
                                double l_scale(max_ld[0]);
                                if (type==2) l_scale=max_ld[1];
                                if (type==4) l_scale=max_ld[2];
                                DrawLoad(dc, area, LM[j], l_scale);
                                y-=dy;
                            }
                        }
                    }
                }
            }
        }
    }
    return ratio;
  
}


void GraphFrame::DrawLoad (wxDC& dc, wxRect bounds,  vector<double> LMi, double max_ld)
{
    //set some vertical space between loads
    int spc = int(bounds.height*0.05);
    int x0 = bounds.x;
    int y0 = bounds.y-spc;
    int wd = bounds.width;
    int ht = bounds.height-2*spc;

    int ispan = int(LMi[0]);
    int type = int(LMi[1]);
    double ld = LMi[2];
    double a = LMi[3];
    double l = LMi[4];

    //scale load
    if (ld>=0) ht=int(0.5*ht+0.5*ht*ld/max_ld);
    else
    {
        ht=-int(0.5*ht+0.5*ht*-ld/max_ld);
        y0+=ht;
    }

    //scale load position
    double L = Store.L[ispan-1];
    int xa = x0+int(wd*a/L);
    int xl = int(wd*l/L);
    
    wxBrush brush = dc.GetBrush();

    switch (type)
    {
        case 1: //load type 1 - udl
                dc.DrawRectangle(x0, y0-ht, wd, ht);
                break;

        case 2: //load type 2 - point load
                dc.DrawLine(xa, y0-ht, xa, y0);
                DrawArrow (dc, wxPoint(xa, y0), ht/3);
                break;

        case 3: //load type 3 - partial udl
                dc.DrawRectangle(xa, y0-ht, xl, ht);
                break;

        case 4: //load type 4 - moment load
                brush.SetStyle(wxTRANSPARENT);
                dc.SetBrush(brush);
                
                if (ld>0)
                {
                    dc.DrawArc(xa, y0, xa-ht/2, y0-ht/2, xa, y0-ht/2);
                    DrawArrow (dc, wxPoint(xa-ht/2, y0-ht/3), ht/3);
                }
                else
                {
                    dc.DrawArc(xa-ht/2, y0-ht/2, xa, y0-ht, xa, y0-ht/2);
                    DrawArrow (dc, wxPoint(xa-ht/2, int(y0-0.67*ht)), -ht/3);
                }
                
                brush.SetStyle(wxVERTICAL_HATCH);
                dc.SetBrush(brush);
                break;
         
        case 5: //load type 5 - trapezoidal loads
                DrawTrapeze(dc, wxPoint(x0, y0), wd, -ht, xa-x0, xa-x0+xl);
                break; 
                
        case 6: //load type 6 - partial triangular loads
                if (xl!=0) DrawTrapeze(dc, wxPoint(xa, y0), xl, -ht, 0, 0);
                break;
                
    }
}


void GraphFrame::DrawArrow (wxDC& dc, wxPoint origin, int sf)
{
    wxCoord x(origin.x);
    wxCoord y(origin.y);
    dc.DrawLine(x, y, x+sf/2, y-sf);
    dc.DrawLine(x+sf/2, y-sf, x-sf/2, y-sf);
    dc.DrawLine(x-sf/2, y-sf, x, y);
}


void GraphFrame::DrawTrapeze (wxDC& dc, wxPoint a, int wd, int ht, int xa, int xb)
{
    wxPoint b(a), c(a), d(a);
    b.x+=wd;
    c.x+=xb; c.y+=ht;
    d.x+=xa; d.y+=ht;
    wxPoint pts[4] ={a,b,c,d};
    dc.DrawPolygon(4, pts);
}

int GraphFrame::GetLoadLines()
{
    //get span numbers
    int nf=Store.L.size();

    //get max number of loads per span
    int mg(0), mq(0);
    for (int i=1; i<=nf; i++)
    {
        int ng(0);
        for (int j=0; j<int(Store.LMg.size()); j++) if (int(Store.LMg[j][0])==i) ng++;
        if (ng>mg) mg=ng;

        int nq(0);
        for (int j=0; j<int(Store.LMq.size()); j++) if (int(Store.LMq[j][0])==i) nq++;
        if (nq>mq) mq=nq;
    }
    return mg+mq;
}


double GraphFrame::GetVecPeak(vector<double> vec)
{
    double max(0), min(0);
    for (int i=0; i<int(vec.size()); i++) if (vec[i]>max) max=vec[i];
    for (int i=0; i<int(vec.size()); i++) if (vec[i]<min) min=vec[i];
    if ((max+min)<0) max=-min;
    return max;
}


void GraphFrame::DrawResults (wxDC& dc, wxRect bounds, int tp)
{
    //checking the result vector
    if (int(Store.results.size())==7)
    {
        //draw single value
        if (tp<3)
        {
            DrawBeam (dc, bounds, bounds.y+bounds.height/2, 0);
            DrawValues (dc, bounds, 1+tp*2);
        }
        else
        {
            //divide page into three, draw M, V and d
            bounds.height/=3;
            for (int i=0; i<3; i++)
            {
                DrawBeam (dc, bounds, bounds.y+bounds.height/2, 0);
                DrawValues (dc, bounds, 1+i*2);
                bounds.y+=bounds.height;
            }
        }
   }
}


void GraphFrame::DrawStress (wxDC& dc, wxRect bounds)
{
    dc.Clear();

    //checking the result vector
    if (int(Store.results.size())==7)
    {
        //draw bending stress
        bounds.height/=3;
        bounds.y+=bounds.height/4;
        DrawBeam (dc, bounds, bounds.y+bounds.height/2, 0);
        DrawValues (dc, bounds, 2);

        //draw shear stress
        bounds.y+=3*bounds.height/2;
        DrawBeam (dc, bounds, bounds.y+bounds.height/2, 0);
        DrawValues (dc, bounds, 4);
    }

}


void GraphFrame::DrawValues (wxDC& dc, wxRect bounds, int val)
{
    int width = bounds.width;
    int height = bounds.height;
    int x0 = bounds.x;
    int y0 = bounds.y+height/2;

    //draw text
    wxString valStr;
    wxString prec(_T("%4.1f"));
    switch (val)
    {
        case 1: if (!Store.LF) valStr=(_T("M:")); else valStr=(_T("Md:")); break;
        case 2: if (Store.Wy>0) valStr=(_T("Sigma:")); val--; break;
        case 3: if (!Store.LF) valStr=(_T("V:")); else valStr=(_T("Vd:")); break;
        case 4: if (Store.Az>0) valStr=(_T("Tau:")); val--; break;
        case 5: if (!Store.EI) valStr=(_T("EId:"));
                else
                {
                    valStr=_T("d:");
                    prec=_T("%1.3f");       //scale deflection values
                }
                break;
    }
    dc.DrawText(valStr, x0, wxCoord(y0-height*0.3));

    //set scale factor for x
    double x_max=Store.results[0].back();
    double x_scale=width/x_max;

    //set scale factor for y and global max/min value
    double y_max=GetVecPeak(Store.results[val]);
    double y_min=GetVecPeak(Store.results[val+1]);

    double y_scale=(0.40*height);
    if (y_max>y_min) y_scale/=y_max;
    else y_scale/=y_min;
    
    for (int j=0; j<2; j++)
    {
        wxCoord x_prv(x0), y_prv(y0);

        switch (j)
        {
            case 0: dc.SetPen(wxPen(wxColour(*wxBLUE),1)); break;
            case 1: dc.SetPen(wxPen(wxColour(*wxRED),1)); break;
        }

        bool xtxt(true);
        for (int i=0; i<int(Store.results[val+j].size()); i++)
        {
            double x=Store.results[0][i];
            double y=Store.results[val+j][i];
            wxCoord x_coord=wxCoord(x0+x*x_scale);
            wxCoord y_coord=wxCoord(y0+y*y_scale);

            //draw value
            if (!(y_coord==y0 && y_prv==y0)) dc.DrawLine(x_prv, y_prv, x_coord, y_coord);

            //if max/min draw value text
            if (((j==0 && y==y_max) || (j==1 && y==-y_min)) && (y_coord!=y0) && xtxt)
            {
                if (valStr==_T("Sigma:") && Store.Wy>0) y/=Store.Wy;
                if (valStr==_T("Tau:") && Store.Az>0) y/=Store.Az;
                
                wxString valY;
                if (y<-1000 || y>1000)
                {
                    valY.Printf(prec,y/1000);
                    valY+=_T("k");
                }
                else valY.Printf(prec,y);
                
                int fs = int(1.6*wxFont(dc.GetFont()).GetPointSize());
                dc.DrawText(valY, x_coord-fs, y_coord+j*-fs);
                xtxt=false;     //draw only one text
            }

            //draw vertical line
            dc.DrawLine(x_coord, y0, x_coord, y_coord);

            x_prv=x_coord;
            y_prv=y_coord;
        }
    }

}


void GraphFrame::DrawBeam (wxDC& dc, wxRect bounds, wxCoord y0, bool dim)
{
    int width = bounds.width;
    int height = bounds.height;
    int x0 = bounds.x;

    dc.SetPen(wxPen(wxColour(*wxBLACK),1));
    dc.DrawLine(x0, y0, x0+width, y0);

    //set scale factor for x
    double x_max(0); for (size_t l=0; l<Store.L.size(); l++) x_max+=Store.L[l];
    double x_scale=width/x_max;

    //set scale factor for support symbol
    wxCoord sf = wxCoord(8+height/50);

    //get span numbers
    int nf=int(Store.L.size());

    wxCoord x(x0);
    for (int l=0; l<nf; l++)
    {
        int len = int(Store.L[l]*x_scale);
        DrawSupport(dc, wxPoint(x,y0), sf, l);
        
        if (dim)
        {
            wxString lStr; lStr.Printf(_T("%4.2f"), Store.L[l]);
            DrawDim(dc, wxPoint(x,y0), len, sf, lStr);
        }
        x+=len;
        
    }
    DrawSupport(dc, wxPoint(x,y0), sf, nf);
}


void GraphFrame::DrawSupport (wxDC& dc, wxPoint origin, wxCoord sf, int nr)
{
    wxCoord x(origin.x);
    wxCoord y(origin.y);

    int def = int(Store.R.at(nr*2));
    int rot = int(Store.R.at(nr*2+1));

    if (def<0)      //fixed support deflection
    {
        dc.DrawLine(x, y, x+sf/2, y+sf);
        dc.DrawLine(x+sf/2, y+sf, x-sf/2, y+sf);
        dc.DrawLine(x-sf/2, y+sf, x, y);
    }

    if (def>0)      //spring support deflection
    {
        dc.DrawLine(x, y, x, int(y+0.1*sf));
        dc.DrawLine(x, int(y+0.1*sf), x-sf/2, int(y+0.2*sf));
        dc.DrawLine(x-sf/2, int(y+0.2*sf), x+sf/2, int(y+0.4*sf));
        dc.DrawLine(x+sf/2, int(y+0.4*sf), x-sf/2, int(y+0.6*sf));
        dc.DrawLine(x-sf/2, int(y+0.6*sf), x+sf/2, int(y+0.8*sf));
        dc.DrawLine(x+sf/2, int(y+0.8*sf), x, int(y+0.9*sf));
        dc.DrawLine(x, int(y+0.9*sf), x, y+sf);
        dc.DrawLine(x-sf/2, y+sf, x+sf/2, y+sf);
    }

    if (rot<0)      //fixed support rotation
    {
        dc.DrawLine(x-sf/2, y-sf/2, x+sf/2, y+sf/2);
        dc.DrawLine(x+sf/2, y-sf/2, x-sf/2, y+sf/2);
    }

    if (rot>0)      //spring support rotation
    {
        dc.DrawLine(x-sf/2, y+sf/2, x-sf/2, y+sf);
        wxBrush brush = dc.GetBrush();
        dc.SetBrush(wxBrush(brush.GetColour(), wxTRANSPARENT));
        dc.DrawArc(x-sf/2, y+sf/2, x, y, x, y+sf/2);
        dc.SetBrush(brush);
    }
}


void GraphFrame::DrawDim(wxDC& dc, wxPoint pos, int len, int sf, wxString txt)
{
    wxPoint from(pos);
    from.y+=2*sf;
    wxPoint to(from);
    to.x+=len;
    
    wxPoint d1, d2;
    d1.x=0; d1.y=sf/2;
    d2.x=+sf/3; d2.y=-sf/3;
 
    dc.DrawLine(from.x, from.y, to.x, to.y);
    dc.DrawLine(from.x+d1.x, from.y+d1.y, from.x-d1.x, from.y-d1.y);
    dc.DrawLine(from.x+d2.x, from.y+d2.y, from.x-d2.x, from.y-d2.y);
    dc.DrawLine(to.x+d1.x, to.y+d1.y, to.x-d1.x, to.y-d1.y);
    dc.DrawLine(to.x+d2.x, to.y+d2.y, to.x-d2.x, to.y-d2.y);
    
    wxSize ext=dc.GetTextExtent(txt);
    wxPoint tpos(from); tpos.x+=int((len-ext.x)/2); tpos.y+=int(ext.y/5);
    dc.DrawText(txt, tpos.x, tpos.y);
}


int GraphFrame::DrawResultText (wxDC& dc, wxRect bounds, wxArrayString resTxt)
{
    int x0 = bounds.x;
    int y0 = bounds.y;

    //set scale factor for text
    wxFont font = dc.GetFont();
    int fs = font.GetPointSize();
   
    //see if it fits on the page
    int lines = resTxt.GetCount();
    int ht = lines*3*fs/2;
    int ratio = (int) 100*ht/bounds.height;
    if (ratio>100) 
    {
        fs = (int) fs*100/ratio;
        ratio=100;
    }

    font.SetPointSize(fs);
    dc.SetFont(font);
 
    //output as table with fixed cols
    for (size_t i=0; i<resTxt.GetCount(); i++)
    {
        wxChar tab(_T('\t'));
        wxString line = resTxt[i];
        wxCoord x(x0);
        
        while (!line.IsEmpty())
        {
            dc.DrawText(line.BeforeFirst(tab), x, wxCoord(y0));
            line=line.AfterFirst(tab);
            x+=6*fs;
            if (x>x0+bounds.width) break;
        }
        y0+=3*fs/2;
    }
    return ratio;
}


int GraphFrame::DrawHead (wxDC& dc, wxRect bounds, wxString headTxt)
{
    int ratio(0);
    
    if (!headTxt.IsEmpty())
    {
        //get font size
        int fs = dc.GetFont().GetPointSize();

        //draw header text
        int y0 = bounds.y;
        while (!headTxt.IsEmpty())
        {
            dc.DrawText(headTxt.BeforeFirst(_T('\n')), bounds.x, y0);
            headTxt=headTxt.AfterFirst(_T('\n'));
            y0+=3*fs/2;
            if (y0>(bounds.y+bounds.height)) break;
        }
    
        //draw a line
        y0+=3*fs/2;
        wxPen pen = dc.GetPen();
        dc.SetPen(wxPen(wxColour(*wxLIGHT_GREY),1));
        dc.DrawLine(bounds.x, y0, bounds.x+bounds.width, y0);
        dc.SetPen(pen);
        ratio = int((y0-bounds.y)*100/bounds.height);
    }
    return ratio;
}




