#include "MainFrame.h"
#include "BobDudeApp.h"
#include "xhex.h"
#include <wx/config.h>
#include <wx/msgdlg.h>
#include <wx/icon.h>
#include <wx/filename.h>
#include <wx/file.h>
//#include <wx/artprov.h>
#include <wx/aboutdlg.h>
#include <boost/lexical_cast.hpp>
#include <time.h>
#if HAVE_USLEEP
#include <unistd.h>
#endif

#ifdef AVRDUDE_CONF
#include "avrdude.h"
#include "programmer.h"
#include "part.h"
#include "update.h"
#else
#include "dude.h"
#endif

//(*InternalHeaders(MainFrame)
#include <wx/settings.h>
#include <wx/intl.h>
#include <wx/string.h>
//*)

//(*IdInit(MainFrame)
const long MainFrame::ID_HTMLWINDOWFILE = wxNewId();
const long MainFrame::ID_HTMLWINDOWPLATFORM = wxNewId();
const long MainFrame::ID_HTMLWINDOWPROGRAM = wxNewId();
const long MainFrame::ID_HTMLWINDOWLOG = wxNewId();
const long MainFrame::ID_GAUGE_PROGRESS = wxNewId();
const long MainFrame::ID_MENUITEM_OPEN = wxNewId();
const long MainFrame::ID_MENUITEM_RESET_MCU = wxNewId();
//*)

wxGauge* progressBar;
std::string infoPrefix;
extern MainFrame * frame;

void doAvrdudeProgress(int percent, double etime) {
    if (percent<0) percent=0;
    if (percent>100) percent=100;
    if (frame) {
        progressBar->SetValue(percent);
        std::string text = infoPrefix;
        text += " ";
        text += boost::lexical_cast<std::string>(percent);
        text += "% done";
    //  infoText->SetLabel(text.c_str());
    //  infoText->Update();
        progressBar->Update();
        wxGetApp().Yield(true);
    }
}


void doAvrdudeMessage(int type, const char * msg, bool replace) {
    if (frame) {
        if (type>1){
            return;
        }
        wxString text(msg);
        if (text.Trim().IsEmpty()) {
            return;
        }
        if (text.Contains("error")) {
            text = wxString::Format("[%i]: <font color='#ee0000'>%s</font>", type, text);
        } else {
            text = wxString::Format("[%i]: <font color='#666666'>%s</font>", type, text);
        }
        frame->appendLogLine(text, replace);
    }
}

// new callback function:
void doProgress(unsigned int progress, unsigned int progressFull, const std::string & info, bool replace) {
    if (progressFull>0) {
        doAvrdudeProgress((100*progress)/progressFull, 0);
    }
    if (info!="") {
        doAvrdudeMessage(0, info.c_str(), replace);
    }
}


BEGIN_EVENT_TABLE(MainFrame,wxFrame)
    //(*EventTable(MainFrame)
    //*)
END_EVENT_TABLE()

MainFrame::MainFrame(wxWindow* parent,wxWindowID id) {
    isProgramming = false;
    xhexfile = 0;
    wxConfig *config = new wxConfig("BobDude", wxEmptyString, wxEmptyString, wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
    wxString strValue = "";
    config->Read("lastFile", &strValue);
    delete config;

    progbob_armswd = new ProgBob_ARMSWD();
    
    //(*Initialize(MainFrame)
    wxBoxSizer* BoxSizer4;
    wxBoxSizer* BoxSizer2;
    wxBoxSizer* BoxSizer1;

    Create(parent, wxID_ANY, _("BobDude"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE|wxSYSTEM_MENU|wxRESIZE_BORDER|wxCLOSE_BOX, _T("wxID_ANY"));
    SetClientSize(wxSize(444,466));
    SetMinSize(wxSize(450,340));
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    BoxSizer1 = new wxBoxSizer(wxVERTICAL);
    BoxSizer2 = new wxBoxSizer(wxHORIZONTAL);
    HtmlWindowFile = new HTMLButton(this, ID_HTMLWINDOWFILE, wxDefaultPosition, wxSize(211,122), wxHW_SCROLLBAR_NEVER|wxHW_NO_SELECTION, _T("ID_HTMLWINDOWFILE"));
    HtmlWindowFile->SetPage(_("<body bgcolor=\"#039cd6\" text=\"#ffffff\">\n<font size=\"2\">\n<table cellspacing=\"0\"  cellpadding=\"0\">\n<tr><td width=\"60\">File:</td><td><b>test1.xhex</b></td></tr>\n<tr><td>Flash:</td><td>12345 bytes</td></tr>\n<tr><td>EEPROM:</td><td>none</td></tr>\n<tr><td>Fuse-Bits:</td><td>HFUSE, LFUSE</td></tr>\n</table>\n</font>"));
    HtmlWindowFile->SetMinSize(wxSize(-1,122));
    HtmlWindowFile->SetMaxSize(wxSize(-1,122));
    BoxSizer2->Add(HtmlWindowFile, 1, wxALL|wxEXPAND|wxFIXED_MINSIZE|wxALIGN_LEFT|wxALIGN_TOP, 5);
    HtmlWindowPlatform = new HTMLButton(this, ID_HTMLWINDOWPLATFORM, wxDefaultPosition, wxSize(212,122), wxHW_SCROLLBAR_NEVER|wxHW_NO_SELECTION, _T("ID_HTMLWINDOWPLATFORM"));
    HtmlWindowPlatform->SetPage(_("<body bgcolor=\"#f2af13\" text=\"#ffffff\">\n<font size=\"2\">\n<table cellspacing=\"0\"  cellpadding=\"0\">\n<tr><td width=\"80\">Platform:</td><td><b>niboburger-m16-15</b></td></tr>\n<tr><td>MCU:</td><td>ATmega16A</td></tr>\n<tr><td>Bitclock:</td><td>default</td></tr>\n<tr><td>Programmer:</td><td>usbasp</td></tr>\n<tr><td>Interface:</td><td>usb</td></tr>\n<tr><td>Baudrate:</td><td>none</td></tr>\n</table>\n</font>"));
    HtmlWindowPlatform->SetMinSize(wxSize(-1,122));
    HtmlWindowPlatform->SetMaxSize(wxSize(-1,122));
    BoxSizer2->Add(HtmlWindowPlatform, 1, wxALL|wxEXPAND|wxFIXED_MINSIZE|wxALIGN_LEFT|wxALIGN_TOP, 5);
    BoxSizer1->Add(BoxSizer2, 0, wxEXPAND|wxFIXED_MINSIZE|wxALIGN_LEFT|wxALIGN_TOP, 5);
    BoxSizer4 = new wxBoxSizer(wxVERTICAL);
    HtmlWindowProgram = new HTMLButton(this, ID_HTMLWINDOWPROGRAM, wxDefaultPosition, wxSize(433,41), wxHW_SCROLLBAR_NEVER, _T("ID_HTMLWINDOWPROGRAM"));
    HtmlWindowProgram->SetPage(_("<body bgcolor=\"#4b9a91\" text=\"#ffffff\">\n<div align=\"center\"><b>Program!</b></div>"));
    BoxSizer4->Add(HtmlWindowProgram, 0, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP, 5);
    HtmlWindowLog = new wxHtmlWindow(this, ID_HTMLWINDOWLOG, wxDefaultPosition, wxSize(433,146), wxHW_SCROLLBAR_AUTO, _T("ID_HTMLWINDOWLOG"));
    HtmlWindowLog->SetPage(_("<html><body>\n<font size=\"2\">\n"));
    BoxSizer4->Add(HtmlWindowLog, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP, 5);
    BoxSizer1->Add(BoxSizer4, 1, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP, 0);
    GaugeProgress = new wxGauge(this, ID_GAUGE_PROGRESS, 100, wxDefaultPosition, wxSize(374,16), wxGA_SMOOTH|wxNO_BORDER, wxDefaultValidator, _T("ID_GAUGE_PROGRESS"));
    BoxSizer1->Add(GaugeProgress, 0, wxALL|wxEXPAND|wxALIGN_LEFT|wxALIGN_TOP, 5);
    SetSizer(BoxSizer1);
    MenuBar1 = new wxMenuBar();
    MenuFile = new wxMenu();
    MenuItemOpen = new wxMenuItem(MenuFile, ID_MENUITEM_OPEN, _("Open ..."), wxEmptyString, wxITEM_NORMAL);
    MenuFile->Append(MenuItemOpen);
    MenuItemExit = new wxMenuItem(MenuFile, wxID_EXIT, _("Exit"), wxEmptyString, wxITEM_NORMAL);
    MenuFile->Append(MenuItemExit);
    MenuBar1->Append(MenuFile, _("File"));
    Menu1 = new wxMenu();
    MenuItem1 = new wxMenuItem(Menu1, ID_MENUITEM_RESET_MCU, _("Reset MCU"), wxEmptyString, wxITEM_NORMAL);
    Menu1->Append(MenuItem1);
    MenuBar1->Append(Menu1, _("Tools"));
    MenuHelp = new wxMenu();
    MenuItem5 = new wxMenuItem(MenuHelp, wxID_ABOUT, _("About..."), wxEmptyString, wxITEM_NORMAL);
    MenuHelp->Append(MenuItem5);
    MenuBar1->Append(MenuHelp, _("Help"));
    SetMenuBar(MenuBar1);
    FileDialog1 = new wxFileDialog(this, _("Select file"), wxEmptyString, wxEmptyString, _("All supported files|*.bob3;*.xhex;*.hex;*.srec|BOB3 files (*.bob3)|*.bob3|XHex files (*.xhex)|*.xhex|Intel HEX files (*.hex)|*.hex|Motorla S-record file (*.srec)|*.srec"), wxFD_DEFAULT_STYLE, wxDefaultPosition, wxDefaultSize, _T("wxFileDialog"));
    SetSizer(BoxSizer1);
    Layout();
    Center();

    Connect(ID_MENUITEM_OPEN,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnMenuItemOpenSelected);
    Connect(wxID_EXIT,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnMenuItemExitSelected);
    Connect(ID_MENUITEM_RESET_MCU,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnMenuItemResetMCU);
    Connect(wxID_ABOUT,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&MainFrame::OnMenuItemAboutSelected);
    //*)

    HtmlWindowFile->setHTML((*(HtmlWindowFile->GetParser()->GetSource())).AfterFirst('\n'));
    HtmlWindowPlatform->setHTML((*(HtmlWindowPlatform->GetParser()->GetSource())).AfterFirst('\n'));
    HtmlWindowProgram->setHTML((*(HtmlWindowProgram->GetParser()->GetSource())).AfterFirst('\n'));

    platformDialog = new PlatformDialog(this);

    //SetBackgroundColour(wxColour(80,80,80));

    //MenuItem5->SetBitmap(wxArtProvider::GetBitmap(wxART_HELP));
    //MenuItemOpen->SetBitmap(wxArtProvider::GetBitmap(wxART_FILE_OPEN));

    FileDialog1->SetPath(strValue);

    SetTitle(PACKAGE_STRING);
    #if defined(__WINDOWS__)
    SetIcon(wxIcon(_("aaaa"), wxBITMAP_TYPE_ICO_RESOURCE ));
    #endif
    progressBar = GaugeProgress;

    /*
    "HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM"


    ComboBoxPort->Append(_("COM1"));
    ComboBoxPort->Append(_("COM2"));
    ComboBoxPort->Append(_("COM3"));
    ComboBoxPort->Append(_("COM4"));
    ComboBoxPort->Append(_("COM5"));
    ComboBoxPort->Append(_("COM6"));
    ComboBoxPort->Append(_("COM7"));
    ComboBoxPort->Append(_("COM8"));
    */

#ifdef AVRDUDE_CONF
    platformDialog->setProgrammers(Programmer::getAvailableProgrammers());
    platformDialog->setParts(Part::getAvailableParts());
#else
    dude_initialize();
    platformDialog->setProgrammers(dude_programmers);
    platformDialog->setParts(dude_parts);
#endif
    
    updateGUI();
    appendLogLine("Using configuration " + wxGetApp().conf + "\n");


    /**/
    HtmlWindowFile->setStyleHTML("default", "<body bgcolor=\"#039cd6\" text=\"#ffffff\">");
    HtmlWindowFile->setStyleHTML("hover", "<body bgcolor=\"#05ace6\" text=\"#ffffff\">");
    HtmlWindowFile->setStyleHTML("active", "<body bgcolor=\"#039cd6\" text=\"#cccccc\">");
    HtmlWindowFile->Bind(wxEVT_BUTTON, &MainFrame::OnMenuItemOpenSelected, this);
    //HtmlWindowFile->Bind(wxEVT_BUTTON, &MainFrame::OnMenuItemOpenSelected, this);

    HtmlWindowPlatform->setStyleHTML("default", "<body bgcolor=\"#f2af13\" text=\"#ffffff\">");
    HtmlWindowPlatform->setStyleHTML("hover", "<body bgcolor=\"#ffbf23\" text=\"#ffffff\">");
    HtmlWindowPlatform->setStyleHTML("active", "<body bgcolor=\"#f2af13\" text=\"#cccccc\">");
    HtmlWindowPlatform->Bind(wxEVT_BUTTON, &MainFrame::OnEditPlatform, this);

    HtmlWindowProgram->setStyleHTML("default", "<body bgcolor=\"#4b9a91\" text=\"#ffffff\">");
    HtmlWindowProgram->setStyleHTML("hover", "<body bgcolor=\"#5baaa1\" text=\"#ffffff\">");
    HtmlWindowProgram->setStyleHTML("active", "<body bgcolor=\"#4b9a91\" text=\"#cccccc\">");
    HtmlWindowProgram->setStyleHTML("disabled", "<body bgcolor=\"#4b9a91\" text=\"#aaaaaa\">");
    HtmlWindowProgram->Bind(wxEVT_BUTTON, &MainFrame::OnButtonProgramClick, this);

    platformDialog->initPlatform();
    HtmlWindowPlatform->setHTML(platformDialog->getHTML());
    HtmlWindowPlatform->activateStyle("default");

    HtmlWindowFile->setHTML(getFileHTML());
    HtmlWindowFile->activateStyle("default");

    for (unsigned int i=0; i<wxGetApp().files.size(); i++) {
        loadFile(wxGetApp().files[i]);
    }
}


MainFrame::~MainFrame() {
    delete xhexfile;
    delete progbob_armswd;
    //(*Destroy(MainFrame)
    //*)
}

wxString MainFrame::getFileHTML() {
    if (m_filename=="") {
        return "<div align='center'><b>no file loaded!</b></div>";
    } else {
        wxString res = "<font size=\"2\"><table cellspacing=\"0\"  cellpadding=\"0\">";
        res += "<tr><td width=\"60\">File:</td><td><b>"+m_filename+"</b></td></tr>";
        if ((xhexfile) && (xhexfile->devicePtr)) {
            segment * s = xhexfile->devicePtr->getSegment("flash");
            if (s) {
                res += wxString::Format("<tr><td>Flash:</td><td>%i bytes</td></tr>", s->byteSize);
            } else {
                res += "<tr><td>Flash:</td><td><em>none</em></td></tr>";
            }
            s = xhexfile->devicePtr->getSegment("eeprom");
            if (s) {
                res += wxString::Format("<tr><td>EEPROM:</td><td>%i bytes</td></tr>", s->byteSize);
            } else {
                res += "<tr><td>EEPROM:</td><td><em>none</em></td></tr>";
            }
            wxString fuses = "";
            if (xhexfile->devicePtr->getSegment("efuse")) {
                fuses += "EFUSE";
            }
            if (xhexfile->devicePtr->getSegment("hfuse")) {
                fuses += fuses.empty()?"HFUSE":", HFUSE";
            }
            if (xhexfile->devicePtr->getSegment("lfuse")) {
                fuses += fuses.empty()?"LFUSE":", LFUSE";
            }
            if (fuses.empty()) {
                fuses = "<em>none</em>";
            }
            res += "<tr><td>Fuse-Bits:</td><td>"+fuses+"</td></tr>";
        }

        res += "</table></font>";
        return res;
    }
}

void MainFrame::OnEditPlatform(wxCommandEvent& event) {
    if ( platformDialog->ShowModal() == wxID_OK ) {
      HtmlWindowPlatform->setHTML(platformDialog->getHTML());
    }
}

void MainFrame::OnButtonProgramClick(wxCommandEvent& event) {
    if (isProgramming) {
        return;
    } else {
        isProgramming = true;
        doStartProgram();
        isProgramming = false;
    }
}
    

void MainFrame::startProgram() {
    HtmlWindowProgram->simClick();
    //doStartProgram();
    //HtmlWindowProgram->Command(new wxCommandEvent(wxEVT_COMMAND_BUTTON_CLICKED));
    //wxCommandEvent* evt = new wxCommandEvent;
    //evt->SetString("startProgram");
    //QueueEvent(evt);
}

void MainFrame::program_AVRISP(const wxString & programmerPort, const wxString & devicePart, bool resetOnly) {
    int step=0;
    ProgrammerBase * progbob = new ProgBob_AVRISP(&doProgress);
    try {
        bool erase = (xhexfile) && (xhexfile->devicePtr) && (xhexfile->devicePtr->erase);
        
        step = 1;
        progbob->connect(static_cast<const char*>(programmerPort));
        step = 2;
        progbob->configure(static_cast<const char*>(devicePart));
        //std::cout << "connected 1" << std::endl;
        usleep(100000);  //100ms
        
        step = 3;
        progbob->startProgramMode("avrisp", false);
        progbob->checkSignature();
        if (!resetOnly) {
            if (erase) progbob->erase();
            if (xhexfile) {
                if (xhexfile->devicePtr) {
                    step = 4;
                    for (unsigned int i=0; i<xhexfile->devicePtr->segmentVec.size(); i++) {
                        segment & seg (xhexfile->devicePtr->segmentVec[i]);
                        if (seg.id=="flash") {
                            unsigned int memend = seg.baseAddress+seg.memory.size();
                            if (memend>0x0003ffff) throw std::runtime_error("Flash overflow!");
                            progbob->program("flash", seg.baseAddress, seg.memory);
                            progbob->verify("flash", seg.baseAddress, seg.memory);
                        }
                    }
                    step = 5;
                }
            }
            progbob->leaveProgramMode();
            appendLogLine("Programming finished <font color='#00aa00'><b>successfully</b></font>.");
        } else {
            progbob->leaveProgramMode();
            appendLogLine("Reset finished <font color='#00aa00'><b>successfully</b></font>.");
        }
        
    } catch (const std::exception & e) {
        if (step>=3) progbob->leaveProgramMode();
        progbob->signalError();
        wxString m = e.what();
        if ((step==2) && (m=="Protocol rx error: no head")) {
            m = "No programmer detected!";
        }
        if ((step==3) && (m=="error: cmd_enterProgmodeIsp failed")) {
            m = "No microcontroller detected!";
        }
        appendLogError("<b>Program error:</b> "+m);
        wxMessageBox(_(m), _("Error"));
    }
    // finally disconnect...
    progbob->connect("");
    delete progbob;
}

void MainFrame::program_ARMSWD(const wxString & programmerPort, const wxString & devicePart, bool resetOnly) {
    if (devicePart=="efm32") {
        // program efm32
        try {
            bool erase = (xhexfile) && (xhexfile->devicePtr) && (xhexfile->devicePtr->erase);
            
            progbob_armswd->connect(static_cast<const char*>(programmerPort));
            //std::cout << "connected 1" << std::endl;
            usleep(100000);  //100ms
            std::string status = progbob_armswd->getStatus();
            status = progbob_armswd->getStatus();
            if (status=="") throw std::runtime_error("Unable to open port!");
            if (status=="NONE") throw std::runtime_error("No device connected to programmer!");
            if (status!="BOB3-907") throw std::runtime_error("Wrong BOB type!");
            //std::cout << "connected 2" << std::endl;
            if (!resetOnly) {
                progbob_armswd->startProgramMode("armsdw", erase);
                appendLogLine("Programming started.");
                if (xhexfile) {
                    if (xhexfile->devicePtr) {
                        for (unsigned int i=0; i<xhexfile->devicePtr->segmentVec.size(); i++) {
                            segment & seg (xhexfile->devicePtr->segmentVec[i]);
                            if (seg.id=="flash") {
                                unsigned int memend = seg.baseAddress+seg.memory.size();
                                if (memend>0x0003ffff) throw std::runtime_error("Flash overflow!");
                                progbob_armswd->program("flash", seg.baseAddress, seg.memory);
                            }
                        }
                    }
                }
                progbob_armswd->reset();
                appendLogLine("Programming finished <font color='#00aa00'><b>successfully</b></font>.");
            } else {
                progbob_armswd->reset();
                appendLogLine("Reset finished <font color='#00aa00'><b>successfully</b></font>.");
            }
            
        } catch (const std::exception & e) {
            progbob_armswd->signalError();
            wxString m = e.what();
            appendLogError("<b>Program error:</b> "+m);
            wxMessageBox(_(m), _("Error"));
        }
        // finally disconnect...
        progbob_armswd->connect("");
        
    } else {
        wxMessageBox(_("Part "+devicePart+" not supported"), _("Error"));
    }
}

#ifdef AVRDUDE_CONF
void MainFrame::program_AVRDUDE(const wxString & programmerPort, const wxString & devicePart, bool resetOnly) {
    // program with avrdude
    Programmer * prg =  wxGetApp().avrdude->locateProgrammer(static_cast<const char*>(programmerType));
    if (prg==0) {
        wxMessageBox(_("Programmer "+programmerType+" not supported"), _("Error"));
    }

    Part * part = wxGetApp().avrdude->locatePart(static_cast<const char*>(devicePart));
    if (part==0) {
        wxMessageBox(_("Part "+devicePart+" not supported"), _("Error"));
    }

    if (prg && part && xhexfile) {
        bool canceled = false;
        Part * writePart = part->clone();
        Part * verifyPart = part->clone();
        prg->initpgm();
        prg->setup();

        if ((programmerBaudrate!="") && (programmerBaudrate!="default") && (programmerBaudrate!="none")) {
            try {
                unsigned int baudrate = boost::lexical_cast<unsigned int>(programmerBaudrate);
                if (baudrate>0) {
                    prg->setBaudrate(baudrate);
                }
            } catch (...) {
                wxMessageBox(_("Baudrate must be a numeric value!"), _("Error"));
                delete writePart;
                delete verifyPart;
                delete part;
                delete prg;
                return;
            }
        }

        if ((programmerBitclock!="") && (programmerBitclock!="default")) {
            try {
                unsigned int bitclock = boost::lexical_cast<unsigned int>(programmerBitclock);
                if (bitclock>0) {
                //prg->setBaudrate(bitclock);
                }
            } catch (...) {
                wxMessageBox(_("Bitclock must be a numeric value!"), _("Error"));
                delete writePart;
                delete verifyPart;
                delete part;
                delete prg;
                return;
            }
        }

        if (prg->open(static_cast<const char*>(programmerPort))) {
            
#if (LIBAVRDUDE_VER >= 70)
            prg->enable(*writePart);
#else
            prg->enable();
#endif
            prg->initialize(*writePart);

            std::string psig = writePart->get_signature();
            std::string sig = prg->read_signature(*writePart);
            success=true;

            appendLogLine("Connected device signature is 0x"+sig+"\n");

            if ((sig=="000000")||(sig=="ffffff")) {
                appendLogError("No microcontroller detected!");
                wxString msg = "No microcontroller detected\n\nIgnore and continue programming?";
                int res=wxMessageBox(msg, _("Error"),wxYES_NO+wxICON_EXCLAMATION);
                if (res==wxNO) {
                    canceled = true;
                } else {
                    appendLogLine("<b>Ignoring problem...</b>");
                    success = false;
                }
            } else if (psig!=sig) {
                appendLogError("Device signature does not match "+devicePart+" signature (0x"+psig+")\n");
                wxString msg = "The connected microcontroller is not the selected one!\n\nconnected device signature: 0x"+sig+"\n"+devicePart+" signature: 0x"+psig+"\n\nContinue programming?";
                int res=wxMessageBox(msg, _("Error"),wxYES_NO+wxICON_EXCLAMATION);
                if (res==wxNO) {
                    canceled = true;
                } else {
                    appendLogLine("<b>Ignoring problem...</b>");
                    success = false;
                }
            }
            if (!resetOnly) {
                if (canceled) {
                    appendLogError("<b>Programming aborted!</b>");
                    success = false;
                } else {
                    appendLogLine("<b>Erasing chip</b>");
                    prg->avr_chip_erase(*writePart);


                    if (xhexfile) {
                        if (xhexfile->devicePtr) {
                            for (unsigned int i=0; i<xhexfile->devicePtr->segmentVec.size(); i++) {
                                segment & seg (xhexfile->devicePtr->segmentVec[i]);
                                int file_format = avrdude::FMT_AUTO;
                                std::string immediate = "";
                                appendLogLine("<b>Writing '"+seg.id+"'</b>");
                                wxFile * file = new wxFile();
                                wxString filename = wxFileName::CreateTempFileName("rd_", file);
                                if (seg.format=="ihex") {
                                    file->Write(seg.content.data(), seg.content.size());
                                    file_format = avrdude::FMT_IHEX;
                                } else if (seg.format=="srec") {
                                    file->Write(seg.content.data(), seg.content.size());
                                    file_format = avrdude::FMT_SREC;
                                } else if ((seg.dataVec.size()==1) && (seg.dataVec[0].address=="")) {
                                    immediate = seg.dataVec[0].content;
                                    if (immediate.size()>2) {
                                        if ((immediate[0]!='0') || (immediate[1]!='x')) {
                                            immediate = "0x" + immediate;
                                        }
                                    }
                                    file_format = avrdude::FMT_IMM;
                                } else {
                                    // convert data to ihex lines
                                    for (unsigned int j=0; j<seg.dataVec.size(); j++) {
                                        std::string length = "10";
                                        std::string address = seg.dataVec[j].address;
                                        std::string data = seg.dataVec[j].content;
                                        std::string check = "00";
                                        file->Write(":"+length+address+"00"+data+check);
                                    }
                                    file->Write(":00000001FF\n");
                                    file_format = avrdude::FMT_IHEX;
                                }

                                file->Flush();
                                file->Close();
                                delete file;
                                avrdude::Update * writeUpdate;
                                if (file_format==avrdude::FMT_IMM) {
                                    //wxMessageBox(immediate, _("immediate"));
                                    writeUpdate = new avrdude::Update(avrdude::DEVICE_WRITE, seg.id, file_format, immediate);
                                } else {
                                    wxString temp = filename.fn_str();
                                    writeUpdate = new avrdude::Update(avrdude::DEVICE_WRITE, seg.id, file_format, static_cast<const char*>(temp));
                                }
                                //avrdude::Update * writeUpdate = new avrdude::Update(avrdude::DEVICE_WRITE, seg.id, avrdude::FMT_AUTO, "D:\\Sandbox\\sourceforge\\nibobeelib\\trunk\\hex\\program1.hex");

                                if (prg->do_update_op(*writePart, *writeUpdate, false, false)) {
                                    //wxMessageBox(_("PROGRAMMED"), _("Info"));
                                } else {
                                    appendLogError("<b>Error while writing "+seg.id+"</b>");
                                    wxMessageBox(_("Programming failed!"), _("Error"));
                                    success = false;
                                };
                                wxRemoveFile(filename);
                            }
                            appendLogLine("Segment finished.");
                        }

                    }
                }
                if (success) {
                    appendLogLine("Programming finished <font color='#00aa00'><b>successfully</b></font>.");
                } else {
                    appendLogLine("Programming finished.");
                }
            } else {
                if (success) {
                    appendLogLine("Reset finished <font color='#00aa00'><b>successfully</b></font>.");
                } else {
                    appendLogLine("Reset finished.");
                }
            }
            prg->disable();
            prg->close();
        } else {
            if (programmerPort!="") {
                appendLogError("<b>No programmer connected to "+programmerPort+"!</b>");
                wxMessageBox(_("No programmer connected to "+programmerPort+",\nplease connect!"), _("Error"));
            } else {
                appendLogError("<b>No programmer connected!</b>");
                wxMessageBox(_("No programmer connected,\nplease connect!"), _("Error"));
            }
        }
        delete writePart;
        delete verifyPart;
    }
    delete part;
    delete prg;
}
#endif // AVRDUDE_CONF

    
void MainFrame::doStartProgram() {
    bool success=false;
    clearLog();
    appendLogLine("Programming file <b>"+m_filename+"</b>");
    time_t starttime, endtime;
    time(&starttime);
    
    wxString programmerType = platformDialog->getProgrammer();
    platformDialog->updateSerialPort(true);
    HtmlWindowPlatform->setHTML(platformDialog->getHTML());
    wxString programmerPort = platformDialog->getInterface();
    //wxString programmerBaudrate = platformDialog->getBaudrate();
    //wxString programmerBitclock = platformDialog->getBitclock();
    wxString devicePart = platformDialog->getMCU().Lower();

    if (programmerType=="avrispv2") programmerType="progbob-avrisp";
    
    
    if ((programmerType=="progbob-armswd")&&(xhexfile)) {
        program_ARMSWD(programmerPort, devicePart, false);
    } else if ((programmerType=="progbob-avrisp")&&(xhexfile)) {
        program_AVRISP(programmerPort, devicePart, false);
    } else {
#ifdef AVRDUDE_CONF
        program_AVRDUDE(programmerPort, devicePart, false);
#else
        wxMessageBox(_("Programmer "+programmerType+" not supported"), _("Error"));
#endif
    }

    if (wxGetApp().autohide) {
        wxGetApp().autohide = false;
        frame->Iconize(true);
    }
    time(&endtime);
    double seconds = difftime(endtime, starttime);
    appendLogLine("Operation took " + (boost::lexical_cast<std::string>(seconds)) + " seconds.");
}


void MainFrame::OnMenuItemOpenSelected(wxCommandEvent& event) {
    if (FileDialog1->ShowModal()==wxID_OK) {
        loadFile(FileDialog1->GetPath());
    }
}


void MainFrame::loadFile(const wxString& name) {
    clearLog();

    std::string defPlatform = static_cast<const char*>(platformDialog->getPlatform());
    //appendLogLine("New file\n");
    appendLogLine("Loading file <font color='#000066'>"+name+"</font>\n");
    try {
        delete xhexfile;
        xhexfile = 0;
        xhexfile = xhex::create(static_cast<const char*>(name), defPlatform);
        wxFileName filename = wxFileName::FileName(name);
        m_filename = filename.GetFullName();
        appendLogLine("<b>File import finished!</b>");
    } catch (const std::exception & e) {
        wxString m = e.what();
        appendLogError("<b>Error during file import:</b> "+m);
    }
    if (xhexfile) {
        platformDialog->applyXhex(xhexfile);
        HtmlWindowPlatform->setHTML(platformDialog->getHTML());
    }
    updateGUI();
    HtmlWindowFile->setHTML(getFileHTML());
    updateGUI();

    wxConfig *config = new wxConfig("BobDude", wxEmptyString, wxEmptyString, wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
    wxString strValue = "";
    config->Write("lastFile", name);
    delete config;
}


void MainFrame::appendLogLine(const wxString& text_, bool replace) {
    wxString text = "<div>"+text_+"</div>";
    if (!replace) {
        m_log_lines += m_log_last;
    }
    m_log_last = text;
    int x, y;
    HtmlWindowLog->GetViewStart(&x,&y);
    HtmlWindowLog->SetPage(m_log_lines + m_log_last);
    HtmlWindowLog->Scroll(0, y+10);
    HtmlWindowLog->Update();
}


void MainFrame::clearLog() {
    m_log_lines = "<html><body><font size=\"2\">";
    m_log_last = "";
    HtmlWindowLog->SetPage(m_log_lines);
    HtmlWindowLog->Update();
    progressBar->SetValue(0);
    progressBar->Update();
}


void MainFrame::appendLogError(const wxString& text) {
    appendLogLine("<font color='#ee0000'>"+text+"</font>", false);
}


void MainFrame::appendLogSuccess(const wxString& text) {
    appendLogLine("<font color='#11dd11'>"+text+"</font>", false);
}


void MainFrame::OnMenuItemAboutSelected(wxCommandEvent& event) {
    wxAboutDialogInfo info;
    info.SetName(_(PACKAGE_NAME));
    info.SetVersion(_(PACKAGE_VERSION));
    info.SetDescription(_("Use BobDude to transfer *.bob3 files to B·O·B·3 robot devices."));
    info.SetCopyright(wxT("written by Nils Springob <nils@bob3.org>"));
    info.SetWebSite("https://www.bob3.org");
    wxAboutBox(info);
}


void MainFrame::OnMenuItemExitSelected(wxCommandEvent& event) {
    Close(true);
}


void MainFrame::updateGUI() {
    if (xhexfile==0) {
        HtmlWindowProgram->activateStyle("disabled");
    } else {
        HtmlWindowProgram->activateStyle("default");
    }
}


void MainFrame::OnMenuItemResetMCU(wxCommandEvent& event) {
    bool success=false;
    clearLog();
    appendLogLine("Resetting MCU");
    wxString programmerType = platformDialog->getProgrammer();
    wxString programmerPort = platformDialog->getInterface();
    //wxString programmerBaudrate = platformDialog->getBaudrate();
    //wxString programmerBitclock = platformDialog->getBitclock();
    wxString devicePart = platformDialog->getMCU().Lower();

    if (programmerType=="avrispv2") programmerType="progbob-avrisp";
    
    if (programmerType=="progbob-armswd") {
        program_ARMSWD(programmerPort, devicePart, true);
    } else if (programmerType=="progbob-avrisp") {
        program_AVRISP(programmerPort, devicePart, true);
    } else {
#ifdef AVRDUDE_CONF
        program_AVRDUDE(programmerPort, devicePart, true);
#else
        wxMessageBox(_("Programmer "+programmerType+" not supported"), _("Error"));
#endif
    }
}
