<[email protected]>
<http://www.gnu.org/licenses/>
#include "WMain.h"
#include "SortAlgo.h"
#include <SDL.h>
#include "wxg/WAbout_wxg.h"
WMain::WMain(wxWindow* parent)
    : WMain_wxg(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE)
{
    m_thread = NULL;
    g_sound_on = false;
    recordButton->Hide();
    panelQuickSortPivot->Hide();
    infoTextctrl->Hide();
    
    {    
        #include "sos.xpm"
	SetIcon( wxIcon(sos) );
    }
    
    SetTitle(_("The Sound of Sorting " PACKAGE_VERSION " - http://panthema.net/2013/sound-of-sorting"));
    
    splitter_0->SetSashPosition(GetSize().x - 280);
    
    for (const AlgoEntry* ae = g_algolist; ae != g_algolist_end; ++ae)
        algoList->Append(ae->name);
    algoList->SetSelection(0);
    
    sortview->FillInputlist(inputTypeChoice);
    sortview->FillData(0, 100);
    inputTypeChoice->SetSelection(0);
    arraySizeSlider->SetValue(100);
    SetArraySize(100);
    
    pivotRuleChoice->Append( QuickSortPivotText() );
    pivotRuleChoice->SetSelection(0);
    
    speedSlider->SetValue(1000);
    SetDelay(1000);
    
    soundSustainSlider->SetValue(700);
    SetSoundSustain(700);
    
    SDL_AudioSpec sdlaudiospec;
    
    sdlaudiospec.freq = 44100;
    sdlaudiospec.format = AUDIO_S16SYS;
    sdlaudiospec.channels = 1;    	
    sdlaudiospec.samples = 4096;  	
    sdlaudiospec.callback = SoundCallback;
    sdlaudiospec.userdata = sortview;
    
    if ( SDL_OpenAudio(&sdlaudiospec, NULL) < 0 ) {
        wxLogError(_("Couldn't open audio: ") + wxString(SDL_GetError(), wxConvISO8859_1));
        soundButton->Disable();
    }
    soundButton->SetValue(false);
    Show();
    
    m_reftimer = new RefreshTimer(this);
    m_reftimer->Start(1000 / g_framerate);
}
WMain::~WMain()
{
    AbortAlgorithm();
    SDL_CloseAudio();
}
BEGIN_EVENT_TABLE(WMain, WMain_wxg)
    EVT_TOGGLEBUTTON(ID_RUN_BUTTON, WMain::OnRunButton)
    EVT_BUTTON(ID_RESET_BUTTON, WMain::OnResetButton)
    EVT_BUTTON(ID_STEP_BUTTON, WMain::OnStepButton)
    EVT_TOGGLEBUTTON(ID_SOUND_BUTTON, WMain::OnSoundButton)
    EVT_BUTTON(ID_RANDOM_BUTTON, WMain::OnRandomButton)
    EVT_BUTTON(wxID_ABOUT, WMain::OnAboutButton)
    EVT_COMMAND_SCROLL(ID_SPEED_SLIDER, WMain::OnSpeedSliderChange)
    EVT_COMMAND_SCROLL(ID_SOUND_SUSTAIN_SLIDER, WMain::OnSoundSustainSliderChange)
    EVT_COMMAND_SCROLL(ID_ARRAY_SIZE_SLIDER, WMain::OnArraySizeSliderChange)
    EVT_LISTBOX(ID_ALGO_LIST, WMain::OnAlgoList)
    EVT_LISTBOX_DCLICK(ID_ALGO_LIST, WMain::OnAlgoListDClick)
    EVT_COMMAND(ID_RUN_FINISHED, wxEVT_COMMAND_BUTTON_CLICKED, WMain::OnRunFinished)
    EVT_COMMAND(ID_INVERSION_LABEL, wxEVT_COMMAND_BUTTON_CLICKED, WMain::OnInversionLabelClick)
END_EVENT_TABLE();
bool WMain::RunAlgorithm()
{
    ASSERT(!m_thread);
    if (algoList->GetSelection() == wxNOT_FOUND)
    {
        wxLogError(_("Please select a sorting algorithm"));
        runButton->SetValue(false);
        return false;
    }
    else
    {
        if (sortview->IsSorted())
            sortview->FillData( inputTypeChoice->GetSelection(), m_array_size );
        sortview->SetStepwise(false);
        g_algo_name = algoList->GetStringSelection();
        g_quicksort_pivot = (QuickSortPivotType)pivotRuleChoice->GetSelection();
        m_thread = new SortAlgoThread(this, *sortview, algoList->GetSelection());
        m_thread_terminate = false;
        m_thread->Create();
        g_algo_running = true;
        m_thread->Run();
        runButton->SetValue(true);
        return true;
    }
}
void WMain::AbortAlgorithm()
{
    if (!m_thread) return;
    m_thread_terminate = true;
    if (m_thread->IsPaused()) m_thread->Resume();
    sortview->SetStepwise(false);
    m_thread->Wait();
    g_algo_running = false;
    delete m_thread;
    m_thread = NULL;
}
void WMain::OnRunButton(wxCommandEvent &event)
{
    
    if (m_thread && !m_thread->IsAlive())
    {
        m_thread->Wait();
        g_algo_running = false;
        delete m_thread;
        m_thread = NULL;
    }
    if (event.IsChecked())
    {
        if (!m_thread)
        {
            RunAlgorithm();
        }
        else
        {
            g_algo_running = true;
            sortview->SetStepwise(false);
            if (m_thread->IsPaused()) m_thread->Resume();
        }
    }
    else
    {
        if (m_thread)
        {
            m_thread->Pause();
            g_algo_running = false;
        }
    }
}
void WMain::OnRunFinished(wxCommandEvent&)
{
    
    if (m_thread)
    {
        m_thread->Wait();
        g_algo_running = false;
        delete m_thread;
        m_thread = NULL;
    }
    runButton->SetValue(false);
}
void WMain::OnResetButton(wxCommandEvent&)
{
    
    AbortAlgorithm();
    runButton->SetValue(false);
    sortview->FillData( inputTypeChoice->GetSelection(), m_array_size );
}
void WMain::OnStepButton(wxCommandEvent&)
{
    
    if (!m_thread)
    {
        if (!RunAlgorithm()) return;
        sortview->SetStepwise(true);
    }
    else
    {
        if (m_thread->IsPaused()) m_thread->Resume();
        sortview->SetStepwise(true);    
        runButton->SetValue(false);
        g_algo_running = true;
    }
    
    sortview->DoStepwise();
}
void WMain::OnSoundButton(wxCommandEvent&)
{
    if (soundButton->GetValue())
    {
        SoundReset();
        SDL_PauseAudio(0);
        g_sound_on = true;
    }
    else
    {
        g_sound_on = false;
        SDL_PauseAudio(1);
    }
}
void WMain::OnRandomButton(wxCommandEvent&)
{
    AbortAlgorithm();
    algoList->SetSelection( rand() % algoList->GetCount() );
    sortview->FillData( inputTypeChoice->GetSelection(), m_array_size );
    RunAlgorithm();
    algoList->SetSelection(wxNOT_FOUND);
}
class WAbout : public WAbout_wxg
{
public:
    WAbout(wxWindow* parent)
        : WAbout_wxg(parent, wxID_ANY, wxEmptyString)
    {
        labelTitle->SetLabel(_("The Sound of Sorting " PACKAGE_VERSION));
        labelBuildDate->SetLabel(_("Build Date: " __DATE__));
        GetSizer()->Fit(this);
        Layout();
        Centre();
    }
};
void WMain::OnAboutButton(wxCommandEvent&)
{
    WAbout dlg(this);
    dlg.ShowModal();
}
void WMain::OnSpeedSliderChange(wxScrollEvent &event)
{
    SetDelay(event.GetPosition());
}
void WMain::SetDelay(size_t pos)
{
    
    const double base = 4;
    
#if __WXGTK__ || MSW_PERFORMANCECOUNTER
    g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0 * 10.0) / log(base)) / 10.0;
#else
    
    g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0) / log(base));
#endif
    if (pos == 0) g_delay = 0;
    if (g_delay > 10)
        labelDelayValue->SetLabel(wxString::Format(_("%.0f ms"), g_delay));
    else if (g_delay > 1)
        labelDelayValue->SetLabel(wxString::Format(_("%.1f ms"), g_delay));
    else
        labelDelayValue->SetLabel(wxString::Format(_("%.2f ms"), g_delay));
}
void WMain::OnSoundSustainSliderChange(wxScrollEvent &event)
{
    SetSoundSustain(event.GetPosition());
}
void WMain::OnInversionLabelClick(wxCommandEvent &)
{
    sortview->ToggleCalcInversions();
}
void WMain::SetSoundSustain(size_t pos)
{
    
    const double base = 4;
    g_sound_sustain = pow(base, pos / 2000.0 * log(50) / log(base));
    labelSoundSustainValue->SetLabel(wxString::Format(_("%.1f"), g_sound_sustain));
    labelSoundSustainValue->GetContainingSizer()->Layout();
}
void WMain::OnArraySizeSliderChange(wxScrollEvent &event)
{
    SetArraySize(event.GetPosition());
}
void WMain::SetArraySize(size_t pos)
{
    
    
    
    m_array_size = pos;
    labelArraySizeValue->SetLabel(wxString::Format(_("%4lu"), (long unsigned)m_array_size));
    labelArraySizeValue->GetContainingSizer()->Layout();
}
void WMain::OnAlgoList(wxCommandEvent&)
{
    int sel = algoList->GetSelection();
    wxString text;
    bool isQuickSort = (algoList->GetStringSelection().Contains(_("Quick Sort")));
    panelQuickSortPivot->Show(isQuickSort);
    if (sel >= 0 && sel < (int)g_algolist_size && !g_algolist[sel].text.IsEmpty())
    {
        text = g_algolist[sel].text;
        infoTextctrl->SetValue(text);
        infoTextctrl->Show();
        infoTextctrl->GetContainingSizer()->Layout();
    }
    else
    {
        infoTextctrl->Hide();
        infoTextctrl->GetContainingSizer()->Layout();
    }
}
void WMain::OnAlgoListDClick(wxCommandEvent&)
{
    
    if (m_thread)
    {
        AbortAlgorithm();
        sortview->FillData( inputTypeChoice->GetSelection(), m_array_size );
    }
    
    RunAlgorithm();
}
WMain::RefreshTimer::RefreshTimer(WMain* wmain)
    : wm(*wmain)
{
}
void WMain::RefreshTimer::Notify()
{
    
    long int accesses = g_access_count;
    wm.labelAccessCount->SetLabel(wxString::Format(_("%ld"), accesses));
    long int compares = g_compare_count;
    wm.labelComparisonsValue->SetLabel(wxString::Format(_("%ld"), compares));
    long int inversions = wm.sortview->GetInversions();
    if (inversions >= 0)
        wm.labelInversionCount->SetLabel(wxString::Format(_("%ld"), inversions));
    else
        wm.labelInversionCount->SetLabel(_("skipped"));
    long int runs = wm.sortview->GetRuns();
    wm.labelRunsCount->SetLabel(wxString::Format(_("%ld"), runs));
    
    wm.sortview->Refresh(false);
}
class MyApp : public wxApp
{
public:
    virtual bool OnInit()
    {
        if (SDL_Init(SDL_INIT_AUDIO) < 0)
        {
            wxLogError(wxT("Couldn't initialize SDL: ") + wxString(SDL_GetError(), wxConvISO8859_1));
            wxLog::FlushActive();
            return false;
        }
        srand((int)wxGetLocalTime());
        WMain* wmain = new WMain(NULL);
        SetTopWindow(wmain);
        wmain->Show();
        return true;
    }
    virtual int OnExit()
    {
        SDL_Quit();
        
        return wxApp::OnExit();
    }
};
IMPLEMENT_APP(MyApp)