/*
 * SoundPlayer.cpp
 *
 *  Created on: 24.01.2012
 *      Author: stefan.detter
 */

#include "Sound.h"
#include "SoundPlayer.h"

#include <QSoundEffect>

#include <QrfeSleeper>

#define THREADING_ON        1

#if	USE_SOUND

SoundPlayer::SoundPlayer(QObject* parent)
	: QObject(parent)
/*	, QrfeTraceModule("SoundPlayer") */
{
	m_running = 0;

    m_parallelCount = 10;

	for (int i = 0; i < m_parallelCount; i++)
	{
		SoundPlayerWorker* worker = new SoundPlayerWorker(this);
		worker->start();
		m_threads.append(worker);
	}
}

SoundPlayer::~SoundPlayer()
{
#if THREADING_ON
	for (int i = 0; i < m_threads.size(); i++)
	{
		m_threads[i]->exitThread();
	}

	for (int i = 0; i < m_threads.size(); i++)
	{
		m_waitCondition.wakeOne();
		while (true)
		{
			m_runningMutex.lock();
			int r = m_running;
			m_runningMutex.unlock();

			if (r == (m_threads.size() - i - 1))
				break;

			QrfeSleeper::MSleep(10);
		}
	}

	for (int i = 0; i < m_threads.size(); i++)
	{
		delete m_threads[i];
	}
#endif
}

bool SoundPlayer::setFiles ( QStringList files )
{
	m_soundInfos.clear();

	for(int i = 0; i < files.size(); i++)
	{
		SOUND_INFO soundInfo;
		m_soundInfos.append(soundInfo);

		QFile* inputFile = new QFile();
		inputFile->setFileName(files.at(i));
		if(!inputFile->open(QIODevice::ReadOnly))
		{
			/* error("Could not open file: " + files.at(i)); */
			delete inputFile;

			continue;
		}

		SoundFormat soundFormat(inputFile);

		QAudioFormat format = soundFormat.format();
		int 		 offset = soundFormat.offset();

		QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
		if (!info.isFormatSupported(format))
		{
			/* error("Audio format not supported by backend, cannot play audio file: " + files.at(i)); */
			delete inputFile;

			continue;
		}

		m_soundInfos[i].fileName 	= files.at(i);
		m_soundInfos[i].format	 	= format;
		m_soundInfos[i].offset	 	= offset;
	}

	return true;
}

bool SoundPlayer::setParallelCount (uint count)
{
    if(count == 0)
		count = 1;
    if(count > 20)
		count = 20;

	if (count > m_parallelCount)
	{
		for (uint i = 0; i < count - m_parallelCount; i++)
		{
			SoundPlayerWorker* worker = new SoundPlayerWorker(this);
			worker->start();
			m_threads.append(worker);
		}
	}
	else if (count < m_parallelCount)
	{
		for (uint i = 0; i < m_parallelCount - count; i++)
		{
			m_threads.takeLast()->deleteLater();
		}
	}

    m_parallelCount = count;

	return true;
}

void SoundPlayer::play(int index)
{
#if THREADING_ON
	m_mutex.lock();
	m_index.push_back(index);
	m_mutex.unlock();
	m_waitCondition.wakeOne();
#else
    SOUND_INFO info = m_soundInfos.at(index);
    QSound::play(info.fileName);
#endif
}


QMutex SoundPlayerWorker::m_lastPlayedMutex;
QTime SoundPlayerWorker::m_lastPlayed;

SoundPlayerWorker::SoundPlayerWorker(SoundPlayer* parent)
	: QThread(parent)
	/*	, QrfeTraceModule("SoundPlayer") */
{
	m_parent = parent;
	m_exitThread = false;
	m_running = false;

    m_lastPlayedMutex.lock();
    m_lastPlayed.start();
    m_lastPlayedMutex.unlock();

	start(QThread::HighestPriority);
}

SoundPlayerWorker::~SoundPlayerWorker()
{
	while (m_running)
		msleep(10);
}

void SoundPlayerWorker::exitThread()
{
	m_exitThread = true;
}

void SoundPlayerWorker::run()
{
	m_running = true;

	m_parent->m_runningMutex.lock();
	m_parent->m_running++;
	m_parent->m_runningMutex.unlock();

	SoundPlayerPrivate* player = new SoundPlayerPrivate(m_parent);
	connect(player, SIGNAL(finished()), this, SLOT(quit()));

	while (true)
	{
		int index = -1;

		m_waitMutex.lock();
		m_parent->m_waitCondition.wait(&m_waitMutex);
        m_waitMutex.unlock();

        if (m_exitThread)
			break;

		m_parent->m_mutex.lock();
		if (m_parent->m_index.size() > 0)
			index = m_parent->m_index.takeFirst();
		m_parent->m_mutex.unlock();

		if (index >= 0 && player != 0) {

            m_lastPlayedMutex.lock();
            int elapsed = m_lastPlayed.elapsed();
            if(elapsed < 50)
                msleep(50 - elapsed);
            m_lastPlayed.restart();
            m_lastPlayedMutex.unlock();

			player->play(index);
			exec();
		}
	}

	delete player;
	player = 0;

	m_running = false;

	m_parent->m_runningMutex.lock();
	m_parent->m_running--;
	m_parent->m_runningMutex.unlock();
}




#define USE_QSOUND   1

SoundPlayerPrivate::SoundPlayerPrivate(SoundPlayer* parent)
    : QObject(0)
/*	, QrfeTraceModule("SoundPlayerPrivate") */
    , m_parent(parent)
{
	m_playing = false;
	connect(&m_sound, SIGNAL(playingChanged()), this, SLOT(playingChanged()));
}

SoundPlayerPrivate::~SoundPlayerPrivate()
{
}

void SoundPlayerPrivate::play ( int index )
{
    if(index < 0 || index >= m_parent->m_soundInfos.size() )
    {
        /* trc(0, "ret"); */
        return;
    }

    SOUND_INFO info = m_parent->m_soundInfos.at(index);

    if(m_parent->m_soundInfos.at(index).fileName.isNull() || m_audio.size() > (int)m_parent->m_parallelCount)
    {
        /* trc(0, "ret"); */
        return;
    }

    /* trc(0, "play"); */
#if USE_QSOUND
    //QSound::play(info.fileName);
    m_sound.setSource(QUrl::fromLocalFile(info.fileName));
    m_sound.play();
#else
    QFile* inputFile = new QFile();
    inputFile->setFileName(info.fileName);
    if(!inputFile->open(QIODevice::ReadOnly))
    {
        /* error("Could not open file"); */
        delete inputFile;
        return;
    }

    QAudioOutput* audio = new QAudioOutput(info.format);
    QObject::connect( audio,		SIGNAL(stateChanged(QAudio::State)),
                      this, 		  SLOT(_q_stateChanged(QAudio::State)), Qt::DirectConnection);

    m_audio.insert(audio, inputFile);

    inputFile->seek(info.offset);
    audio->start(inputFile);
#endif
}

//void SoundPlayerPrivate::_q_stateChanged ( QAudio::State state )
//{
//	if(state == QAudio::IdleState)
//	{
//		/* trc(0, "delete"); */
//		QAudioOutput* audio = (QAudioOutput*)sender();
//		audio->deleteLater();
//		m_audio.value(audio)->deleteLater();
//		m_audio.remove(audio);
//	}
//}

void SoundPlayerPrivate::playingChanged()
{
	if (m_playing == true && m_sound.isPlaying() == false)
		emit finished();

	m_playing = m_sound.isPlaying();
}

#endif
