/////////////////////////////////////////////////////////////////////////////
// Name:        mmboard.cpp
// Purpose:     Multimedia Library sample
// Author:      Guilhem Lavaux (created from minimal by J. Smart)
// Modified by:
// Created:     13/02/2000
// RCS-ID:      $Id: mmboard.cpp,v 1.6 2002/04/08 10:50:13 JS Exp $
// Copyright:   (c) Guilhem Lavaux
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#ifdef __GNUG__
    #pragma implementation "mmboard.cpp"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWindows headers
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// ----------------------------------------------------------------------------
// ressources
// ----------------------------------------------------------------------------
// the application icon
#if defined(__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__) || defined(__WXMAC__)
    #include "mondrian.xpm"
#endif

// include multimedia classes
#include "wx/mmedia/sndbase.h"
#ifdef __WIN32__
    #include "wx/mmedia/sndwin.h"
#endif
#ifdef __UNIX__
    #include "wx/mmedia/sndoss.h"
    #include "wx/mmedia/sndesd.h"
#endif

#include "wx/statline.h"
#include "wx/stattext.h"

// include personnal classes
#include "mmboard.h"
#include "mmbman.h"

#include "play.xpm"
#include "stop.xpm"
#include "eject.xpm"
#include "pause.xpm"

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

// Main Multimedia Board frame
class MMBoardFrame : public wxFrame
{
public:
    // ctor(s)
    MMBoardFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
    // dtor
    ~MMBoardFrame();
    
    // event handlers
    void OnQuit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
    void OnOpen(wxCommandEvent& event);
    void OnPlay(wxCommandEvent& event);
    void OnStop(wxCommandEvent& event);
    void OnPause(wxCommandEvent& event);
    void OnEject(wxCommandEvent& event);
    void OnRefreshInfo(wxEvent& event);
    void OnSetPosition(wxCommandEvent& event);
    
    void OpenVideoWindow();
    void CloseVideoWindow();
    
private:
    // any class wishing to process wxWindows events must use this macro
    DECLARE_EVENT_TABLE()

private:
    void UpdateMMedInfo(); 
    void UpdateInfoText();
    
    MMBoardFile *m_opened_file;
    
    wxSlider *m_positionSlider;
    wxBitmapButton *m_playButton, *m_pauseButton, *m_stopButton, *m_ejectButton;
    wxStaticText *m_fileType, *m_infoText;
    wxWindow *m_video_window;
    
    wxPanel *m_panel;
    wxSizer *m_sizer;
    
    wxTimer *m_refreshTimer;
};

// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// IDs for the controls and the menu commands
enum
{
    // menu items
    MMBoard_Quit = 1,
    MMBoard_Open,
    MMBoard_About,
    MMBoard_PositionSlider,
    MMBoard_PlayButton,
    MMBoard_PauseButton,
    MMBoard_ResumeButton,
    MMBoard_StopButton,
    MMBoard_EjectButton,
    MMBoard_RefreshInfo
};

// ----------------------------------------------------------------------------
// event tables and other macros for wxWindows
// ----------------------------------------------------------------------------

BEGIN_EVENT_TABLE(MMBoardFrame, wxFrame)
  EVT_MENU(MMBoard_Quit,  MMBoardFrame::OnQuit)
  EVT_MENU(MMBoard_About, MMBoardFrame::OnAbout)
  EVT_MENU(MMBoard_Open, MMBoardFrame::OnOpen)
  EVT_BUTTON(MMBoard_PlayButton, MMBoardFrame::OnPlay)
  EVT_BUTTON(MMBoard_StopButton, MMBoardFrame::OnStop)
  EVT_BUTTON(MMBoard_PauseButton, MMBoardFrame::OnPause)
  EVT_BUTTON(MMBoard_EjectButton, MMBoardFrame::OnEject)  
  EVT_SLIDER(MMBoard_PositionSlider, MMBoardFrame::OnSetPosition)
  EVT_CUSTOM(wxEVT_TIMER, MMBoard_RefreshInfo, MMBoardFrame::OnRefreshInfo)
END_EVENT_TABLE()

// ---------------------------------------------------------------------------
// Main board application launcher
// ---------------------------------------------------------------------------

IMPLEMENT_APP(MMBoardApp)

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------

bool MMBoardApp::OnInit()
{
    // create the main application window
    MMBoardFrame *frame = new MMBoardFrame("Multimedia Board",
                                 wxPoint(50, 50), wxSize(450, 340));

    // and show it (the frames, unlike simple controls, are not shown when
    // created initially)
    frame->Show(TRUE);

    m_caps = TestMultimediaCaps();

    if (!m_caps) {
      wxMessageBox("Your system has no multimedia capabilities. We are exiting now.", "Major error !", wxOK | wxICON_ERROR, NULL);
      return FALSE;
    }

    wxString msg;
    msg.Printf("Detected : %s%s%s", (m_caps & MM_SOUND_OSS) ? "OSS " : "",
	                            (m_caps & MM_SOUND_ESD) ? "ESD " : "",
	                            (m_caps & MM_SOUND_WIN) ? "WIN" : "");

    wxMessageBox(msg, "Good !", wxOK | wxICON_INFORMATION, NULL);

    // success: wxApp::OnRun() will be called which will enter the main message
    // loop and the application will run. If we returned FALSE here, the
    // application would exit immediately.
    return TRUE;
}

wxUint8 MMBoardApp::TestMultimediaCaps()
{
    wxSoundStream *dev;
    wxUint8 caps;
    
    caps = 0;
    
#ifdef __UNIX__
    // We now test the ESD support
    
    dev = new wxSoundStreamESD();
    if (dev->GetError() == wxSOUND_NOERROR) 
        caps |= MM_SOUND_ESD;
    delete dev;
    
    // We test the OSS (Open Sound System) support.
    // WARNING: There is a conflict between ESD and ALSA. We may be interrested
    // in disabling the auto detection of OSS is ESD has been detected.
#if 1
    if (!(caps & MM_SOUND_ESD)) {
#endif

    dev = new wxSoundStreamOSS();
    if (dev->GetError() == wxSOUND_NOERROR)
        caps |= MM_SOUND_OSS;
    delete dev;
#if 1
    }
#endif

#endif
    
#ifdef __WIN32__
    // We test the Windows sound support.

    dev = new wxSoundStreamWin();
    if (dev->GetError() == wxSOUND_NOERROR)
        caps |= MM_SOUND_WIN;
    delete dev;
#endif
    
    return caps;
}

// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------

// frame constructor
MMBoardFrame::MMBoardFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
       : wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
#ifdef __WXMAC__
    // we need this in order to allow the about menu relocation, since ABOUT is
    // not the default id of the about menu
    wxApp::s_macAboutMenuItemId = MMBoard_About;
#endif

    // set the frame icon
    SetIcon(wxICON(mondrian));

    // create a menu bar
    wxMenu *menuFile = new wxMenu(wxT(""), wxMENU_TEAROFF);

    // the "About" item should be in the help menu
    wxMenu *helpMenu = new wxMenu;
    helpMenu->Append(MMBoard_About, wxT("&About...\tCtrl-A"), wxT("Show about dialog"));

    menuFile->Append(MMBoard_Open, wxT("&Open\tAlt-O"), wxT("Open file"));
    menuFile->AppendSeparator();
    menuFile->Append(MMBoard_Quit, wxT("E&xit\tAlt-X"), wxT("Quit this program"));

    // now append the freshly created menu to the menu bar...
    wxMenuBar *menuBar = new wxMenuBar();
    menuBar->Append(menuFile, wxT("&File"));
    menuBar->Append(helpMenu, wxT("&Help"));

    // ... and attach this menu bar to the frame
    SetMenuBar(menuBar);

#if wxUSE_STATUSBAR
    // create a status bar just for fun (by default with 1 pane only)
    CreateStatusBar(3);
    SetStatusText(wxT("Welcome to wxWindows!"));
#endif // wxUSE_STATUSBAR

    // Misc variables
    m_opened_file = NULL;

    m_panel = new wxPanel(this, -1);

    // Initialize main slider
    m_positionSlider = new wxSlider( m_panel, MMBoard_PositionSlider, 0, 0, 60,
				     wxDefaultPosition, wxSize(300, -1),
				     wxSL_HORIZONTAL | wxSL_AUTOTICKS);
    m_positionSlider->SetPageSize(60);  // 60 secs
    m_positionSlider->Enable(FALSE);
    
    // Initialize info panel
    wxPanel *infoPanel = new wxPanel( m_panel, -1);
    infoPanel->SetBackgroundColour(*wxBLACK);
    infoPanel->SetForegroundColour(*wxWHITE);

    wxBoxSizer *infoSizer = new wxBoxSizer(wxVERTICAL);

    m_fileType = new wxStaticText(infoPanel, -1, wxT(""));
    wxStaticLine *line = new wxStaticLine(infoPanel, -1);
    m_infoText = new wxStaticText(infoPanel, -1, "");

    UpdateInfoText();

    infoSizer->Add(m_fileType, 0, wxGROW | wxALL, 1);
    infoSizer->Add(line, 0, wxGROW | wxCENTRE, 20);
    infoSizer->Add(m_infoText, 0, wxGROW | wxALL, 1);
 
    infoPanel->SetSizer(infoSizer);
    infoPanel->SetAutoLayout(TRUE);

    // Bitmap button panel
    wxBoxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
    
    wxBitmap play_bmp(play_back_xpm);
    wxBitmap stop_bmp(stop_back_xpm);
    wxBitmap eject_bmp(eject_xpm);
    wxBitmap pause_bmp(pause_xpm);

    m_playButton = new wxBitmapButton(m_panel, MMBoard_PlayButton, play_bmp);
    m_playButton->Enable(FALSE);
    m_pauseButton = new wxBitmapButton(m_panel, MMBoard_PauseButton, pause_bmp);
    m_pauseButton->Enable(FALSE);
    m_stopButton = new wxBitmapButton(m_panel, MMBoard_StopButton, stop_bmp);
    m_stopButton->Enable(FALSE);
    m_ejectButton = new wxBitmapButton(m_panel, MMBoard_EjectButton, eject_bmp);
    m_ejectButton->Enable(FALSE);
    
    buttonSizer->Add(m_playButton, 0, wxALL, 2);
    buttonSizer->Add(m_pauseButton, 0, wxALL, 2);  
    buttonSizer->Add(m_stopButton, 0, wxALL, 2);
    buttonSizer->Add(m_ejectButton, 0, wxALL, 2);

    // Top sizer
    m_sizer = new wxBoxSizer(wxVERTICAL);
    m_sizer->Add(new wxStaticLine(m_panel, -1), 0, wxGROW | wxCENTRE, 0);
    m_sizer->Add(m_positionSlider, 0, wxCENTRE | wxGROW | wxALL, 2);
    m_sizer->Add(new wxStaticLine(m_panel, -1), 0, wxGROW | wxCENTRE, 0);
    m_sizer->Add(buttonSizer, 0, wxALL, 0);
    m_sizer->Add(new wxStaticLine(m_panel, -1), 0, wxGROW | wxCENTRE, 0);
    m_sizer->Add(infoPanel, 1, wxCENTRE | wxGROW, 0);
    
    m_panel->SetSizer(m_sizer);
    m_panel->SetAutoLayout(TRUE);
    m_sizer->Fit(this);
    m_sizer->SetSizeHints(this);

    // Timer
    m_refreshTimer = new wxTimer(this, MMBoard_RefreshInfo);

    // Video window
    m_video_window = NULL;

    // Multimedia file
    m_opened_file = NULL;
}

MMBoardFrame::~MMBoardFrame()
{
    if (m_opened_file)
        delete m_opened_file;
    
    delete m_refreshTimer;
}

void MMBoardFrame::OpenVideoWindow()
{
  if (m_video_window)
    return;

  m_video_window = new wxWindow(m_panel, -1, wxDefaultPosition, wxSize(200, 200));
  m_video_window->SetBackgroundColour(*wxBLACK);
  m_sizer->Prepend(m_video_window, 2, wxGROW | wxSHRINK | wxCENTRE, 1);

  m_sizer->Fit(this);
}

void MMBoardFrame::CloseVideoWindow()
{
    if (!m_video_window)
        return;
    
    m_sizer->Remove(m_video_window);
    delete m_video_window;
    m_video_window = NULL;
    
    m_sizer->Fit(this);
}

// event handlers

void MMBoardFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
    // TRUE is to force the frame to close
    Close(TRUE);
}

void MMBoardFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxString msg;
    msg.Printf( wxT("wxWindows Multimedia board v1.0a, wxMMedia v2.0a:\n")
                wxT("an example of the capabilities of the wxWindows multimedia classes.\n")
		wxT("Copyright 1999, 2000, Guilhem Lavaux.\n"));
 
    wxMessageBox(msg, "About MMBoard", wxOK | wxICON_INFORMATION, this);
}

void MMBoardFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
{
    wxString selected_file;
    
    if (m_opened_file) {
        if (!m_opened_file->IsStopped()) {
            wxCommandEvent event2;
            OnStop(event2);
        }
        delete m_opened_file;
    }
    
    // select a file to be opened
    selected_file = wxLoadFileSelector("multimedia", "*", NULL, this);
    if (selected_file.IsNull())
        return;
    
    m_opened_file = MMBoardManager::Open(selected_file);
    
    // Change the range values of the slider.
    MMBoardTime length;
    
    length = m_opened_file->GetLength();
    m_positionSlider->SetRange(0, length.hours * 3600 + length.minutes * 60 + length.seconds);
    
    // Update misc info
    UpdateMMedInfo();
    
    SetStatusText(selected_file, 2);
  
    // Update info text
    UpdateInfoText();
    
    // Enable a few buttons
    m_playButton->Enable(TRUE);
    m_ejectButton->Enable(TRUE);
    m_positionSlider->Enable(TRUE);
    
    if (m_opened_file->NeedWindow()) {
        OpenVideoWindow();
        m_opened_file->SetWindow(m_video_window);
    } else
        CloseVideoWindow();
}

void MMBoardFrame::UpdateInfoText()
{
    wxString infotext1, infotext2;
    
    if (m_opened_file) {
        infotext1 = wxT("File type:\n\t");
        infotext1 += m_opened_file->GetStringType() + wxT("\n");
        
        infotext2 = wxT("File informations:\n\n");
        infotext2 += m_opened_file->GetStringInformation();
    } else {
        infotext1 = wxT("File type: \n\tNo file opened");
        infotext2 = wxT("File informations:\nNo information\n\n\n\n\n");
    }
    
    m_fileType->SetLabel(infotext1);
    m_infoText->SetLabel(infotext2);
}

void MMBoardFrame::UpdateMMedInfo()
{
    wxString temp_string;
    MMBoardTime current, length;
    
    if (m_opened_file) {
        current = m_opened_file->GetPosition();
        length  = m_opened_file->GetLength();
    } else {
        current.hours = current.minutes = current.seconds = 0;
        length = current;
    }

    // We refresh the status bar
    temp_string.Printf(wxT("%02d:%02d / %02d:%02d"), current.hours * 60 + current.minutes,
                       current.seconds, length.hours * 60 + length.minutes, length.seconds);
    SetStatusText(temp_string, 1);
    
    // We set the slider position
    m_positionSlider->SetValue(current.hours * 3600 + current.minutes * 60 + current.seconds);
}

// ----------------------------------------------------------------------------
// Playing management, refreshers, ...

void MMBoardFrame::OnRefreshInfo(wxEvent& WXUNUSED(event))
{
    UpdateMMedInfo();
    
    if (m_opened_file->IsStopped()) {
        m_refreshTimer->Stop();
        m_playButton->Enable(TRUE);
        m_stopButton->Enable(FALSE);
        m_pauseButton->Enable(FALSE);
    }
}

void MMBoardFrame::OnPlay(wxCommandEvent& WXUNUSED(event))
{
    m_stopButton->Enable(TRUE);
    m_pauseButton->Enable(TRUE);
    m_playButton->Enable(FALSE);
    
    if (m_opened_file->IsPaused()) {
        m_opened_file->Resume();
        return;
    }
    
    m_refreshTimer->Start(1000, FALSE);
    
    m_opened_file->Play();
    
    m_stopButton->Enable(TRUE);
    m_pauseButton->Enable(TRUE);
    m_playButton->Enable(FALSE);
}

void MMBoardFrame::OnStop(wxCommandEvent& WXUNUSED(event))
{
    m_opened_file->Stop();
    m_refreshTimer->Stop();

    m_stopButton->Enable(FALSE);
    m_playButton->Enable(TRUE);
    
    UpdateMMedInfo();
}

void MMBoardFrame::OnPause(wxCommandEvent& WXUNUSED(event))
{
    m_opened_file->Pause();
    
    m_playButton->Enable(TRUE);
    m_pauseButton->Enable(FALSE);
}

void MMBoardFrame::OnEject(wxCommandEvent& WXUNUSED(event))
{
    m_opened_file->Stop();

    delete m_opened_file;
    m_opened_file = NULL;
    
    m_playButton->Enable(FALSE);
    m_pauseButton->Enable(FALSE);
    m_stopButton->Enable(FALSE);
    m_ejectButton->Enable(FALSE);
    m_positionSlider->Enable(FALSE);

    UpdateInfoText();
    UpdateMMedInfo();
}

void MMBoardFrame::OnSetPosition(wxCommandEvent& WXUNUSED(event))
{
    wxUint32 itime;
    MMBoardTime btime;
    
    itime = m_positionSlider->GetValue();
    btime.seconds = itime % 60;
    btime.minutes = (itime / 60) % 60;
    btime.hours = itime / 3600;
    m_opened_file->SetPosition(btime);

    UpdateMMedInfo();
}

