#include "engine.h"
#include <string.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kstddirs.h>
#include <kconfig.h>
#include <qfile.h>
#include <qdir.h>
#include "plugin.h"
#include "effects.h"
#include "noatunplaylist.h"
#include <sys/wait.h>

#include <dynamicrequest.h>
#include <soundserver.h>
#include <kmedia2.h>
#include <flowsystem.h>
#include "Equalizer.h"
#include "StereoEffectStack_Noatun.h"
#include "StereoVolumeControl_Noatun.h"
#include <connect.h>

using namespace std;

class Engine::EnginePrivate
{
public:
	EnginePrivate()
		: playobj(Arts::PlayObject::null()),
		  server(Arts::SimpleSoundServer::null()),
		  globalEffectStack(Arts::StereoEffectStack_Noatun::null()),
		  effectsStack(Arts::StereoEffectStack_Noatun::null()),
		  visStack(Arts::StereoEffectStack_Noatun::null()),
		  volumeControl(Arts::StereoVolumeControl_Noatun::null())
		{}
	Arts::PlayObject playobj;
	Arts::SimpleSoundServer server;
	Arts::Synth_AMAN_PLAY amanPlay;

	// globalEffectStack
	//  |- effectsStack
	//      |- Effects...
	//  |- visStack
	//      |- Visualizations
	//  |- Volume Control
	//
	Arts::StereoEffectStack_Noatun globalEffectStack;
	Arts::StereoEffectStack_Noatun effectsStack;
	Arts::StereoEffectStack_Noatun visStack;
	Arts::Equalizer equalizer;

	int volumeID;
	Arts::StereoVolumeControl_Noatun volumeControl;
};

Arts::SimpleSoundServer *Engine::server() const              { return &d->server;}
Arts::PlayObject *Engine::playObject() const                 { return &d->playobj; }
Arts::SimpleSoundServer *Engine::simpleSoundServer() const   { return &d->server; }
Arts::StereoEffectStack_Noatun *Engine::effectStack() const  { return &d->effectsStack; }
Arts::StereoEffectStack_Noatun *Engine::visualizationStack() const { return &d->visStack; }
Arts::Equalizer *Engine::equalizer() const { return &d->equalizer; }

static Arts::StereoVolumeControl_Noatun createVolumeControl(Arts::SimpleSoundServer &server, Arts::StereoEffectStack_Noatun &es, int &id)
{
	Arts::StereoVolumeControl_Noatun result;

	result = Arts::DynamicCast(server.createObject("Arts::StereoVolumeControl_Noatun"));
	result.start();

	id=es.insertBottom(result,"Volume Control");

	return result;
}

Engine::Engine(QObject *parent) : QObject(parent), mPlay(false)
{
	d=new EnginePrivate;
	// Connect to aRts
        if (!initArts())
	{
		KMessageBox::error(0, i18n("There was an error communicating to the aRts daemon."), i18n("Danger, Will Robinson!"));
		exit(0);
	}
//	mStereoEffect = DynamicCast(server.createObject("Arts::ExtraStereo"));
//	mStereoEffect.start();


}

Engine::~Engine()
{
	stop();
	d->server=Arts::SimpleSoundServer::null();
	delete d;
}

static string getObjectType(const PlayListItem *file)
{
	string objectType;

	Arts::TraderQuery query;
	query.supports("Interface","Arts::PlayObject");
	query.supports("MimeType", std::string(file->mimetype()));

	vector<Arts::TraderOffer> *offers = query.query();
	if(!offers->empty())
		objectType = offers->front().interfaceName();

	// prefer WAVPlayObject when available (don't cache wavs)
	vector<Arts::TraderOffer>::iterator i;
	for(i = offers->begin(); i != offers->end(); i++)
		if(i->interfaceName() == "WAVPlayObject")
			objectType = i->interfaceName();

	delete offers;

	return objectType;
}

void Engine::setInitialized()
{
	mPlay=true;
}

bool Engine::play(const PlayListItem *file)
{
//        if (!initArts()) return false;

	string objectType=getObjectType(file);
	if (!objectType.length())
		return false;

	d->playobj = Arts::DynamicCast(d->server.createObject(objectType));
	if(!d->playobj.loadMedia(file->file().ascii()))
		return false;

	d->playobj._node()->start();

	// TODO: check for existence of left & right streams
	Arts::connect(d->playobj,"left",d->globalEffectStack,"inleft");
	Arts::connect(d->playobj,"right",d->globalEffectStack,"inright");

	bool res = play();

	return res;
}

bool Engine::play()
{
	if (!mPlay) return true;
	if(d->playobj.isNull())
		return false;
	d->playobj.play();
	return true;
}

void Engine::pause()
{
	d->playobj.pause();
}

void Engine::stop()
{
	if(d->playobj.isNull()) return;

	d->playobj.pause();
	d->playobj=Arts::PlayObject::null();
//	advplayobject=AdvancedPlayObject::null();
}

void Engine::seek(int newTime)
{
	if (d->playobj.isNull()) return;

	Arts::poTime t;
	t.seconds=newTime;
	if (!d->playobj.isNull())
		d->playobj.seek(t);
}

int Engine::position()
{
	if (d->playobj.isNull()) return -1;

	Arts::poTime time(d->playobj.currentTime());
	while (time.ms<0)
	{
		time.ms+=1000;
		time.seconds--;
	}
	int pos=time.seconds;
	if (time.ms>500)
		pos++;
	return pos;
}

int Engine::length()
{
	if (d->playobj.isNull()) return 0;

	Arts::poTime time(d->playobj.overallTime());
	while (time.ms<0)
	{
		time.ms+=1000;
		time.seconds--;
	}
	int pos=time.seconds;
	if (time.ms>500)
		pos++;
	return pos;
}

int Engine::state()
{
	if(!d->playobj.isNull())
		return d->playobj.state();
	else
		return Arts::posIdle;
}

void Engine::setVolume(int percent)
{
	if (percent>100)
		percent=100;
	if (percent<0)
		percent=0;
	if (!d->volumeControl.isNull())
		d->volumeControl.percent((long)percent);
}

int Engine::volume() const
{
	if (!d->volumeControl.isNull())
		return (int)d->volumeControl.percent();
	return 100;
}

bool Engine::initArts()
{
	if ( d->server.isNull() || d->server.error()  )
	{
		d->server = Arts::Reference("global:Arts_SimpleSoundServer");

		if( d->server.isNull() || d->server.error() )
		{
		// aRts seems not to be running, let's try to run it
		// First, let's read the configuration as in kcmarts
			KConfig *config = new KConfig("kcmartsrc");
			QCString cmdline;

			config->setGroup("Arts");

			bool rt = config->readBoolEntry("StartRealTime",false);
			bool x11Comm = config->readBoolEntry("X11GlobalComm",false);

		/* put the value of x11Comm into .mcoprc */
			KConfig *X11CommConfig = new KConfig(QDir::homeDirPath()+"/.mcoprc");

			if(x11Comm)
				X11CommConfig->writeEntry("GlobalComm",
					"Arts::X11GlobalComm");
			else
				X11CommConfig->writeEntry("GlobalComm",
					"Arts::TmpGlobalComm");

			X11CommConfig->sync();
			delete X11CommConfig;

			cmdline = QFile::encodeName(KStandardDirs::findExe(QString::fromLatin1("kdeinit_wrapper")));
			cmdline += " ";

			if (rt)
				cmdline += QFile::encodeName(KStandardDirs::findExe(
				QString::fromLatin1("artswrapper")));
			else
				cmdline += QFile::encodeName(KStandardDirs::findExe(
				QString::fromLatin1("artsd")));

			cmdline += " ";
			cmdline += config->readEntry("Arguments", "-F 5 -S 8192").utf8();

			int status=system(cmdline);

			if ( status!=-1 && WIFEXITED(status) )
			{
			// We could have a race-condition here.
			// The correct way to do it is to make artsd fork-and-exit
			// after starting to listen to connections (and running artsd
			// directly instead of using kdeinit), but this is better
			// than nothing.
				int time = 0;
				do
				{
					sleep(1);
					d->server = Arts::Reference("global:Arts_SimpleSoundServer");
				} while(++time < 5 && (d->server.isNull()));
			}
		}

		if ( !d->server.isNull() )
		{
			d->amanPlay = Arts::DynamicCast(
				d->server.createObject("Arts::Synth_AMAN_PLAY"));
			d->amanPlay.title("noatun");
			d->amanPlay.autoRestoreID("noatun");
			d->amanPlay.start();

			d->globalEffectStack=Arts::DynamicCast(
			d->server.createObject("Arts::StereoEffectStack_Noatun"));;
			d->globalEffectStack.start();
			Arts::connect(d->globalEffectStack,d->amanPlay);

			d->effectsStack=Arts::DynamicCast(
				d->server.createObject("Arts::StereoEffectStack_Noatun"));
			d->effectsStack.start();
			d->globalEffectStack.insertBottom(d->effectsStack, "Effects Stack");

			d->equalizer=Arts::DynamicCast(d->server.createObject("Arts::Equalizer"));
			d->equalizer.start();
			d->globalEffectStack.insertBottom(d->equalizer, "Equalizer");

			d->visStack=Arts::DynamicCast(
				d->server.createObject("Arts::StereoEffectStack_Noatun"));
			d->visStack.start();
			d->globalEffectStack.insertBottom(d->visStack, "Visualization Stack");

			d->volumeControl = createVolumeControl(d->server,
			d->globalEffectStack, d->volumeID);
		}
	}

	d->playobj = Arts::PlayObject::null();

	return true;
}


#include "engine.moc"

