/*
  Player Object for playing synthesized audio files.  Plays them
  synchronously.
  -------------------
  Copyright:
  (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
  -------------------
  Original author: Gary Cramblitt <garycramblitt@comcast.net>

  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ******************************************************************************/

// TQt includes.
#include <tqfile.h>

// KDE includes.
#include <tdeapplication.h>
#include <tdetempfile.h>
#include <tdestandarddirs.h>
#include <tdeparts/componentfactory.h>
#include <ktrader.h>
#include <kdebug.h>

// KTTS includes.
#include "player.h"
#include "stretcher.h"
#include "pluginconf.h"

// TestPlayer includes.
#include "testplayer.h"

/**
 * Constructor.
 */
TestPlayer::TestPlayer(TQObject *parent, const char *name,
    const int playerOption, const float audioStretchFactor, const TQString &sinkName) :
    TQObject(parent, name)
{
    m_playerOption = playerOption;
    m_audioStretchFactor = audioStretchFactor;
    m_stretcher = 0;
    m_player = 0;
    m_sinkName = sinkName;
}

/**
 * Destructor.
 */
TestPlayer::~TestPlayer()
{
    delete m_stretcher;
    delete m_player;
}

/**
 * Sets which audio player to use.
 *  0 = aRts
 *  1 = gstreamer
 *  2 = ALSA
 *  3 = aKode
 */
void TestPlayer::setPlayerOption(const int playerOption) { m_playerOption = playerOption; }

/**
 * Sets the audio stretch factor (Speed adjustment).
 * 1.0 = normal
 * 0.5 = twice as fast
 * 2.0 = twice as slow
 */
void TestPlayer::setAudioStretchFactor(const float audioStretchFactor)
    { m_audioStretchFactor = audioStretchFactor; }

void TestPlayer::setSinkName(const TQString &sinkName) { m_sinkName = sinkName; }

/**
 * Plays the specifified audio file and waits for completion.
 * The audio file speed is adjusted according to the stretch factor.
 * @param waveFile              Name of the audio file to play.
 */
void TestPlayer::play(const TQString &waveFile)
{
    // kdDebug() << "TestPlayer::play: running" << endl;
    // Create a Stretcher object to adjust the audio Speed.
    TQString playFile = waveFile;
    TQString tmpFile;
    if (m_audioStretchFactor != 1.0)
    {
        tmpFile = makeSuggestedFilename();
        // kdDebug() << "TestPlayer::play: stretching file " << playFile << " by " << m_audioStretchFactor
        //     << " to file " << tmpFile << endl;
        m_stretcher = new Stretcher();
        if (m_stretcher->stretch(playFile, tmpFile, m_audioStretchFactor))
        {
            while (m_stretcher->getState() != Stretcher::ssFinished) tqApp->processEvents();
            playFile = m_stretcher->getOutFilename();
        }
        delete m_stretcher;
        m_stretcher = 0;
    }

    // Create player object based on player option.
    // kdDebug() << "TestPlayer::play: creating Player object with playerOption " << m_playerOption << endl;
    m_player = createPlayerObject(m_playerOption);
    // If player object could not be created, avoid crash is the best we can do!
    if (!m_player) return;
    // kdDebug() << "TestPlayer::play: starting playback." << endl;
    m_player->startPlay(playFile);

    // TODO: The following hunk of code would ideally be unnecessary.  We would just
    // return at this point and let take care of
    // cleaning up the play object.  However, because we've been called from DCOP,
    // this seems to be necessary.  The call to processEvents is problematic because
    // it can cause re-entrancy.
    while (m_player->playing()) tqApp->processEvents();
    // kdDebug() << "TestPlayer::play: stopping playback." << endl;
    m_player->stop();
    delete m_player;
    m_player = 0;
    if (!tmpFile.isEmpty()) TQFile::remove(tmpFile);
}

/**
 * Creates and returns a player object based on user option.
 */
Player* TestPlayer::createPlayerObject(int playerOption)
{
    Player* player = 0;
    TQString plugInName;
    switch(playerOption)
    {
        case 1 :
        {
            plugInName = "kttsd_gstplugin";
            break;
        }
        case 2 :
        {
            plugInName = "kttsd_alsaplugin";
            break;
        }
        case 3 :
        {
            plugInName = "kttsd_akodeplugin";
            break;
        }
        default:
        {
            plugInName = "kttsd_artsplugin";
            break;
        }
    }
    TDETrader::OfferList offers = TDETrader::self()->query(
            "KTTSD/AudioPlugin", TQString("DesktopEntryName == '%1'").arg(plugInName));

    if(offers.count() == 1)
    {
        // kdDebug() << "TestPlayer::createPlayerObject: Loading " << offers[0]->library() << endl;
        KLibFactory *factory = KLibLoader::self()->factory(offers[0]->library().latin1());
        if (factory)
            player = 
                KParts::ComponentFactory::createInstanceFromLibrary<Player>(
                    offers[0]->library().latin1(), this, offers[0]->library().latin1());
        else
            kdDebug() << "TestPlayer::createPlayerObject: Could not create factory." << endl;
    } 
    if (player == 0)
        kdDebug() << "TestPlayer::createPlayerObject: Could not load " + plugInName +
            ".  Is TDEDIRS set correctly?" << endl;
    else
        // Must have GStreamer >= 0.8.7.
        if (playerOption == 1)
        {
            if (!player->requireVersion(0, 8, 7))
            {
                delete player;
                player = 0;
            }
        }
    if (player) player->setSinkName(m_sinkName);
    return player;
}

/**
 * Constructs a temporary filename for plugins to use as a suggested filename
 * for synthesis to write to.
 * @return                        Full pathname of suggested file.
 */
TQString TestPlayer::makeSuggestedFilename()
{
    KTempFile tempFile (locateLocal("tmp", "kttsmgr-"), ".wav");
    TQString waveFile = tempFile.file()->name();
    tempFile.close();
    TQFile::remove(waveFile);
    // kdDebug() << "TestPlayer::makeSuggestedFilename: Suggesting filename: " << waveFile << endl;
    return PlugInConf::realFilePath(waveFile);
}

#include "player.moc"
