#include "cmodule.h"
#include "downloader.h"
#include "effects.h"
#include "effectview.h"
#include "engine.h"
#include "equalizer.h"
#include "equalizerview.h"
#include "vequalizer.h"
#include "app.h"
#include "playlist.h"
#include "pref.h"
#include "player.h"
#include "plugin.h"
#include "pluginloader.h"
#include "globalvideo.h"
#include "stdaction.h"

#include <common.h>
#include <dcopobject.h>
#include <dispatcher.h>
#include <tdeaboutdata.h>
#include <tdecmdlineargs.h>
#include <tdeconfig.h>
#include <kdebug.h>
#include <tdefiledialog.h>
#include <tdeglobal.h>
#include <klibloader.h>
#include <tdelocale.h>
#include <tdemainwindow.h>
#include <tdemessagebox.h>
#include <knotifyclient.h>
#include <tdepopupmenu.h>
#include <tqfile.h>
#include <tqimage.h>
#include <qiomanager.h>
#include <tqsessionmanager.h>
#include <tqtextstream.h>
#include <signal.h>
#include <kmimetype.h>


using std::string;
using std::vector;

// forgot the d pointer in Player TODO
static GlobalVideo *globalVideo;
struct NoatunApp::Private
{
	Effects *effects;
	VEqualizer *vequalizer;
};


NoatunApp::NoatunApp()
	: KUniqueApplication(true, true, true), mPluginMenu(0), mPluginActionMenu(0), mEqualizer(0)
{
	d = new Private;
	d->vequalizer=0;
	d->effects=0;

	Visualization::internalVis=true;

	mDownloader=new Downloader;

	Visualization::initDispatcher(); // 316

	showingInterfaces = true;

	// set the default config data
	// TODO: Maybe a first time wizard instead?
	TDEConfig *config=TDEGlobal::config(); // +
	config->setGroup(TQString()); // 1
	if (!config->readEntry("Modules").length())
	{
		TQStringList modules;
		modules.append("excellent.plugin");
		modules.append("splitplaylist.plugin");
		modules.append("marquis.plugin");
		modules.append("systray.plugin");
		modules.append("metatag.plugin");
		config->writeEntry("Modules", modules);
		config->sync();
	} // 1

	mPref=new NoatunPreferences(0L); // 115
	mPref->hide(); // 1
	mLibraryLoader = new LibraryLoader; // 0

	mLibraryLoader->add("dcopiface.plugin");

	new General(TQT_TQOBJECT(this)); // 25
	new Plugins(TQT_TQOBJECT(this)); // 149
//	new Types(TQT_TQOBJECT(this));

	mPlayer=new Player; // 139
	d->effects=new Effects; // 1
	d->vequalizer = new VEqualizer;
	d->vequalizer->init();
	mEqualizer=new Equalizer; // 0
	mEqualizer->init();  // 41
	mEffectView=new EffectView; // 859
	mEqualizerView=new EqualizerView; // 24

	TQTimer::singleShot(0, mDownloader, TQT_SLOT(start()));

	::globalVideo = new GlobalVideo;

	if(isRestored())
	{
		mLibraryLoader->add("marquis.plugin");
		static_cast<SessionManagement*>(mLibraryLoader->plugins().first())->restore();
	}
	else
	{
		loadPlugins(); //  1531
		if (!playlist())
		{
			KMessageBox::error(0,i18n("No playlist plugin was found. " \
				"Please make sure that Noatun was installed correctly."));
			TDEApplication::quit();
			delete this;
		}
		else
		{
			config->setGroup(TQString()); // 0
			player()->setVolume(config->readNumEntry("Volume", 100)); // 10
			player()->loop(config->readNumEntry("LoopStyle", (int)Player::None));
			mPlayer->engine()->setInitialized(); // 0

			switch (startupPlayMode())
			{
			case Restore:
				restoreEngineState();
				break;
			case Play:
				player()->play();
				break;
			case DontPlay:
			default:
				break;
			}
		}
	}
}

NoatunApp::~NoatunApp()
{
	saveEngineState();
	TDEConfig *config = TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("Volume", player()->volume());
	config->writeEntry("LoopStyle", player()->loopStyle());
	// for version continuity in the future
	config->writeEntry("Version", TQString(version())); // 1
	config->sync(); // 40

	mPlayer->stop();
	delete ::globalVideo;
	delete mLibraryLoader;
	delete mEffectView;
	delete mDownloader;
	delete mEqualizer;
	delete d->vequalizer;
	delete mEqualizerView;
	delete d->effects;

	delete mPlayer;
	delete mPref;
	config->sync();

	delete d;
}

TQCString NoatunApp::version() const
{
	return aboutData()->version().ascii();
}

inline bool logicalXOR(bool A, bool B)
{
	return (A || B) && !(A && B);
}

#define READBOOLOPT_EX(name, string, def, group, reversal) \
bool NoatunApp::name() const \
{ \
	TDEConfig *config=TDEGlobal::config(); \
	config->setGroup(group); \
	return logicalXOR(config->readBoolEntry(string, def), reversal); \
}

#define READBOOLOPT(name, string, def) \
	READBOOLOPT_EX(name, string, def, "", false)


READBOOLOPT(autoPlay, "AutoPlay", false)
READBOOLOPT(loopList, "LoopList", true)
READBOOLOPT(hackUpPlaylist, "HackUpPlaylist", true)
READBOOLOPT(fastMixer, "FastMixer", false)
READBOOLOPT(displayRemaining, "DisplayRemaining", false)
READBOOLOPT(clearOnOpen, "ClearOnOpen", false)
READBOOLOPT_EX(oneInstance, "MultipleInstances", true, "KDE", true)

#undef READBOOLOPT
#undef READBOOLOPT_EX

bool NoatunApp::clearOnStart() const
{
	return clearOnOpen();
}

int NoatunApp::startupPlayMode() const
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	return config->readNumEntry("StartupPlayMode", autoPlay() ? Play : Restore);
}

void NoatunApp::setStartupPlayMode(int mode)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("StartupPlayMode", mode);
	config->sync();
}

void NoatunApp::setHackUpPlaylist(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("HackUpPlaylist", b);
	config->sync();
}

void NoatunApp::setFastMixer(bool b)
{
	bool whatBefore=fastMixer();
	if (whatBefore!=b)
	{
		TDEConfig *config=TDEGlobal::config();
		config->setGroup(TQString());
		config->writeEntry("FastMixer", b);
		config->sync();
		player()->engine()->useHardwareMixer(b);
	}
}

void NoatunApp::setOneInstance(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup("KDE");
	config->writeEntry("MultipleInstances", !b);
	config->sync();
}

void NoatunApp::setLoopList(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("LoopList", b);
	TDEGlobal::config()->sync();
}

void NoatunApp::setAutoPlay(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("AutoPlay", b);
	TDEGlobal::config()->sync();
}

void NoatunApp::setRememberPositions(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("RememberPositions", b);
	TDEGlobal::config()->sync();
}

void NoatunApp::setSaveDirectory(const TQString &s)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writePathEntry("SaveDirectory", s);
	config->sync();
}

TQString NoatunApp::saveDirectory() const
{
	TDEConfig *c=TDEGlobal::config();
	c->setGroup(TQString());
	return c->readPathEntry("SaveDirectory", TQString(getenv("HOME")));
}

TQString NoatunApp::titleFormat() const
{
	TDEConfig *c=TDEGlobal::config();
	c->setGroup(TQString());
	return c->readEntry("TitleFormat", "$(\"[\"author\"] - \")$(title)$(\" (\"bitrate\"kbps)\")");
}

void NoatunApp::setTitleFormat(const TQString &format)
{
	TDEConfig *c=TDEGlobal::config();
	c->setGroup(TQString());
	return c->writeEntry("TitleFormat", format);
}

void NoatunApp::setClearOnStart(bool b)
{
	setClearOnOpen(b);
}

void NoatunApp::setClearOnOpen(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("ClearOnOpen", b);
	config->sync();
}

void NoatunApp::setDisplayRemaining(bool b)
{
	TDEConfig *config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("DisplayRemaining", b);
	config->sync();
}

void NoatunApp::toggleInterfaces()
{
	showingInterfaces ^= true;

	if(showingInterfaces)
		emit showYourself();
	else
		emit hideYourself();
}

void NoatunApp::showInterfaces()
{
	showingInterfaces = true;
	emit showYourself();
}

void NoatunApp::hideInterfaces()
{
	showingInterfaces = false;
	emit hideYourself();
}


int NoatunApp::pluginMenuAdd(const TQString &text, const TQObject *receiver, const char *member)
{
	return pluginActionMenu()->menuAdd(text, receiver, member);
}

void NoatunApp::pluginMenuRemove(int id)
{
	pluginActionMenu()->menuRemove(id);
}

NoatunStdAction::PluginActionMenu *NoatunApp::pluginActionMenu()
{
	if (!mPluginActionMenu)
		mPluginActionMenu = new NoatunStdAction::PluginActionMenu(TQT_TQOBJECT(this), "menu_actions");
	return mPluginActionMenu;
}

TDEPopupMenu *NoatunApp::pluginMenu()
{
	if(!mPluginMenu)
		mPluginMenu = pluginActionMenu()->popupMenu();
	return mPluginMenu;
}

int NoatunApp::newInstance()
{
	// TODO, this should call playlist()->handleArguments();
	TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
	bool clear = clearOnOpen();
	bool playme=true;

	for (int i=0; i<args->count(); i++)
	{
		player()->openFile(args->url(i), clear, playme);
		playme=false;
		clear=false;
	}

	args->clear();
	return 0;
}

void NoatunApp::preferences()
{
	mPref->show();
	mPref->raise();
}

void NoatunApp::quit()
{
	TDEApplication::quit();
}

void NoatunApp::fileOpen()
{
	KURL::List files = KFileDialog::getOpenURLs(":mediadir", mimeTypes(), 0,
		i18n("Select File to Play"));

	if (files.count())
		mPlayer->openFile(files, clearOnOpen(), true);
}

void NoatunApp::effectView()
{
	mEffectView->show();
}

void NoatunApp::equalizerView()
{
	mEqualizerView->show();
}

VEqualizer * NoatunApp::vequalizer()
{
	return d->vequalizer;
}

Effects *NoatunApp::effects() const
{
	return d->effects;
}

TQString NoatunApp::mimeTypes()
{
	TQString mimeTypes;
	Arts::TraderQuery q;
	vector<Arts::TraderOffer> *results = q.query();
	vector<Arts::TraderOffer>::iterator i;

	for (i=results->begin(); i != results->end(); i++)
	{
		vector<string> *prop = (*i).getProperty("MimeType");

		vector<string>::iterator istr;
		for (istr = prop->begin(); istr != prop->end(); istr++)
		{
			if ( !(*istr).length())
				continue;

			const char *m = (*istr).c_str();
			if ((KServiceType::serviceType(m)) && !mimeTypes.contains(m))
			{
				mimeTypes += m;
				mimeTypes += ' ';
			}
		}
		delete prop;
	}
	delete results;
	return mimeTypes;
}

void NoatunApp::loadPlugins()
{
	mLibraryLoader->loadAll();
	// backup in case of trouble
	if(!mLibraryLoader->playlist())
	{
		kdDebug(66666) << k_funcinfo << "NO playlist plugin found!" << endl;
		//mLibraryLoader->add("splitplaylist.plugin");
	}
}

Playlist *NoatunApp::playlist() const
{
	return mLibraryLoader->playlist();
}

void NoatunApp::commitData(TQSessionManager &)
{
}

void NoatunApp::saveState(TQSessionManager &sm)
{
	TQStringList restartCommand = sm.restartCommand();
	sm.setRestartCommand( restartCommand );

	TDEApplication::saveState(sm);
}

// Deprecated
TQImage NoatunApp::readPNG(const TQString &filename)
{
	TQImageIO file(filename, "PNG");
	file.setGamma(0.0);
	file.read();
	return file.image();
}

void NoatunApp::restoreEngineState()
{
	TDEConfig* config = TDEGlobal::config();
	config->setGroup(TQString());
	int state = config->readNumEntry("EngineState", Arts::posPlaying);
	switch (state)
	{
		case Arts::posPlaying:
			player()->play();
			break;
		case Arts::posPaused:
			if (player()->isPlaying())
				player()->playpause();
			break;
		case Arts::posIdle:
		default:
			break;
	}
}

void NoatunApp::saveEngineState()
{
	TDEConfig* config=TDEGlobal::config();
	config->setGroup(TQString());
	config->writeEntry("EngineState", player()->engine()->state());
	// we don't sync here since it's done in the destructor afterwards anyway
}

#include "app.moc"
