//cbaGUI.cpp

#include "cbaGUI.h"

//--------------- MainApp ---------------
IMPLEMENT_APP(MainApp)

bool MainApp::OnInit()
{

    wxString title(_T("wxCBA"));
#ifdef __WXGTK__
    title = _T("cba-gtk");
#endif
    frame = new MainFrame(title);
    frame->Show(TRUE);
    SetTopWindow(frame);
    return TRUE;
}

int MainApp::OnExit()
{
    return 0;
}


//--------------- MainFrame ---------------
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
    EVT_MENU(ID_QUIT, MainFrame::OnQuit)
    EVT_MENU(ID_INFO, MainFrame::OnInfo)
    EVT_MENU(ID_NEW, MainFrame::OnNewBeam)
    EVT_MENU(ID_OPEN, MainFrame::OnOpenFile)
    EVT_MENU(ID_SAVE, MainFrame::OnSaveInput)
    EVT_MENU(ID_SET, MainFrame::OnSettings)

    EVT_BUTTON(ID_BT_SP_ADD, MainFrame::OnSpanAdd)
    EVT_BUTTON(ID_BT_SP_DEL, MainFrame::OnSpanDel)
    EVT_BUTTON(ID_BT_EI, MainFrame::OnSetEI)
    EVT_BUTTON(ID_BT_RST, MainFrame::OnSetRst)

    EVT_BUTTON(ID_BT_LD_ADD, MainFrame::OnLoadAdd)
    EVT_BUTTON(ID_BT_LD_DEL, MainFrame::OnLoadDel)
    EVT_BUTTON(ID_BT_LD_FAC, MainFrame::OnSetLF)
    EVT_CHECKBOX(ID_CHK_SW, MainFrame::OnChkSw)

    EVT_BUTTON(ID_BT_COPY, MainFrame::OnClipRes)
    EVT_BUTTON(ID_BT_SAVE, MainFrame::OnSaveRes)
    EVT_BUTTON(ID_BT_PRINT, MainFrame::OnPrint)

    EVT_GRID_CELL_CHANGE(MainFrame::OnCellInputGrid)
    EVT_NOTEBOOK_PAGE_CHANGED(ID_NB_RES, MainFrame::OnSysPage)
    EVT_NOTEBOOK_PAGE_CHANGED(ID_NB_INP, MainFrame::OnLoadPage)
    EVT_RADIOBUTTON(wxID_ANY, MainFrame::OnRadioButton)

END_EVENT_TABLE()


MainFrame::MainFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
{
    //set defaults
    Store.E=1.0; Store.I=1.0;
    wxString setDir = wxGetCwd();

#ifdef __WXGTK__
    //change to user home dir in linux
    setDir = wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+_T(".cba");
    if (!wxFileName::DirExists(setDir)) wxMkdir(setDir);

    //set decimal point to "."
    setlocale(LC_NUMERIC, "C");
#endif

    //read values stored in settings.xml
    wxString setFile = setDir+wxFileName::GetPathSeparator()+_T("settings.xml");
    SettingsDialog *dialog = new SettingsDialog(this, -1, _T(""), setFile);
    SetDefaults(dialog->GetItems());

    CreateGUIControls();
}


MainFrame::~MainFrame()
{
}


void MainFrame::CreateGUIControls()
{
    //Main window
    SetBackgroundColour(wxColour(*wxWHITE));
    SetSize(50, 50, 600, 600);

    //Icon
    SetIcon(wxIcon(cba_xpm));

    //Menu
    wxMenu *menuFile = new wxMenu;
    menuFile->Append(ID_NEW, _T("Create &New Beam") );
    menuFile->AppendSeparator();
    menuFile->Append(ID_OPEN, _T("Open &Input File") );
    menuFile->Append(ID_SAVE, _T("&Save Input Data") );
    menuFile->AppendSeparator();
    menuFile->Append(ID_SET, _T("Se&ttings") );
    menuFile->AppendSeparator();
    menuFile->Append(ID_QUIT, _T("E&xit") );

    wxMenu *menuAbout = new wxMenu;
    menuAbout->Append(ID_INFO, _T("A&bout") );

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menuFile, _T("&File") );
    menuBar->Append( menuAbout, _T("&Info") );
    SetMenuBar( menuBar );


    //input grids
    NbInp = new wxNotebook(this, ID_NB_INP, wxDefaultPosition, wxSize(600,150), wxNB_BOTTOM);
    udl_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);
    pl_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);
    pudl_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);
    ml_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);
    tz_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);
    ptr_Grid = new wxGrid(NbInp, -1, wxPoint(0,0), wxDefaultSize);

    for (int i=0; i<6; i++)
    {
        wxArrayString rows;
        rows.Add(_T("l="));

        switch (i)
        {
            case 0: grid=udl_Grid;
                    rows.Add(_T("g:"));
                    rows.Add(_T("q:"));
                    break;
            case 1: grid=pl_Grid;
                    rows.Add(_T("G:"));
                    rows.Add(_T("Q:"));
                    rows.Add(_T("a="));
                    break;
            case 2: grid=pudl_Grid;
                    rows.Add(_T("g\':"));
                    rows.Add(_T("q\':"));
                    rows.Add(_T("a="));
                    rows.Add(_T("l\'="));
                    break;
            case 3: grid=ml_Grid;
                    rows.Add(_T("Mg:"));
                    rows.Add(_T("Mq:"));
                    rows.Add(_T("a="));
                    break;
            case 4: grid=tz_Grid;
                    rows.Add(_T("g:"));
                    rows.Add(_T("q:"));
                    rows.Add(_T("a="));
                    rows.Add(_T("l\'="));
                    break;
            case 5: grid=ptr_Grid;
                    rows.Add(_T("g\':"));
                    rows.Add(_T("q\':"));
                    rows.Add(_T("a="));
                    rows.Add(_T("l\'="));
                    break;
        }

        grid->CreateGrid(0,0,wxGrid::wxGridSelectCells);
        grid->SetLabelBackgroundColour(*wxWHITE);
        grid->SetColLabelSize(20);
        grid->SetRowLabelSize(40);
        grid->SetDefaultColSize(50);
	    grid->SetDefaultRowSize(20);
	    grid->SetDefaultCellAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE );

	    grid->AppendCols(1);
	    grid->SetColLabelValue(0, _T("1"));

        grid->AppendRows(rows.GetCount());
        grid->SetCellBackgroundColour(0, 0, wxColour(255,255,150,255));

        for (size_t j=0; j<rows.GetCount(); j++) grid->SetRowLabelValue(j, rows[j]);
    }

    wxImageList *images = new wxImageList(24,16);
    images->Add(wxIcon(udl_xpm));
    images->Add(wxIcon(pl_xpm));
    images->Add(wxIcon(pudl_xpm));
    images->Add(wxIcon(ml_xpm));
    images->Add(wxIcon(tz_xpm));
    images->Add(wxIcon(ptr_xpm));

    NbInp->SetImageList(images);

    NbInp->AddPage(udl_Grid, _T("p"), true, 0);
    NbInp->AddPage(pl_Grid, _T("P"), false, 1);
    NbInp->AddPage(pudl_Grid, _T("p\'"), false, 2);
    NbInp->AddPage(ml_Grid, _T("M"), false, 3);
    NbInp->AddPage(tz_Grid, _T("tz"), false, 4);
    NbInp->AddPage(ptr_Grid, _T("tr\'"), false, 5);

    grid=udl_Grid;
    grid->SetFocus();

    //input buttons
    wxPanel *PanelBtLd = new wxPanel(this, -1, wxDefaultPosition, wxSize(340,40));
    BtLdDel = new wxButton(PanelBtLd, ID_BT_LD_DEL, _T("-load"), wxPoint(0,0), wxSize(50,25));
    BtLdAdd = new wxButton(PanelBtLd, ID_BT_LD_ADD, _T("+load"), wxPoint(60,0), wxSize(50,25));
    BtLdFac = new wxButton(PanelBtLd, ID_BT_LD_FAC, _T("LF"), wxPoint(130,0), wxSize(40,25));
    ChkSw = new wxCheckBox(PanelBtLd, ID_CHK_SW, _T("self-weight"),  wxPoint(200,0), wxSize(95,25));
    ChkSw->Show(false);

    wxPanel *PanelBtGeo = new wxPanel(this, -1, wxDefaultPosition, wxSize(240,40));
    BtEI = new wxButton(PanelBtGeo, ID_BT_EI, _T("EI"), wxPoint(0,0), wxSize(40,25));
    BtRst = new wxButton(PanelBtGeo, ID_BT_RST, _T("R"), wxPoint(50,0), wxSize(40,25));
    BtSpDel = new wxButton(PanelBtGeo, ID_BT_SP_DEL,  _T("-span"), wxPoint(110,0), wxSize(50,25));
    BtSpAdd = new wxButton(PanelBtGeo, ID_BT_SP_ADD, _T("+span"), wxPoint(170,0), wxSize(50,25));

    wxBoxSizer *H1sizer = new wxBoxSizer(wxHORIZONTAL);
    H1sizer->Add(PanelBtLd, 1, wxTOP, 10);
    H1sizer->Add(PanelBtGeo, 0, wxALIGN_RIGHT | wxTOP, 10);

    wxBoxSizer *V1sizer = new wxBoxSizer(wxVERTICAL);
    V1sizer->Add(NbInp, 1, wxEXPAND | wxALL, 10);
    V1sizer->Add(H1sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);

    //choice forces/stresses
    wxPanel *PanelFS = new wxPanel(this, -1, wxDefaultPosition, wxSize(340,40));
    BtFc = new wxRadioButton(PanelFS, ID_BT_FC, _T("Forces"), wxPoint(5,0), wxSize(60,25), wxRB_SINGLE);
    BtSt = new wxRadioButton(PanelFS, ID_BT_ST, _T("Stresses"), wxPoint(80,0), wxSize(80,25), wxRB_SINGLE);
    BtFc->SetValue(true);

    //results
    NbRes = new wxNotebook(this, ID_NB_RES, wxDefaultPosition, wxSize(600,340), wxNB_BOTTOM);

	system = new GraphFrame;
	system->Create(NbRes, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
	system->SetBackgroundColour(wxColour(*wxWHITE));
	system->SetType(0);

	graph = new GraphFrame;
	graph->Create(NbRes, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL);
	graph->SetBackgroundColour(wxColour(*wxWHITE));
	graph->SetType(1);

	TcRes = new wxTextCtrl(NbRes, ID_TC_RES, _T(""), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

    NbRes->AddPage(system, _T("System"));
    NbRes->AddPage(graph, _T("Graph"));
    NbRes->AddPage(TcRes, _T("Results"));

    NbRes->SetSelection(1);
    NbRes->SetSelection(0);

    //result buttons
    wxPanel *PanelBtRes = new wxPanel(this, -1, wxDefaultPosition, wxSize(240,40));
    BtCopy = new wxBitmapButton(PanelBtRes, ID_BT_COPY, wxBitmap(copy_xpm), wxPoint(0,0), wxSize(50,25));
    BtSave = new wxBitmapButton(PanelBtRes, ID_BT_SAVE, wxBitmap(save_xpm), wxPoint(60,0), wxSize(50,25));
    BtPrint = new wxBitmapButton(PanelBtRes, ID_BT_PRINT, wxBitmap(print_xpm), wxPoint(120,0), wxSize(50,25));

    wxBoxSizer *H2sizer = new wxBoxSizer(wxHORIZONTAL);
    H2sizer->Add(PanelFS, 1, wxTOP, 10);
    H2sizer->Add(PanelBtRes, 0, wxALIGN_RIGHT | wxTOP, 10);

    wxBoxSizer *V2sizer = new wxBoxSizer(wxVERTICAL);
    V2sizer->Add(NbRes, 1, wxEXPAND | wxALL, 10);
    V2sizer->Add(H2sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);

    wxBoxSizer *Vsizer = new wxBoxSizer(wxVERTICAL);
    Vsizer->Add(V1sizer, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 20);
    Vsizer->Add(V2sizer, 1, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 20);

    SetSizer(Vsizer);
    Vsizer->SetSizeHints(this);

    //Statusbar
    CreateStatusBar();
    SetStatusText( _T("continuous beam analysis") );

}


void MainFrame::OnQuit(wxCommandEvent& event)
{
    Close(TRUE);
}


void MainFrame::OnInfo(wxCommandEvent &event)
{
    wxString info = _T("cba 0.3.6");
    info+=_T("\ncontinuous beam analysis");
    info+=_T("\nhttp://cbeam.sourceforge.net/");
    wxMessageBox(info, _T("About"), wxOK , this);
}


//--------------- create a new beam by user dialog ---------------
void MainFrame::OnNewBeam(wxCommandEvent& event)
{
    wxArrayString items;
    BeamDialog *dialog = new BeamDialog(this, -1, _T("Create New Beam:"));
    if (dialog->ShowModal()) items = dialog->GetItems();

    if (items.GetCount()>3)
    {
        long int nf;
        items[0].ToLong(&nf);
        double l;
        ChkValue(items[1]).ToDouble(&l);

        if (nf>0 && l>0)
        {
            Store.L.clear();
            Store.LMg.clear();
            Store.LMq.clear();

            for (int i=0; i<nf; i++)
            {
                Store.L.push_back(l);
                for (int lc=0; lc<2; lc++)
                {
                    double p;
                    ChkValue(items[2+lc]).ToDouble(&p);
                    if (p!=0)
                    {
                        vector<double> ld(5,0);
                        ld[0]=i+1;
                        ld[1]=1;
                        ld[2]=p;
                        if (lc==0) Store.LMg.push_back(ld);
                        if (lc==1) Store.LMq.push_back(ld);
                    }
                }
            }

            //set defaults
            Store.E=1.0; Store.I=1.0;
            Store.R.clear();
            for (int i=0; i<(nf+1*2); i++)
                if (i%2==0) Store.R.push_back(-1);
                else Store.R.push_back(0);

            FillInputGrids();
            Solve();
        }
    }
}


//--------------- open file for input data ---------------
void MainFrame::OnOpenFile(wxCommandEvent& event)
{
    wxArrayString text;

    wxFileDialog *openFileDialog = new wxFileDialog(this,_T("Open Input File:"), _T(""), _T(""), _T("*"), wxFD_OPEN);
	if (openFileDialog->ShowModal() == wxID_OK)
	{
		wxString fileName = openFileDialog->GetPath();
        wxTextFile *txtFile = new wxTextFile(fileName);
        if (txtFile->Exists())
        {
            txtFile->Open();
            for (size_t line=0; line<txtFile->GetLineCount(); line++) text.Add(txtFile->GetLine(line));
            txtFile->Close();
        }

        //converting text to values
        vector<double> L;
        double E(1), I(1);
        vector<double> R;
        vector<double> F;
        vector< vector<double> > LM;

        for (int i=0; i<int(text.GetCount()); i++)
        {
            wxString txtLine = text[i];
            if (txtLine.Find('#')==wxNOT_FOUND)     //ignore comments
            {
                wxArrayString cut;
                wxChar spc(_T(' '));
                while (!txtLine.IsEmpty())
                {
                    cut.Add(txtLine.BeforeFirst(spc));
                    txtLine=txtLine.AfterFirst(spc);
                }
                if (cut.GetCount()>0)
                {
                    wxString head(cut[0]);
                    if (head.Len()>3) head=head.Left(4);

                    vector<double> val;
                    for (int j=1; j<int(cut.GetCount()); j++)
                    {
                        double v;
                        cut[j].ToDouble(&v);
                        val.push_back(v);
                    }

                    if (head==_T("SPAN")) for (int k=0; k<int(val.size()); k++) if (val[k]>0) L.push_back(val[k]);
                    if (head==_T("ELAS")) if (val.size()>0) E=val[0];
                    if (head==_T("INER")) if (val.size()>0) I=val[0];
                    if (head==_T("CONS")) for (int k=0; k<int(val.size()); k++) R.push_back(val[k]);
                    if (head==_T("FACT")) Store.ldFac=text[i].AfterFirst(_T(' '));
                    if (head==_T("LOAD"))
                    {
                        vector<double> ld;
                        for (int k=0; k<int(val.size()); k++) ld.push_back(val[k]);
                        if (ld.size()>0) LM.push_back(ld);
                    }

                    //gui values
                    if (head==_T("UNIT"))
                    {
                        if (cut.GetCount()>1) Store.fcUnit=cut[1];
                        if (cut.GetCount()>2) Store.lnUnit=cut[2];
                    }
                    if (head==_T("MATE"))
                    {
                        if (cut.GetCount()>1) Store.Mat=cut[1]+spc;
                        if (cut.GetCount()>2) Store.Mat+=cut[2];
                    }
                    if (head==_T("SECT"))
                    {
                        Store.Sec.Empty();
                        for (int j=1; j<int(cut.GetCount()); j++) Store.Sec+=(cut[j]+spc);
                        Store.Sec=Store.Sec.BeforeLast(spc);
                        MatSecDialog *dialog = new MatSecDialog(this, -1, _T(""));
                        dialog->SetDbLoc(Store.dbMat, Store.dbSec);
                        dialog->SetUnits(Store.fcUnit, Store.lnUnit);
                        dialog->SetMat(Store.Mat);
                        dialog->SetSec(Store.Sec);
                        SetMatSec(dialog->GetItems());
                    }
                    if (head==_T("SELF"))
                    {
                        if (cut.GetCount()>1)
                            if (cut[1]==_T("YES")) ChkSw->SetValue(true); else  ChkSw->SetValue(false);
                    }
                }
            }
        }

        //use the first inertia for the whole beam
        vector<double> Ivec;
        for (int i=0; i<int(L.size()); i++) Ivec.push_back(I);

        //check if the input file is valid
        cBeam *bm = new cBeam();
        if (bm->SetGeometry(L,E,Ivec,R))
        {
            vector< vector<double> > geo(4);
            geo=bm->GetGeometry();
            Store.L=bm->GetGeometry()[0];
            Store.E=bm->GetGeometry()[1][0];
            Store.I=bm->GetGeometry()[2][0];
            Store.R=bm->GetGeometry()[3];

            if (bm->SetLoads(LM))
            {
                Store.LMg=bm->GetLoadsG();
                Store.LMq=bm->GetLoadsQ();
            }
            FillInputGrids();
            Solve();
        }
        else wxMessageBox(_T("This file seems to be not valid, try again."), _T("Input File Reading Failure"), wxOK);

        delete bm;
    }
}


//--------------- save input data ---------------
void MainFrame::OnSaveInput(wxCommandEvent& event)
{
    wxFileDialog *saveFileDialog = new wxFileDialog(this,_T("Save Input To:"), _T(""), _T("*.txt"), _T("*.txt"), wxFD_SAVE);

	if (saveFileDialog->ShowModal() == wxID_OK)
	{
		wxString fileName = saveFileDialog->GetPath();
		wxString saveDir = wxFileName(fileName).GetPath();

		wxTextFile *File = new wxTextFile;
        File->Create(fileName);

        wxString txt, line;
        line=_T("#cba input file");
        File->AddLine(line);

        if (Store.L.size()>0)
        {
            line=_T("SPANS");
            for (size_t i=0; i<Store.L.size(); i++)
            {
                txt.Printf(_T(" %4.2f"), Store.L[i]);
                line+=txt;
            }
            File->AddLine(line);
        }
        if (Store.E!=1)
        {
            line.Printf(_T("ELASTICITY %4.2e"), Store.E);
            File->AddLine(line);
        }
        if (Store.I!=1)
        {
            line.Printf(_T("INERTIA %4.2e"), Store.I);
            File->AddLine(line);
        }
        if (Store.R.size()>0)
        {
            line=_T("CONSTRAINTS");
            for (size_t i=0; i<Store.R.size(); i++)
            {
                txt.Printf(_T(" %d"), int(Store.R[i]));
                line+=txt;
            }
            File->AddLine(line);
        }

        //dont save self weight
        size_t start(0);
        if (ChkSw->IsChecked()) start=Store.L.size();

        for (size_t i=start; i<Store.LMg.size(); i++)
        {
            line=_T("LOADS");
            for (size_t j=0; j<Store.LMg[i].size(); j++)
            {
                if (j<2) txt.Printf(_T(" %d"), int(Store.LMg[i][j]));
                else txt.Printf(_T(" %4.2f"), Store.LMg[i][j]);
                if (j==2) txt+=(_T(" 0.00"));
                line+=txt;
            }
            File->AddLine(line);
        }
        for (size_t i=0; i<Store.LMq.size(); i++)
        {
            line=_T("LOADS");
            for (size_t j=0; j<Store.LMq[i].size(); j++)
            {
                if (j<2) txt.Printf(_T(" %d"), int(Store.LMq[i][j]));
                else txt.Printf(_T(" %4.2f"), Store.LMq[i][j]);
                if (j==1) txt+=(_T(" 0.00"));
                line+=txt;
            }
            File->AddLine(line);
        }

        if (!Store.ldFac.IsEmpty()) File->AddLine(_T("FACTORS ")+Store.ldFac);

        if (!Store.Mat.IsEmpty() && !Store.Sec.IsEmpty())
        {
            File->AddLine(_T("#gui values - ignored by cba"));
            File->AddLine(_T("UNITS ")+Store.fcUnit+_T(" ")+Store.lnUnit);
            File->AddLine(_T("MATERIAL ")+Store.Mat);
            File->AddLine(_T("SECTION ")+Store.Sec);
            if (ChkSw->IsChecked()) File->AddLine(_T("SELFWT YES")); else File->AddLine(_T("SELFWT NO"));
        }

        File->Write();
        File->Close();
        wxMessageBox(_T("Input data saved to ")+fileName, _T("File saved"), wxOK);
    }
}


//--------------- change global settings ---------------
void MainFrame::OnSettings(wxCommandEvent& event)
{
    wxString setDir = wxGetCwd();
#ifdef __WXGTK__
    //change to user home dir in linux
    setDir = wxFileName::GetHomeDir()+wxFileName::GetPathSeparator()+_T(".cba");
#endif
     //read values stored in settings.xml
    wxArrayString items;
    wxString setFile = setDir+wxFileName::GetPathSeparator()+_T("settings.xml");
    SettingsDialog *dialog = new SettingsDialog(this, -1, _T("Global Settings"), setFile);
    if (dialog->ShowModal()) items = dialog->GetItems();
    if (!items.IsEmpty())
    {
        SetDefaults(items);
        Solve();
    }
}


void MainFrame::SetDefaults(wxArrayString items)
{
    if (!items.IsEmpty())
    {
        for (size_t i=0; i<items.GetCount(); i++)
        {
            wxString vStr=items[i];
            switch (i)
            {
                case 0: Store.Head=vStr; break;     //header
                case 1: Store.fcUnit=vStr; break;   //force unit
                case 2: Store.lnUnit=vStr; break;   //length unit
                case 3: Store.ldFac=vStr; break;    //load factors
                case 4: Store.dbMat=vStr; break;    //material database
                case 5: Store.dbSec=vStr; break;    //section database
            }

        }
    }
}


//--------------- set geometry ---------------
void MainFrame::OnSpanAdd(wxCommandEvent& event)
{
    //check if last length >0
    double l;
    wxString lStr=grid->GetCellValue(0, grid->GetNumberCols()-1);
    lStr.ToDouble(&l);
    if (l>0)
    {
        addCols(udl_Grid, 1);
        addCols(pl_Grid, 1);
        addCols(pudl_Grid, 1);
        addCols(ml_Grid, 1);
        addCols(tz_Grid, 1);
        addCols(ptr_Grid, 1);
        Solve();
    }
}


void MainFrame::OnSpanDel(wxCommandEvent& event)
{
    addCols(udl_Grid, -1);
    addCols(pl_Grid, -1);
    addCols(pudl_Grid, -1);
    addCols(ml_Grid, -1);
    addCols(tz_Grid, -1);
    addCols(ptr_Grid, -1);
    Solve();
}


void MainFrame::OnSetEI(wxCommandEvent& event)
{
    MatSecDialog *dialog = new MatSecDialog(this, -1, _T("Set Material/Section:"));
    dialog->SetDbLoc(Store.dbMat, Store.dbSec);
    dialog->SetUnits(Store.fcUnit, Store.lnUnit);
    dialog->SetMat(Store.Mat);
    dialog->SetSec(Store.Sec);

    wxArrayString items;
    if (dialog->ShowModal()) items = dialog->GetItems();
    SetMatSec(items);
    Solve();
}


void MainFrame::SetMatSec(wxArrayString items)
{
    for (size_t i=0; i<items.GetCount(); i++)
    {
        double val;
        wxString vStr(items[i]);
        vStr.ToDouble(&val);
        switch (i)
        {
            case 0: if (!val>0) val=1.0; Store.E=val; break;    //elasticity
            case 1: Store.g0=val; break;                        //density
            case 2: Store.g0*=val; break;                       //area
            case 3: if (!val>0) val=1.0; Store.I=val; break;    //inertia
            case 4: Store.Wy=val; break;                        //sec modulus
            case 5: Store.Az=val; break;                        //shear area
            case 6: Store.Mat=vStr; break;                      //material
            case 7: Store.Sec=vStr; break;                      //section
        }
    }

    //show selfweight checkbox
    if (Store.g0>0) ChkSw->Show(true);
    else
    {
        ChkSw->SetValue(false);
        ChkSw->Show(false);
    }

    //show force/stress buttons
    if (NbRes->GetSelection()==1 && Store.Wy*Store.Az>0)
    {
        BtFc->Show(true);
        BtSt->Show(true);
        //resize the window to show
        wxSize sz = this->GetClientSize();
        sz.SetHeight(sz.GetHeight()+1);
        this->SetClientSize(sz);
    }
    else
    {
        BtFc->SetValue(true);
        BtSt->SetValue(false);
        BtFc->Show(false);
        BtSt->Show(false);
    }
}


void MainFrame::OnSetRst(wxCommandEvent& event)
{
    if (int(Store.L.size())>0)
    {
        wxArrayString labels;
        labels.Add(_T("def"));
        labels.Add(_T("rot"));

        wxArrayString defaults;
        for (int i=0; i<int(Store.R.size()); i++)
        {
            wxString rStr; rStr.Printf(_T("%4.0f"), Store.R[i]);
            defaults.Add(rStr);
        }

        wxArrayString items;
        ConstraintsDialog *dialog = new ConstraintsDialog(this, -1, _T("Set Restraints/Springs:"), defaults);
        if (dialog->ShowModal()) items = dialog->GetValues();

        if (items.GetCount()>0)
        {
            Store.R.clear();
            for (size_t i=0; i<items.GetCount(); i++)
            {
                double d;
                items[i].ToDouble(&d);
                Store.R.push_back(d);
            }
            Solve();
        }
    }
}


//--------------- set loads ---------------
void MainFrame::OnLoadAdd(wxCommandEvent& event)
{
    int rows(0);
    switch (NbInp->GetSelection())
    {
        case 0: rows=2; break;
        case 1: rows=3; break;
        case 2: rows=4; break;
        case 3: rows=3; break;
        case 4: rows=4; break;
        case 5: rows=4; break;
    }
    addRows(rows);
}


void MainFrame::OnLoadDel(wxCommandEvent& event)
{
    int rows(0);
    switch (NbInp->GetSelection())
    {
        case 0: rows=2; break;
        case 1: rows=3; break;
        case 2: rows=4; break;
        case 3: rows=3; break;
        case 4: rows=4; break;
        case 5: rows=4; break;
    }
    if (grid->GetNumberRows()>rows+1) addRows(-rows);
    else
    {
        for (int i=0; i<grid->GetNumberCols(); i++)
            for (int j=1; j<=grid->GetNumberRows(); j++) grid->SetCellValue(j, i, _T(""));

        Solve();
    }
}


void MainFrame::OnSetLF(wxCommandEvent& event)
{
    wxString factors;
    LoadFactorDialog *dialog = new LoadFactorDialog(this, -1, _T("Set Load Factors:"), Store.ldFac);
    if (dialog->ShowModal()) factors = dialog->GetFactors();

    if (!factors.IsEmpty())
    {
        Store.ldFac=factors;
        Solve();
    }
    else if (!Store.ldFac.IsEmpty()) { Store.ldFac.Empty(); Solve(); }
}


void MainFrame::OnChkSw(wxCommandEvent& event)
{
     Solve();
}


//--------------- output ---------------
void MainFrame::OnClipRes(wxCommandEvent& event)
{
    TcRes->SetSelection(-1, -1);
    TcRes->Copy();
    TcRes->SetInsertionPoint(0);
    wxMessageBox(_T("Results were copied successfully to clipboard."), _T("Clipboard"), wxOK);
}


void MainFrame::OnSaveRes(wxCommandEvent& event)
{
    wxArrayString txtLines;
    wxString saveWhat;

    //save plot data if graph is selected
    if (NbRes->GetSelection()==1)
    {
        txtLines=CreatePlotTable();
        saveWhat=_T("Plot Data");
    }
    else
    {
        for (int i=0; i<TcRes->GetNumberOfLines(); i++) txtLines.Add(TcRes->GetLineText(i));
        saveWhat=_T("Results");
    }

    wxFileDialog *saveFileDialog = new wxFileDialog(this,_T("Save ")+saveWhat+_T(" To:"), _T(""), _T("*.txt"), _T("*.txt"), wxFD_SAVE);
	if (saveFileDialog->ShowModal() == wxID_OK)
	{
		wxString fileName = saveFileDialog->GetPath();
		wxTextFile *txtFile = new wxTextFile;
        txtFile->Create(fileName);
        for (size_t i=0; i<txtLines.GetCount(); i++) txtFile->AddLine(txtLines[i]);
        txtFile->Write();
        txtFile->Close();
        wxMessageBox(saveWhat+_T(" saved successfully to ")+wxFileName(fileName).GetFullName(), _T("Save"), wxOK);
    }
}


void MainFrame::OnPrint(wxCommandEvent& event)
{
    wxPrintDialogData printDialogData;
    wxPrintPreview *preview = new wxPrintPreview(new PrintFrame, new PrintFrame, &printDialogData);
    if (!preview->Ok())
    {
        delete preview;
        wxMessageBox(_T("Maybe printer is not set correctly?"), _T("Printing"), wxOK);
        return;
    }
    wxPreviewFrame *frame = new wxPreviewFrame(preview, this, _T("Print Preview"), wxPoint(100, 100), wxSize(600, 650));
    frame->Centre(wxBOTH);
    frame->Initialize();
    frame->Show();
}


void MainFrame::OnSysPage(wxNotebookEvent& event)
{
    bool rb(true);
    if (Store.Wy*Store.Az==0) rb=false;

    switch (event.GetSelection())
    {
        case 0: rb=false; break;
        case 1: graph->SetType(1+(int)BtSt->GetValue()); break;
        case 2: rb=false; break;
    }

    BtFc->Show(rb);
    BtSt->Show(rb);
    if (rb)
    {
        //resize the window to show the buttons
        wxSize sz = this->GetClientSize();
        sz.SetHeight(sz.GetHeight()+1);
        this->SetClientSize(sz);
    }
}


void MainFrame::OnRadioButton(wxCommandEvent& event)
{
    int id = event.GetId();
    bool set(true);
    if (id==ID_BT_FC) set=false;
    BtFc->SetValue(!set);
    BtSt->SetValue(set);
    graph->SetType(1+(int)set);
}


void MainFrame::OnLoadPage(wxNotebookEvent& event)
{
    switch (event.GetSelection())
    {
        case 0: grid=udl_Grid; break;
        case 1: grid=pl_Grid; break;
        case 2: grid=pudl_Grid; break;
        case 3: grid=ml_Grid; break;
        case 4: grid=tz_Grid; break;
        case 5: grid=ptr_Grid; break;
    }
    grid->SetFocus();
}


void MainFrame::OnCellInputGrid(wxGridEvent& event)
{
    int row = event.GetRow();
    int col = event.GetCol();

    wxString val=grid->GetCellValue(row,col);

    //change span length in all grids
    if (row==0)
    {
        udl_Grid->SetCellValue(0, col, ChkValue(val));
        pl_Grid->SetCellValue(0, col, ChkValue(val));
        pudl_Grid->SetCellValue(0, col, ChkValue(val));
        ml_Grid->SetCellValue(0, col, ChkValue(val));
        tz_Grid->SetCellValue(0, col, ChkValue(val));
        ptr_Grid->SetCellValue(0, col, ChkValue(val));
    }
    else grid->SetCellValue(row, col, ChkValue(val));

    Solve();
}


//--------------- solve using cbeam_class ---------------
void MainFrame::Solve()
{
    //get input values
    vector< vector<double> > LM = ReadInput();

    //add self weight if chosen
    if (ChkSw->IsChecked())
    {
        system->SetSW(true);

        vector< vector<double> > g;
        for (size_t i=0; i<Store.L.size(); i++)
        {
            vector<double> ld;
            ld.push_back(double(i+1));  //nr span
            ld.push_back(1.0);          //load type = 1 (udl)
            ld.push_back(Store.g0);     //load value = g0
            g.push_back(ld);
        }
        //push it in front
        for (size_t i=0; i<LM.size(); i++) g.push_back(LM[i]);
        LM=g;
    }
    else
    {
        system->SetSW(false);
    }

    //construct beam object
    cBeam *bm = new cBeam();

    //set geometry
    vector<double> I;
    for (size_t i=0; i<Store.L.size(); i++) I.push_back(Store.I);

    TcRes->Clear();
    system->Clear();
    graph->Clear();


    if (bm->SetGeometry(Store.L, Store.E, I, Store.R))
    {
        //get geometry values checked by cbeam class
        vector< vector<double> > geometry = bm->GetGeometry();
        if (int(geometry.size())>3)
        {
            Store.L=geometry[0];
            Store.E=geometry[1][0];
            Store.I=geometry[2][0];
            Store.R=geometry[3];
        }

        if (bm->SetLoads(LM))
        {
            //enter checked loads into results
            Store.LMg=bm->GetLoadsG();
            Store.LMq=bm->GetLoadsQ();

            //solve the analysis problem
            bm->Solve();

            vector< vector <double> > results = bm->GetResults();

            system->SetSystem(Store.L, Store.R, Store.LMg, Store.LMq);
            graph->SetSystem(Store.L, Store.R, Store.LMg, Store.LMq);

            if (Store.E*Store.I!=1) graph->SetEI(true); else graph->SetEI(false);
            graph->SetSec(Store.Wy, Store.Az);
            graph->SetLF(false);
            graph->SetResults(results);


            wxArrayString chRes, dsRes;
            chRes = resOut(bm->GetMax(), bm->GetReaction());

            //if factors are given
            if (!Store.ldFac.IsEmpty())
            {
                vector <double> lf;
                wxString fStr(Store.ldFac);
                while (!fStr.IsEmpty())
                {
                    wxString vStr = fStr.BeforeFirst(_T(' '));
                    fStr=fStr.AfterFirst(_T(' '));
                    double val;
                    vStr.ToDouble(&val);
                    lf.push_back(val);
                }

                if (lf.size()>1) bm->SetLoadFactors(lf[0], lf[1]);
                if (lf.size()>3) bm->SetLoadFactors(lf[0], lf[1], lf[2], lf[3]);

                bm->Solve();

                dsRes = resOut(bm->GetMax(), bm->GetReaction());

                //change the result matrix for M/V graph
                for (size_t i=1; i<5; i++) results[i]=bm->GetResults()[i];
                graph->SetLF(true);
                graph->SetResults(results);
           }
           UpdateResultText(chRes, dsRes, strOut(bm->GetMax()));
        }


        for (size_t i=0; i<Store.resTxt.GetCount(); i++) TcRes->AppendText(Store.resTxt[i]+_T("\n"));
        TcRes->SetInsertionPoint(0);

        int nf = int(Store.L.size());
        int nl = int(Store.LMg.size()+Store.LMq.size());
        if (ChkSw->IsChecked()) nl-=nf;
        wxString sts; sts.Printf(_T("continuous beam %d spans %d loads"),nf, nl);
        if (nf==1) sts.Printf(_T("single-span beam %d loads"),nl);
        SetStatusText(sts);
    }
    else TcRes->AppendText(_T("problem could not be solved\ncheck geometry and/or loads\n"));

    delete bm;
}


vector< vector<double> > MainFrame::ReadInput()
{
    Store.L.clear();
    vector< vector<double> > LM;

    int n=grid->GetNumberCols();

    for (int i=0; i<n; i++)
    {
        //building the span length vector
        double d;
        wxString clStr;
        clStr=grid->GetCellValue(0, i);
        clStr.ToDouble(&d);
        Store.L.push_back(d);

        //building the load matrix for this span
        vector<double> ld;
        wxGrid *ldGrid(grid);

        int nl(0);
        for (int j=0; j<6; j++)
        {
            switch (j)
            {
                case 0: ldGrid=udl_Grid; nl=2; break;
                case 1: ldGrid=pl_Grid; nl=3; break;
                case 2: ldGrid=pudl_Grid; nl=4; break;
                case 3: ldGrid=ml_Grid; nl=3; break;
                case 4: ldGrid=tz_Grid; nl=4; break;
                case 5: ldGrid=ptr_Grid; nl=4; break;
            }

            int nrLoads = (ldGrid->GetNumberRows()-1)/nl;
            for (int k=0; k<nrLoads; k++)
            {
                ld.clear();
                ld.push_back(double(i+1));              //nr span
                ld.push_back(double(j+1));              //load type

                for (int r=1; r<=nl; r++)               //load values
                {
                    clStr=ldGrid->GetCellValue(k*nl+r, i);
                    clStr.ToDouble(&d);
                    ld.push_back(d);
                }
                LM.push_back(ld);

            }
        }
    }
    return LM;
}


void MainFrame::UpdateResultText(wxArrayString chRes, wxArrayString dsRes, wxString strRes)
{
    wxArrayString resTxt;
    wxString nStr, line;

    //add span numbers
    int nf = int(Store.L.size());
    nStr.Printf(_T("continuous beam %d spans:"),nf);
    if (nf==1) nStr.Printf(_T("single-span beam:"));
    resTxt.Add(nStr);

    //add span lengths
    line=_T("l:");
    for (int i=0; i<nf; i++)
    {
        nStr.Printf(_T("%4.2f"),Store.L[i]);
        line+=_T("\t")+nStr;
    }
    line+=_T("\t")+Store.lnUnit;
    resTxt.Add(line);

    //add Material/Section or EI
    if (!Store.Mat.IsEmpty()) resTxt.Add(_T("Mat:\t")+Store.Mat);
    else
    {
        line.Printf(_T("E:\t%1.2e"), Store.E);
        line+=(_T("\t")+Store.fcUnit+_T("/")+Store.lnUnit+_T("2"));
        if (Store.E!=1.0) resTxt.Add(line);
    }

    if (!Store.Sec.IsEmpty()) resTxt.Add(_T("Sec:\t")+Store.Sec);
    else
    {
        line.Printf(_T("I:\t%1.2e"), Store.I);
        line+=(_T("\t")+Store.lnUnit+_T("4"));
        if (Store.I!=1.0) resTxt.Add(line);
    }

    //add restraints, if they are no defaults
    bool chk(true);
    for (size_t i=0; i<Store.R.size(); i+=2)
    {
        if (Store.R[i]!=-1) chk=false;
        if (Store.R[i+1]!=0) chk=false;
    }

    if (!chk)
    {
        line=_T("defR:");
        for (size_t i=0; i<Store.R.size(); i+=2)
        {
            nStr.Printf(_T("%4.0f"), Store.R[i]);
            line+=_T("\t")+nStr;
        }
        resTxt.Add(line);
        line=_T("rotR:");
        for (size_t i=1; i<Store.R.size(); i+=2)
        {
            nStr.Printf(_T("%4.0f"), Store.R[i]);
            line+=_T("\t")+nStr;
        }
        resTxt.Add(line);
    }

    //add loads
    if (Store.LMg.size()>0)
    {
        resTxt.Add(_T(""));
        resTxt.Add(_T("permanent loads:"));
        for (size_t i=0; i<lmSort(Store.LMg).GetCount(); i++) resTxt.Add(lmSort(Store.LMg)[i]);
    }

    if (Store.LMq.size()>0)
    {
        resTxt.Add(_T(""));
        resTxt.Add(_T("live loads:"));
        for (size_t i=0; i<lmSort(Store.LMq).GetCount(); i++) resTxt.Add(lmSort(Store.LMq)[i]);
    }

    //create result output for characteristic loads
    if (!chRes.IsEmpty())
    {
        resTxt.Add(_T(""));
        resTxt.Add(_T("results - characteristic loads (1.0 1.0):"));
        for (size_t i=0; i<chRes.GetCount(); i++) resTxt.Add(chRes[i]);
    }

    //if factors are given
    if (!dsRes.IsEmpty())
    {
        resTxt.Add(_T(""));
        resTxt.Add(_T("results - design loads (")+Store.ldFac+_T("):"));
        for (size_t i=0; i<dsRes.GetCount(); i++)  resTxt.Add(dsRes[i]);
    }

    //if stresses are calculated
    while (!strRes.IsEmpty())
    {
        resTxt.Add(strRes.BeforeFirst(_T('\n')));
        strRes=strRes.AfterFirst(_T('\n'));
    }

    Store.resTxt=resTxt;

}


//--------------- sort loads for output ---------------
wxArrayString MainFrame::lmSort(vector< vector<double> > LM)
{
    wxString line, unit, valStr;
    wxArrayString output;

    //find number of loaded spans
    int nf=0;
    for (size_t 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> > ldlen(nf);

    //create output string for different load types
    for (int lt=1; lt<=6; lt++)
    {
        for (int i=0; i<nf; i++)
        {
            ldvalue[i].clear();
            ldstart[i].clear();
            ldlen[i].clear();
            for (int j=0; j<int(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]);
                    ldlen[i].push_back(LM[j][4]);
                }
        }

        //find max number of loads per type
        int max=0;
        for (int i=0; i<nf; i++) if (int(ldvalue[i].size())>max) max=int(ldvalue[i].size());

        for (int k=0; k<max; k++)
        {
            switch (lt)
            {
                case 1: line = _T("p:"); unit=Store.fcUnit+_T("/")+Store.lnUnit; break;     //load type 1 - udl
                case 2: line = _T("P:"); unit=Store.fcUnit; break;                          //load type 2 - point load
                case 3: line = _T("p\':"); unit=Store.fcUnit+_T("/")+Store.lnUnit; break;   //load type 3 - partial udl
                case 4: line = _T("M:";) unit=Store.fcUnit+Store.lnUnit; break;             //load type 4 - moment load
                case 5: line = _T("tz:";) unit=Store.fcUnit+_T("/")+Store.lnUnit; break;    //load type 5 - trapezoidal load
                case 6: line = _T("tr\':";) unit=Store.fcUnit+_T("/")+Store.lnUnit; break;  //load type 6 - partial triangular load
            }

            for (int i=0; i<nf; i++)
            {
                if (int(ldvalue[i].size())>k)
                {
                    valStr.Printf(_T("%4.2f"),ldvalue[i][k]);
                    line+=(_T("\t")+valStr);
                }
                else line+=(_T("\t"));
            }
            line+=_T("\t")+unit;
            output.Add(line);

            //add start point for load type 2-6
            if (lt>1)
            {
                line = _T(" a=");
                for (int i=0; i<nf; i++)
                {
                    if (int(ldstart[i].size())>k)
                    {
                        valStr.Printf(_T("%4.2f"),ldstart[i][k]);
                        line+=(_T("\t")+valStr);
                    }
                    else line+=(_T("\t"));
                }
                line+=_T("\t")+Store.lnUnit;
                output.Add(line);
            }

            //add len for load type 3, 5 and 6
            if (lt==3 || lt>=5)
            {
                line = _T(" l\'=");
                for (int i=0; i<nf; i++)
                {
                    if (int(ldlen[i].size())>k)
                    {
                        valStr.Printf(_T("%4.2f"),ldlen[i][k]);
                        line+=(_T("\t")+valStr);
                    }
                    else line+=(_T("\t"));
                }
                line+=_T("\t")+Store.lnUnit;
                output.Add(line);
            }
        }
    }

    return output;
}


//--------------- prepare max/min results for output ---------------
wxArrayString MainFrame::resOut(vector< vector<double> > maxValues, vector< vector <double> > reactions)
{
    wxString line, unit;
    wxArrayString output;

    //max/min results
    for (int j=1; j<int(maxValues.size()); j++)     //skip x (Mmax)
    {
        wxString prec(_T("%4.2f"));                 //set precision
        switch (j)
        {
            case 1: line = _T("Mmax:"); unit=Store.fcUnit+Store.lnUnit; break;
            case 2: line = _T("Mmin:"); unit=Store.fcUnit+Store.lnUnit; break;
            case 3: line = _T("Vmax:"); unit=Store.fcUnit; break;
            case 4: line = _T("Vmin:"); unit=Store.fcUnit; break;
            case 5: line = _T("dmax:"); unit=Store.lnUnit; prec=_T("%1.4f"); break;
            case 6: line = _T("dmin:"); unit=Store.lnUnit; prec=_T("%1.4f"); break;
        }

        if (Store.E*Store.I==1.0 && prec==_T("%1.4f"))
        {
            line=_T("EI")+line;
            prec=_T("%4.2f");
        }

        for (size_t i=0; i<maxValues[j].size(); i++)
        {
            wxString rStr;
            rStr.Printf(prec, maxValues[j][i]);
            line+=_T("\t")+rStr;
        }
        line+=_T("\t")+unit;
        output.Add(line);
    }

    //support reactions
    for (size_t j=0; j<reactions.size(); j++)
    {
        switch (j)
        {
            case 0: line = _T("Rmax:"); unit=Store.fcUnit; break;
            case 1: line = _T("Rmin:"); unit=Store.fcUnit; break;
        }
        for (size_t i=0; i<reactions[j].size(); i++)
        {
            wxString rStr; rStr.Printf(_T("\t%4.2f"),reactions[j][i]);
            line+=rStr;
        }
        line+=_T("\t")+unit;
        output.Add(line);
    }

    return output;
}


//--------------- calculate stresses for output ---------------
wxString MainFrame::strOut(vector< vector<double> > maxValues)
{
    wxString line;
    size_t n = maxValues.size();

    //Mmax, Mmin -> Sigma
    if (Store.Wy>0 && n>2)
    {
        line+= _T("\nSigma:");
        size_t m = maxValues[1].size();
        if (maxValues[2].size()<m) m = maxValues[2].size();

        for (size_t i=0; i<m; i++)
        {
            double v = maxValues[1][i];
            if (-maxValues[2][i]>v) v=-maxValues[2][i];
            v/=Store.Wy;

            wxString rStr;
            if (v>1000) rStr.Printf(_T("%4.1fk"),v/1000);
            else rStr.Printf(_T("%4.1f"),v);
            line+=_T("\t")+rStr;
        }
        line+=(_T("\t")+Store.fcUnit+_T("/")+Store.lnUnit+_T("2"));
    }

    //Vmax, Vmin -> Tau
    if (Store.Az>0 && n>4)
    {
        line+= _T("\nTau:");
        size_t m = maxValues[3].size();
        if (maxValues[4].size()<m) m = maxValues[4].size();

        for (size_t i=0; i<m; i++)
        {
            double v = maxValues[3][i];
            if (-maxValues[4][i]>v) v=-maxValues[4][i];
            v/=Store.Az;

            wxString rStr;
            if (v>1000) rStr.Printf(_T("%4.1fk"),v/1000);
            else rStr.Printf(_T("%4.1f"),v);
            line+=_T("\t")+rStr;
        }
        line+=(_T("\t")+Store.fcUnit+_T("/")+Store.lnUnit+_T("2"));
    }
    return line;
}


//--------------- create results along the beam ---------------
wxArrayString MainFrame::CreatePlotTable()
{
    wxString line, valStr;
    wxArrayString output;

    //design values if factors are given
    if (!Store.ldFac.IsEmpty()) line=(_T("#x\tMdmax\tMdmin\tVdmax\tVdmin\tdmax\tdmin"));
    else line=(_T("#x\tMmax\tMmin\tVmax\tVmin\tdmax\tdmin"));
    output.Add(line);

    int nf=int(Store.L.size());
    vector< vector<double> > results = graph->GetResults();

    if (results.size()>6)
    {
        for (int i=0;i<nf*101;i++)
        {
            line.Empty();
            for (int j=0; j<7; j++)
            {
                wxString val;
                val.Printf(_T("%4.3f"),results[j][i]);
                line+=val+_T("\t");
            }
            output.Add(line);
        }
    }
    return output;
}


//--------------- grid functions ---------------
void MainFrame::FillInputGrids()
{
    int nf = int(Store.L.size());
    for (int j=0; j<6; j++)
    {
        int rows(0);
        switch (j)
        {
            case 0: grid=udl_Grid; rows=2; break;
            case 1: grid=pl_Grid; rows=3; break;
            case 2: grid=pudl_Grid; rows=4; break;
            case 3: grid=ml_Grid; rows=3; break;
            case 4: grid=tz_Grid; rows=4; break;
            case 5: grid=ptr_Grid; rows=4; break;
        }
        grid->DeleteCols(0, grid->GetNumberCols());
        if (grid->GetNumberRows()>rows+1) grid->DeleteRows(rows+1, grid->GetNumberRows());
        grid->ClearGrid();

        for (int i=0; i<nf; i++)
        {
            //fill in span lengths
            addCols(grid,1);
            wxString lStr;
            lStr.Printf(_T("%4.2f"), Store.L[i]);
            grid->SetCellValue(0, i, lStr);

            //build a matrix for loads on this span
            vector< vector<double> > spanLd;
            for (int lc=0; lc<2; lc++)
            {
                vector< vector<double> > LM;
                switch (lc)
                {
                    case 0: LM=Store.LMg;  break;
                    case 1: LM=Store.LMq;  break;
                }

                for (int k=0; k<int(LM.size()); k++)
                {
                    if (int(LM[k][0])==i+1 && int(LM[k][1])==j+1)
                    {
                        double ld = LM[k][2];
                        double a = LM[k][3];
                        double b = LM[k][4];

                        //check if there is a place
                        bool pl(false);
                        for (int l=0; l<int(spanLd.size()); l++)
                        {
                            if (spanLd[l][0+lc]==0 && spanLd[l][2]==a && spanLd[l][3]==b)
                            {
                                spanLd[l][0+lc]=ld;
                                pl=true;
                                break;
                            }
                        }
                        if (!pl)
                        {
                            vector<double> sl(4,0);
                            sl[0+lc]=ld;
                            sl[2]=a;
                            sl[3]=b;
                            spanLd.push_back(sl);
                        }
                    }
                }
            }

            //fill in loads
            int row(1);
            for (int k=row; k<grid->GetNumberRows(); k++) grid->SetCellValue(k, i, _T(""));
            for (int k=0; k<int(spanLd.size()); k++)
            {
                wxString g; g.Printf(_T("%4.2f"), spanLd[k][0]);
                wxString q; q.Printf(_T("%4.2f"), spanLd[k][1]);
                wxString a; a.Printf(_T("%4.2f"), spanLd[k][2]);
                wxString b; b.Printf(_T("%4.2f"), spanLd[k][3]);

                double d;
                wxString clStr=grid->GetCellValue(row,i)+grid->GetCellValue(row+1,i);
                clStr.ToDouble(&d);
                if (d!=0)
                {
                    addRows(rows);
                    row+=rows;
                }
                grid->SetCellValue(row, i, g);
                grid->SetCellValue(row+1, i, q);
                if (rows>2) grid->SetCellValue(row+2, i, a);
                if (rows>3) grid->SetCellValue(row+3, i, b);
            }
        }
    }
    grid=udl_Grid;
    grid->MovePageUp();
    grid->SetGridCursor(0,0);
    NbInp->SetSelection(0);
}


void MainFrame::addCols(wxGrid *Grid, int n)
{
    int oldNr = Grid->GetNumberCols();
    int newNr = oldNr+n;

    if (newNr > oldNr)        //extend
    {
        for (int i=oldNr+1; i<=newNr; i++)
        {
            wxString nrStr;
            nrStr.Printf(_T("%d"),i);

            Grid->AppendCols(1);
            Grid->SetColLabelValue(i-1, nrStr);
            Grid->SetCellBackgroundColour(0, i-1, wxColour(255,255,150,255));


            if (i>1)        // clone previous
            {
                for (int j=0; j<=Grid->GetNumberRows(); j++)
                {
                    Grid->SetCellValue(j, i-1, Grid->GetCellValue(j, i-2));
                    Grid->SetCellBackgroundColour(j, i-1, Grid->GetCellBackgroundColour(j, i-2));
                }
            }
       }
    }

    if (newNr < oldNr)        //implode
    {
        if (newNr>0)
            for (int i=oldNr-1; i>=newNr; i--)
                Grid->DeleteCols(i);
    }

    Grid->SetGridCursor(0, newNr-1);
    Grid->SetFocus();
}


void MainFrame::addRows(int n)
{
    int oldNr = grid->GetNumberRows();
    int newNr = oldNr+n;


     wxColour bg_colour(250,225,200,255);
    if (grid->GetCellBackgroundColour(oldNr-1, 0)==bg_colour) bg_colour=wxColour(*wxWHITE);

    if (newNr > oldNr)        //extend
    {
        //it would make no sense to add too many empty rows
        wxString clStr;
        for (int i=oldNr-n; i<oldNr; i++)
            for (int j=0; j<grid->GetNumberCols(); j++) clStr+=grid->GetCellValue(i, j);

        if (!clStr.IsEmpty())
        {
            for (int i=oldNr; i<newNr; i++)
            {
                wxString label;
                if (oldNr>n) label=grid->GetRowLabelValue(i-n);
                grid->AppendRows(1);
                grid->SetRowLabelValue(i, label);
                for (int j=0; j<grid->GetNumberCols(); j++) grid->SetCellBackgroundColour(i, j, bg_colour);
            }
            grid->MovePageDown();
            grid->SetGridCursor(oldNr, 0);
        }
    }

    if (newNr < oldNr)        //implode
    {
        if (newNr>1)
        {
            for (int i=oldNr-1; i>=newNr; i--) grid->DeleteRows(i);
            grid->MovePageUp();
            grid->SetGridCursor(newNr-1, 0);
            Solve();
        }
    }
    grid->SetFocus();

}


wxString MainFrame::ChkValue(wxString valStr)
{
    wxString chkStr(valStr);

    //entered commas will be changed to points
    if (chkStr.Find(_T(",")) > -1) chkStr.Replace( _T(","),_T(".") );

    double d;
    chkStr.ToDouble(&d);
    chkStr.Empty();
    chkStr.Printf(_T("%4.2f"),d);
    return chkStr;
}


int MainFrame::Draw(wxDC& dc, wxRect bounds, int type)
{
    //this is for printout
    int ratio(1);
    switch (type)
    {
        case 0 : ratio = graph->DrawHead(dc, bounds, Store.Head); break;
        case 1 : ratio = system->DrawSystem(dc, bounds, 0); break;
        case 2 : ratio = graph->DrawResultText(dc, bounds, Store.resTxt); break;
        case 3 : graph->DrawResults(dc, bounds, 0); break;
        case 4 : graph->DrawResults(dc, bounds, 1); break;
        case 5 : graph->DrawResults(dc, bounds, 2); break;
    }
    return ratio;
}


//---------------- PrintFrame -------------------------
void PrintFrame::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
{
    *minPage = 1;
    *maxPage = 2;
    *selPageFrom = 1;
    *selPageTo = 2;
}


bool PrintFrame::HasPage(int pageNum)
{
    return (pageNum > 0 && pageNum <= 2);
}


bool PrintFrame::OnBeginDocument(int startPage, int endPage)
{
    if (!wxPrintout::OnBeginDocument(startPage, endPage)) return false;
    else return true;
}


bool PrintFrame::OnPrintPage(int page)
{
    wxDC& dc = *GetDC();

    //scale margins
    wxPageSetupDialogData *prt_setup = new wxPageSetupDialogData();
    wxRect bounds = GetLogicalPageMarginsRect(*prt_setup);

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

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

    int ratio;
    wxRect area(bounds);
    int whtspc = int(bounds.height/20);

    if (page==1)
    {
        //draw head
        area.height=bounds.height/10;
        ratio = frame->Draw(dc, area, 0);
        area.y+=int(area.height*ratio/100)+whtspc/2;

        //draw system in the upper third
        area.height=bounds.height/3;
        ratio = frame->Draw(dc, area, 1);
        area.y+=int(area.height*ratio/100)+whtspc;

        //draw text
        area.height=bounds.height-area.y;
        ratio = frame->Draw(dc, area, 2);
        area.y+=int(area.height*ratio/100)+whtspc;

        //see what is left
        area.height=bounds.height-area.y;

        if (area.height>0)
        {
            //draw some results if they fit
            resNo = area.height/(bounds.height/5);
            for (int i=0; i<resNo; i++)
            {
                area.height=bounds.height/5;
                frame->Draw(dc, area, i+3);
                area.y+=area.height;
            }
        }
        else resNo=0;

    }

    if (page==2)
    {
        //draw head
        area.height=bounds.height/10;
        ratio = frame->Draw(dc, area, 0);
        area.y+=int(area.height*ratio/100+whtspc/2);

        //draw the rest of results
        area.height=bounds.height-area.y;
        for (int i=resNo; i<3; i++)
        {
            area.height=bounds.height/5;
            frame->Draw(dc, area, i+3);
            area.y+=area.height;
        }
    }

    return true;
}





