/*
 *  This program 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.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef XSIDPLAY_PLAYER_H
#define XSIDPLAY_PLAYER_H

class ConfigFile;
class EmuWrapper;
class MainDialog;
class TimeLCD;
class Playlist;
class PlaylistDialog;
class Player;
class SidTuneMod;
class SidTuneWrapper;

#include <qobject.h>
#include <qstring.h>
#include <qthread.h>

#include "AudioConfig.h"
#include "Fading.h"
#include "PlaylistItem.h"
#include "songlendb/File.h"
#include "tools/MyTimer.h"

typedef void (Player::*ptr2PlayerThreadFunc)(QThread*);

class PlayerThread : public QThread
{
 public:
    void connectPlayer(Player*,ptr2PlayerThreadFunc);
    virtual void run();
    
 protected:
    Player* player;
    ptr2PlayerThreadFunc playFunc;
};

class Player : public QObject
{
    Q_OBJECT
        
 public:
    Player();
    ~Player();

    void shutdown();
    
    void readConfig(ConfigFile*);
    void writeConfig(ConfigFile*);
    
    void link(MainDialog*, TimeLCD*, PlaylistDialog*);
    bool init(const AudioConfig&);
    const QString& getErrorStr();
         
    void start();
    void pause(bool hard = false);
    void resume();
    void stop();

    void playFastForward();
    void playNormalSpeed();
    
    bool isReady() const;
    bool isPaused() const;
    bool isStopped() const;
    bool isPlaying() const;
    bool isForwarding() const;

    bool haveSidTune() const;
    bool havePrevSong();
    bool haveNextSong();
    
    void initSong(int);
    void initNextSong();
    void initPrevSong();
    
    const AudioConfig& getAudioConfig();
    
    SidTuneMod* getSidTune() const;
    SidTuneWrapper* getSidTuneWrapper() const;

    bool enableSongLengthDB(bool value = true);
        
    Playlist* getPlaylist() const;
    void setPlaylistParams(const PlaylistItem& item);
    const PlaylistItem& getCurPlaylistParams();
    void enablePlaylist(bool value = true);
    void enablePlaylistLoop(bool value = true);
    void enablePlaylistRand(bool value = true);

    // Thread-safe.
    EmuWrapper* getEmuEngine() const;
    
 protected:
    PlayerThread playThread;
    bool playJobActive;
    
    // Trying to make all the stuff thread-safe.
    QMutex audioDriverMutex;

    MyTimer displayTimer;

    EmuWrapper* pEmuEngine;
    SidTuneWrapper* pSidTuneWrapper;

    AudioConfig myAudioConfig;
    
    SongLengthDB* pSongLengthDB;
    
    Playlist* pPlaylist;
    
    struct PlayerState
    {
        bool ready;      // whether all components are initialized 
                         // and the last operation on a sidtune has been
                         // successful
        bool playing;
        bool paused;
        bool forwarding;
        bool stopped;
    } state;

    bool endOfSong;

    bool init_main();
    
    bool openAudioDriver();
    void closeAudioDriver();
    void resetAudioDriver();
    void playAudio(void*, unsigned long int);

    void initAudioBufferSystem();
    bool openAudioBufferSystem();
    void freeAudioBufferSystem();

    void fillBuffer(unsigned char*, unsigned long int);

    void playJob(QThread*);
    void controlJob(QThread*);
    
    // Whether the player at the end of a song asks the playlist
    // dialog to activate a new song.
    bool playlistDriven;
    bool loopPlaylist;
    bool randPlaylist;
    PlaylistItem curPlaylistItem;
    
    bool driverIsOpen;

    FadingOut myFadeOut;

    MainDialog* myMainDlg;
    TimeLCD* myTimeLCD;
    PlaylistDialog* myPlaylistDlg;
    
    bool audioBuffersReady;
    int multiBufferSize;
    int currentBuffer;
    unsigned char* pBuffer[2];
    int bufferCount[2];

    QString errorString;
        
 public slots:
    void handleEndOfCurrentSong();
    bool playNextFromList(int delta);

 protected slots:
    void timeJob();

 signals:
    void playListItem(const PlaylistItem&);
    void playerPlayRequest(uint);
    void stopPlaylistPlay();
    void endOfCurrentSong();
    
 private:
    // prevent copying
    Player(const Player&);
    Player& operator=(Player&);  
};

#endif  /* XSIDPLAY_PLAYER_H */
