/*
 * This file is part of XDRE.
 *
 * XDRE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * XDRE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with XDRE.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Этот файл — часть XDRE.
 *
 * XDRE — свободная программа: вы можете перераспространять её и/или
 * изменять её на условиях Стандартной общественной лицензии GNU в том виде,
 * в каком она была опубликована Фондом свободного программного обеспечения;
 * либо версии 2 лицензии, либо (по вашему выбору) любой более поздней
 * версии.
 *
 * XDRE распространяется в надежде, что она будет полезной,
 * но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
 * или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
 * общественной лицензии GNU.
 *
 * Вы должны были получить копию Стандартной общественной лицензии GNU
 * вместе с этой программой. Если это не так, см.
 * <http://www.gnu.org/licenses/>.
 *
 * Description: brute force dialog.
*/

#include "BruteDialog.hpp"

#include <wx/intl.h>
#include <wx/string.h>
#include <wx/valnum.h>
#include <wx/valtext.h>
#include <wx/msgdlg.h>
#include <wx/tokenzr.h>

class BruteException : public std::exception {
public:
    BruteException(const char* str) : str{str} {}
    virtual const char* what() const noexcept {return str;}
    const char* str;
};

const long BruteDialog::ID_BRUTETICSBOX = wxNewId();
const long BruteDialog::ID_BRUTETICINPUT = wxNewId();
const long BruteDialog::ID_STRAFECMDINPUT = wxNewId();
const long BruteDialog::ID_RUNCMDINPUT = wxNewId();
const long BruteDialog::ID_TURNCMDINPUT = wxNewId();
const long BruteDialog::ID_TURNANGLEBUTTON = wxNewId();
const long BruteDialog::ID_FIRERADIOBOX = wxNewId();
const long BruteDialog::ID_USERADIOBOX = wxNewId();
const long BruteDialog::ID_CANCEL = wxNewId();
const long BruteDialog::ID_START = wxNewId();
const long BruteDialog::ID_ADDBUTTON = wxNewId();
const long BruteDialog::ID_REMOVEBUTTON = wxNewId();

wxDEFINE_EVENT(EVT_BRUTE_DONE, wxThreadEvent);
BEGIN_EVENT_TABLE(BruteDialog,wxDialog)
END_EVENT_TABLE()

BruteDialog::BruteDialog(wxWindow* parent,wxWindowID id,const wxPoint& pos,const wxSize& size) {
    wxDialog::Create(parent, wxID_ANY, _("Brute force"));
    SetClientSize(wxSize(400,440));
    SetMinSize(wxSize(400,440));
    SetMaxSize(wxSize(400,-1));
    p = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(400,440), wxTAB_TRAVERSAL);

    checkTicInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(288,44), wxSize(96,21), wxTE_RIGHT, wxIntegerValidator<unsigned int>());
    rngInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,80), wxSize(56,21), wxTE_RIGHT);
    damageInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,112), wxSize(56,21), wxTE_RIGHT, wxIntegerValidator<unsigned int>());
    xPosInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,144), wxSize(56,21), wxTE_RIGHT, wxFloatingPointValidator<double>());
    yPosInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,176), wxSize(56,21), wxTE_RIGHT, wxFloatingPointValidator<double>());
    zPosInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,208), wxSize(56,21), wxTE_RIGHT, wxIntegerValidator<int>());
    xMomInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,240), wxSize(56,21), wxTE_RIGHT, wxFloatingPointValidator<double>());
    yMomInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,272), wxSize(56,21), wxTE_RIGHT, wxFloatingPointValidator<double>());
    speedInput = new wxTextCtrl(p, wxID_ANY, wxEmptyString, wxPoint(328,304), wxSize(56,21), wxTE_RIGHT, wxFloatingPointValidator<double>());

    rngCheckBox = new wxCheckBox(p, wxID_ANY, _("RNG index"), wxPoint(204,84), wxDefaultSize, wxALIGN_RIGHT);
    rngCheckBox->SetValue(false);
    rngChoice = new wxChoice(p, wxID_ANY, wxPoint(280,80), wxSize(40,21), 0, 0, 0, wxDefaultValidator);
    rngChoice->SetSelection( rngChoice->Append(_("=")) );
    rngChoice->Append(_("!="));

    damageCheckBox = new wxCheckBox(p, wxID_ANY, _("Damage"), wxPoint(216,116), wxDefaultSize, wxALIGN_RIGHT);
    damageCheckBox->SetValue(false);
    damageChoice = new wxChoice(p, wxID_ANY, wxPoint(280,112), wxSize(40,21), 0, 0, 0);
    damageChoice->SetSelection( damageChoice->Append(_("=")) );
    damageChoice->Append(_(">="));
    damageChoice->Append(_(">"));
    damageChoice->Append(_("<="));
    damageChoice->Append(_("<"));

    xPosCheckBox = new wxCheckBox(p, wxID_ANY, _("X"), wxPoint(248,148), wxDefaultSize, wxALIGN_RIGHT);
    xPosCheckBox->SetValue(false);
    xPosChoice = new wxChoice(p, wxID_ANY, wxPoint(280,144), wxSize(40,21), 0, 0, 0);
    xPosChoice->SetSelection(xPosChoice->Append(_("=")));
    xPosChoice->Append(_(">="));
    xPosChoice->Append(_(">"));
    xPosChoice->Append(_("<="));
    xPosChoice->Append(_("<"));

    yPosCheckBox = new wxCheckBox(p, wxID_ANY, _("Y"), wxPoint(248,180), wxDefaultSize, wxALIGN_RIGHT);
    yPosCheckBox->SetValue(false);
    yPosChoice = new wxChoice(p, wxID_ANY, wxPoint(280,176), wxSize(40,21), 0, 0, 0);
    yPosChoice->SetSelection(yPosChoice->Append(_("=")));
    yPosChoice->Append(_(">="));
    yPosChoice->Append(_(">"));
    yPosChoice->Append(_("<="));
    yPosChoice->Append(_("<"));

    zPosCheckBox = new wxCheckBox(p, wxID_ANY, _("Z"), wxPoint(248,212), wxDefaultSize, wxALIGN_RIGHT);
    zPosCheckBox->SetValue(false);
    zPosChoice = new wxChoice(p, wxID_ANY, wxPoint(280,208), wxSize(40,21), 0, 0, 0);
    zPosChoice->SetSelection(zPosChoice->Append(_("=")));
    zPosChoice->Append(_(">="));
    zPosChoice->Append(_(">"));
    zPosChoice->Append(_("<="));
    zPosChoice->Append(_("<"));

    xMomCheckBox = new wxCheckBox(p, wxID_ANY, _("X mom."), wxPoint(220,244), wxDefaultSize, wxALIGN_RIGHT);
    xMomCheckBox->SetValue(false);
    xMomChoice = new wxChoice(p, wxID_ANY, wxPoint(280,240), wxSize(40,21), 0, 0, 0);
    xMomChoice->SetSelection(xMomChoice->Append(_("=")));
    xMomChoice->Append(_(">="));
    xMomChoice->Append(_(">"));
    xMomChoice->Append(_("<="));
    xMomChoice->Append(_("<"));

    yMomCheckBox = new wxCheckBox(p, wxID_ANY, _("Y mom."), wxPoint(220,276), wxDefaultSize, wxALIGN_RIGHT);
    yMomCheckBox->SetValue(false);
    yMomChoice = new wxChoice(p, wxID_ANY, wxPoint(280,272), wxSize(40,21), 0, 0, 0);
    yMomChoice->SetSelection(yMomChoice->Append(_("=")));
    yMomChoice->Append(_(">="));
    yMomChoice->Append(_(">"));
    yMomChoice->Append(_("<="));
    yMomChoice->Append(_("<"));

    speedCheckBox = new wxCheckBox(p, wxID_ANY, _("Speed"), wxPoint(224,308), wxDefaultSize, wxALIGN_RIGHT);
    speedCheckBox->SetValue(false);
    speedChoice = new wxChoice(p, wxID_ANY, wxPoint(280,304), wxSize(40,21), 0, 0, 0);
    speedChoice->SetSelection(speedChoice->Append(_("=")));
    speedChoice->Append(_(">="));
    speedChoice->Append(_(">"));
    speedChoice->Append(_("<="));
    speedChoice->Append(_("<"));
    useCheckBox = new wxCheckBox(p, wxID_ANY, _("Use"), wxPoint(240,340), wxDefaultSize, wxALIGN_RIGHT);
    useCheckBox->SetValue(false);

    bruteTicsBox = new wxListBox(p, ID_BRUTETICSBOX, wxPoint(16,40), wxSize(168,80), 0, 0, 0);
    addButton = new wxButton(p, ID_ADDBUTTON, _("Add"), wxPoint(16,128), wxSize(74,-1), 0);
    removeButton = new wxButton(p, ID_REMOVEBUTTON, _("Remove"), wxPoint(110,128), wxSize(74,-1), 0);
    removeButton->Disable();
    bruteTicInput = new wxTextCtrl(p, ID_BRUTETICINPUT, wxEmptyString, wxPoint(88,160), wxSize(96,21), wxTE_RIGHT, wxIntegerValidator<unsigned int>());
    bruteTicInput->Disable();
    strafeCmdInput = new wxTextCtrl(p, ID_STRAFECMDINPUT, wxEmptyString, wxPoint(88,184), wxSize(96,21), wxTE_RIGHT, wxTextValidator());
    strafeCmdInput->Disable();
    runCmdInput = new wxTextCtrl(p, ID_RUNCMDINPUT, wxEmptyString, wxPoint(88,208), wxSize(96,21), wxTE_RIGHT, wxTextValidator());
    runCmdInput->Disable();
    turnCmdInput = new wxTextCtrl(p, ID_TURNCMDINPUT, wxEmptyString, wxPoint(88,232), wxSize(96,21), wxTE_RIGHT, wxTextValidator());
    turnCmdInput->Disable();
    turnAngleButton = new wxButton(p, ID_TURNANGLEBUTTON, _("Turn cmds"), wxPoint(16,232), wxSize(67,23), 0);
    turnAngleButton->Disable();
    wxString __wxRadioBoxChoices_1[3] = {_("No"), _("Both"), _("Yes")};
    fireRadioBox = new wxRadioBox(p, ID_FIRERADIOBOX, _("Fire"), wxPoint(72,256), wxSize(56,88), 3, __wxRadioBoxChoices_1, 3, wxRA_VERTICAL);
    fireRadioBox->SetSelection(0);
    fireRadioBox->Disable();
    wxString __wxRadioBoxChoices_2[3] = {_("No"), _("Both"), _("Yes")};
    useRadioBox = new wxRadioBox(p, ID_USERADIOBOX, _("Use"), wxPoint(128,256), wxSize(56,88), 3, __wxRadioBoxChoices_2, 3, wxRA_VERTICAL);
    useRadioBox->SetSelection(0);
    useRadioBox->Disable();
    cancel = new wxButton(p, ID_CANCEL, _("Cancel"), wxPoint(248,392), wxDefaultSize, 0, wxDefaultValidator);
    start = new wxButton(p, ID_START, _("Start"), wxPoint(80,392), wxDefaultSize, 0, wxDefaultValidator);
    workingText = new wxStaticText(p, wxID_ANY, _("Working..."), wxPoint(176,396), wxDefaultSize, 0);
    workingText->Hide();
    StaticBox1 = new wxStaticBox(p, wxID_ANY, _("Checks"), wxPoint(200,8), wxSize(192,376), 0);
    StaticBox2 = new wxStaticBox(p, wxID_ANY, _("Tics to brute force"), wxPoint(8,8), wxSize(192,336), 0);
    StaticText1 = new wxStaticText(p, wxID_ANY, _("On tic"), wxPoint(248,48), wxDefaultSize, 0);
    StaticText2 = new wxStaticText(p, wxID_ANY, _("Tic"), wxPoint(62,164), wxDefaultSize, 0);
    StaticText3 = new wxStaticText(p, wxID_ANY, _("Run cmds"), wxPoint(26,212), wxDefaultSize, 0);
    StaticText4 = new wxStaticText(p, wxID_ANY, _("Strafe cmds"), wxPoint(16,188), wxDefaultSize, 0);

    Connect(ID_BRUTETICSBOX,wxEVT_COMMAND_LISTBOX_SELECTED,(wxObjectEventFunction)&BruteDialog::OnBruteTicsBoxSelect);
    Connect(ID_BRUTETICINPUT,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&BruteDialog::OnBruteTicInputText);

    Connect(ID_STRAFECMDINPUT,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&BruteDialog::OnStrafeCmdInputText);
    Connect(ID_RUNCMDINPUT,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&BruteDialog::OnRunCmdInputText);
    Connect(ID_TURNCMDINPUT,wxEVT_COMMAND_TEXT_UPDATED,(wxObjectEventFunction)&BruteDialog::OnTurnCmdInputText);
    Connect(ID_FIRERADIOBOX,wxEVT_COMMAND_RADIOBOX_SELECTED,(wxObjectEventFunction)&BruteDialog::OnFireRadioBoxSelect);
    Connect(ID_USERADIOBOX,wxEVT_COMMAND_RADIOBOX_SELECTED,(wxObjectEventFunction)&BruteDialog::OnUseRadioBoxSelect);
    Connect(ID_TURNANGLEBUTTON,wxEVT_BUTTON,(wxObjectEventFunction)&BruteDialog::OnTurnAngleButtonClick);

    Connect(wxID_ANY,wxEVT_CLOSE_WINDOW,(wxObjectEventFunction)&BruteDialog::OnClose);
    Connect(ID_CANCEL,wxEVT_BUTTON,(wxObjectEventFunction)&BruteDialog::OnCancelClick);
    Connect(ID_START,wxEVT_BUTTON,(wxObjectEventFunction)&BruteDialog::OnStartClick);
    Connect(ID_ADDBUTTON,wxEVT_BUTTON,(wxObjectEventFunction)&BruteDialog::OnAddButtonClick);
    Connect(ID_REMOVEBUTTON,wxEVT_BUTTON,(wxObjectEventFunction)&BruteDialog::OnRemoveButtonClick);


    (*checkTicInput) << static_cast<long>(xdre::getCurrentTic());
    rngInput->ChangeValue("0");
    damageInput->ChangeValue("0");
    xPosInput->ChangeValue("0");
    yPosInput->ChangeValue("0");
    zPosInput->ChangeValue("0");
    xMomInput->ChangeValue("0");
    yMomInput->ChangeValue("0");
    speedInput->ChangeValue("0");
}

BruteDialog::~BruteDialog() {}

std::vector<signed char> BruteDialog::ParseCmds(wxString str, bool parseAsUnsigned) {
    long limitMax = 127;
    long limitMin = -128;
    if(parseAsUnsigned) {
        limitMax = 255;
        limitMin = 0;
    }

    std::vector<signed char> ret;

    auto cmds = wxStringTokenize(str, ",", wxTOKEN_STRTOK);

    for(auto& cmd : cmds) {
        auto range = wxStringTokenize(cmd, ":", wxTOKEN_STRTOK);

        if(range.GetCount() == 1) {
            long num;
            if(cmd.ToLong(&num)) {
                if(num > limitMax || num < limitMin) throw BruteException("Too big input value given.");
                ret.push_back(num);
            } else {
                throw BruteException("Invalid value.");
            }
        } else if(range.GetCount() == 2) {
            long start;
            long end;
            if(!range[0].ToLong(&start) || !range[1].ToLong(&end)) throw BruteException("Invalid range given.");

            if(start > limitMax || start < limitMin || end > limitMax || end < limitMin)
                throw BruteException("Too big input value given.");

            if(start <= end) for(; start <= end; ++start) ret.push_back(start);
            else for(; start >= end; --start) ret.push_back(start);
        } else throw BruteException("Invalid range given.");
    }
    return ret;
}

std::vector<BruteTic> BruteDialog::CreateBruteTics() {
    std::vector<BruteTic> ret;
    for(auto& item : items) {
        BruteTic tic;
        long num;

        if(!item.ticStr.ToLong(&num)) {
            throw BruteException("Tic value not a number.");
        } else if(num < 0) {throw BruteException("Tic value < 0.");}

        tic.tic = num;
        tic.fire = item.fire - 1;
        tic.use = item.use - 1;

        if(tic.fire < -1 || tic.fire > 1 || tic.use < -1 || tic.use > 1) throw BruteException("Invalid fire/use choices.");

        bool turnsUnsigned;
        if(item.turnOrAngle == 0) {
            tic.turnsAreAngles = false;
            turnsUnsigned = false;
        } else {
            tic.turnsAreAngles = true;
            turnsUnsigned = true;
        }

        tic.runCmds = ParseCmds(item.runStr);
        tic.strafeCmds = ParseCmds(item.strafeStr);
        tic.turnCmds = ParseCmds(item.turnStr, turnsUnsigned);

        if(tic.runCmds.empty() || tic.strafeCmds.empty() || tic.turnCmds.empty()) throw BruteException("Empty field.");

        ret.push_back(tic);
    }

    return ret;
}

BruteCheck BruteDialog::CreateBruteCheck() {
    BruteCheck check;

    long tic;
    if(!checkTicInput->GetValue().ToLong(&tic)) {
        throw BruteException("Checktic not a number.");
    } else if(tic < 0) {throw BruteException("Checktic < 0.");}

    check.tic = tic;

//TODO: refactor this shit
    if(rngCheckBox->GetValue()) {
        auto indexes = ParseCmds(rngInput->GetValue(), true);
        switch(rngChoice->GetSelection()) {
        case 0:
            check.rngFunc = [indexes](int val) {
                for(auto index : indexes) {
                    if(val == index) return true;
                }
                return false;
            };
            break;

        case 1:
            check.rngFunc = [indexes](int val) {
                for(auto index : indexes) {
                    if(val == index) return false;
                }
                return true;
            };
            break;
        }
    } else {check.rngFunc = [](int val){return true;};}
    if(damageCheckBox->GetValue()) {
        long num;
        if(!damageInput->GetValue().ToLong(&num)) throw BruteException("Invalid damage.");
        switch(damageChoice->GetSelection()) {
        case 0: check.damageFunc = [num](int val){return val == num;}; break;
        case 1: check.damageFunc = [num](int val){return val >= num;}; break;
        case 2: check.damageFunc = [num](int val){return val > num;}; break;
        case 3: check.damageFunc = [num](int val){return val <= num;}; break;
        case 4: check.damageFunc = [num](int val){return val < num;}; break;
        }
    } else {check.damageFunc = [](int val){return true;};}
    if(xPosCheckBox->GetValue()) {
        double dbl;
        if(!xPosInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid X position.");
        switch(xPosChoice->GetSelection()) {
        case 0: check.xPosFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.xPosFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.xPosFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.xPosFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.xPosFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.xPosFunc = [](double val){return true;};}
    if(yPosCheckBox->GetValue()) {
        double dbl;
        if(!yPosInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid Y position.");
        switch(yPosChoice->GetSelection()) {
        case 0: check.yPosFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.yPosFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.yPosFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.yPosFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.yPosFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.yPosFunc = [](double val){return true;};}
    if(zPosCheckBox->GetValue()) {
        double dbl;
        if(!zPosInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid Z position.");
        switch(zPosChoice->GetSelection()) {
        case 0: check.zPosFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.zPosFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.zPosFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.zPosFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.zPosFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.zPosFunc = [](double val){return true;};}
    if(xMomCheckBox->GetValue()) {
        double dbl;
        if(!xMomInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid X momentum.");
        switch(xMomChoice->GetSelection()) {
        case 0: check.xMomFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.xMomFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.xMomFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.xMomFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.xMomFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.xMomFunc = [](double val){return true;};}
    if(yMomCheckBox->GetValue()) {
        double dbl;
        if(!yMomInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid Y momentum.");
        switch(yMomChoice->GetSelection()) {
        case 0: check.yMomFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.yMomFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.yMomFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.yMomFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.yMomFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.yMomFunc = [](double val){return true;};}

    if(speedCheckBox->GetValue()) {
        double dbl;
        if(!speedInput->GetValue().ToDouble(&dbl)) throw BruteException("Invalid speed.");
        switch(speedChoice->GetSelection()) {
        case 0: check.speedFunc = [dbl](double val){return val == dbl;}; break;
        case 1: check.speedFunc = [dbl](double val){return val >= dbl;}; break;
        case 2: check.speedFunc = [dbl](double val){return val > dbl;}; break;
        case 3: check.speedFunc = [dbl](double val){return val <= dbl;}; break;
        case 4: check.speedFunc = [dbl](double val){return val < dbl;}; break;
        }
    } else {check.speedFunc = [](double val){return true;};}
    check.use = useCheckBox->GetValue();
    return check;
}

void BruteDialog::ShowData(int n) {
    ListItem const& data = items.at(n);
    bruteTicInput->ChangeValue(data.ticStr);
    runCmdInput->ChangeValue(data.runStr);
    strafeCmdInput->ChangeValue(data.strafeStr);
    turnCmdInput->ChangeValue(data.turnStr);
    fireRadioBox->SetSelection(data.fire);
    useRadioBox->SetSelection(data.use);
    turnAngleButton->SetLabel(turnAngleTexts[data.turnOrAngle]);
}

void BruteDialog::EnableStuff() {
    bruteTicInput->Enable();
    runCmdInput->Enable();
    strafeCmdInput->Enable();
    turnCmdInput->Enable();
    fireRadioBox->Enable();
    useRadioBox->Enable();
    turnAngleButton->Enable();
    removeButton->Enable();
}

void BruteDialog::DisableStuff() {
    bruteTicInput->Disable();
    runCmdInput->Disable();
    strafeCmdInput->Disable();
    turnCmdInput->Disable();
    fireRadioBox->Disable();
    useRadioBox->Disable();
    turnAngleButton->Disable();
    removeButton->Disable();
}

void BruteDialog::OnClose(wxCloseEvent& event) {
    if(GetThread() && GetThread()->IsRunning()) return;
    Destroy();
}

void BruteDialog::OnCancelClick(wxCommandEvent& event)
{
    if(GetThread() && GetThread()->IsRunning()) {
        abortBrute = true;
        GetThread()->Wait();
        return;
    }
    Close();
}

void BruteDialog::OnTurnAngleButtonClick(wxCommandEvent& event) {
    auto item = bruteTicsBox->GetSelection();
    if(items[item].turnOrAngle) {
        items[item].turnOrAngle = 0;
    } else {items[item].turnOrAngle = 1;}
    turnAngleButton->SetLabel(turnAngleTexts[items[item].turnOrAngle]);
}

void BruteDialog::OnAddButtonClick(wxCommandEvent& event) {
    items.push_back({});
    int last = items.size() - 1;
    bruteTicsBox->Append(items.at(last).ticStr);
    bruteTicsBox->SetSelection(last);
    items.at(last).ticStr = wxString::Format(wxT("%i"), xdre::getCurrentTic());
    bruteTicsBox->SetString(last, items.at(last).ticStr);
    EnableStuff();
    ShowData(last);
}

void BruteDialog::OnRemoveButtonClick(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    bruteTicsBox->Delete(item);
    items.erase(std::begin(items) + item);
    int sel = item < items.size() ? item : items.size() - 1;
    bruteTicsBox->SetSelection(sel);
    if(sel >= 0) ShowData(sel);
    if(bruteTicsBox->GetCount() < 1) DisableStuff();
}

void BruteDialog::OnBruteTicsBoxSelect(wxCommandEvent& event) {ShowData(bruteTicsBox->GetSelection());}

void BruteDialog::OnBruteTicInputText(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    items.at(item).ticStr = bruteTicInput->GetValue();
    if(items.at(item).ticStr.IsSameAs("")) items.at(item).ticStr = "_";
    bruteTicsBox->SetString(item, items.at(item).ticStr);
}

void BruteDialog::OnRunCmdInputText(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    items.at(item).runStr = runCmdInput->GetValue();
}

void BruteDialog::OnStrafeCmdInputText(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    items.at(item).strafeStr = strafeCmdInput->GetValue();
}

void BruteDialog::OnTurnCmdInputText(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND)return;
    items.at(item).turnStr = turnCmdInput->GetValue();
}

void BruteDialog::OnFireRadioBoxSelect(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    items.at(item).fire = fireRadioBox->GetSelection();
}

void BruteDialog::OnUseRadioBoxSelect(wxCommandEvent& event) {
    int item = bruteTicsBox->GetSelection();
    if(item == wxNOT_FOUND) return;
    items.at(item).use = useRadioBox->GetSelection();
}

wxThread::ExitCode BruteDialog::Entry() {
    bruteSuccess = xdre::bruteForce(bruteTics, bruteCheck, abortBrute);
    wxQueueEvent(this, new wxThreadEvent(EVT_BRUTE_DONE));
    return static_cast<wxThread::ExitCode>(0);
}

void BruteDialog::OnBruteDone(wxThreadEvent& event) {
    Disconnect(wxID_ANY, EVT_BRUTE_DONE, (wxObjectEventFunction)&BruteDialog::OnBruteDone);
    start->Enable();
    workingText->Hide();

    if(bruteSuccess) {
        wxMessageBox("Found it!",
                     "Success",
                     wxOK | wxCENTRE,
                     this);
        if(GetThread() && GetThread()->IsRunning()) {
            GetThread()->Wait();
        }
//        Close();
    } else if(!bruteSuccess && !abortBrute) {
        wxMessageBox("Whole range examined. No cigar.",
                     "No success",
                     wxOK | wxCENTRE,
                     this);
    }
}

void BruteDialog::OnStartClick(wxCommandEvent& event)
{
    if(GetThread() && GetThread()->IsRunning()) return;

    try {
        bruteTics = CreateBruteTics();
        if(bruteTics.empty()) return;
        bruteCheck = CreateBruteCheck();

        if(CreateThread(wxTHREAD_JOINABLE) == wxTHREAD_NO_ERROR) {
            Connect(wxID_ANY, EVT_BRUTE_DONE, (wxObjectEventFunction)&BruteDialog::OnBruteDone);
            start->Disable();
            abortBrute = false;
            bruteSuccess = false;
            if(GetThread()->Run() == wxTHREAD_NO_ERROR) {
                workingText->Show();
            } else {
                wxMessageBox("Couldn't start brute force thread.",
                             "Brute force error",
                             wxOK | wxCENTRE | wxICON_ERROR,
                             this);
                Disconnect(wxID_ANY, EVT_BRUTE_DONE, (wxObjectEventFunction)&BruteDialog::OnBruteDone);
                start->Enable();
                workingText->Hide();
            }
        } else {
            wxMessageBox("Couldn't create brute force thread.",
                         "Brute force error",
                         wxOK | wxCENTRE | wxICON_ERROR,
                         this);
        }
    } catch (BruteException& e) {
        wxMessageBox(e.what(),
                     "Brute force error",
                     wxOK | wxCENTRE | wxICON_ERROR,
                     this);
    }
}

