/**
 * Copyright (C) 2005 Benjamin C Meyer (ben at meyerhome dot 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 library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include <unistd.h>

#include "mainwindow.h"
#include "jukquiz.h"
#include "options.h"
#include "settings.h"

#include <dcopclient.h>
#include <kstdaction.h>
#include <qlayout.h>
#include <qcstring.h>
#include <kapplication.h>
#include <qdatastream.h>
#include <dcopref.h>
#include <qbuttongroup.h>
#include <qprogressbar.h>
#include <qtimer.h>
#include <klocale.h>
#include <qlabel.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kconfigdialog.h>
#include <kconfigbase.h>
#include <kstatusbar.h>
#include <kiconloader.h>

#define SKIP_AHEAD 30
#define JUK "juk"

#define STATUSBAR_TIME_ID  1
#define STATUSBAR_GAMES_ID 2
#define STATUSBAR_WINS_ID  4

MainWindow::MainWindow(QWidget *parent, const char *name) : KMainWindow(parent,name) ,
									totalSongs(Settings::numberOfChoices()), interval(Settings::time()), numberOfGames(1)
{
	mWidget = new JukQuiz(this, "jukquiz");
	
	QByteArray data, replyData;
	QCString replyType;
	client = kapp->dcopClient();

	// Start up Juk if it isn't running
	if(!kapp->dcopClient()->isApplicationRegistered( JUK )){
		KApplication::startServiceByDesktopName( JUK );
		while(!kapp->dcopClient()->isApplicationRegistered( JUK ))
			sleep(1);
		client->call( JUK, "juk-mainwindow#1", "minimize()",
		  data, replyType, replyData );
	}

	if ( client->call( JUK, "Collection", "playlists()",
		  data, replyType, replyData ) ) {
		if ( replyType == "QStringList" ) {
		 	QStringList result;
			QDataStream reply( replyData, IO_ReadOnly );
			reply >> result;
			//kdDebug() "The result is: " << result.join("\n")).latin1()) << endl;
			result.sort();
			mWidget->playlist->insertStringList(result);
		}
	}
	else
		kdDebug() << "Error getting playlists" << endl;

	timer = new QTimer( this );
	connect( timer, SIGNAL(timeout()), SLOT(timeOut()) );
	
	connect( mWidget->playlist, SIGNAL(activated(const QString &)), this, SLOT(playListSelected(const QString &)));
	connect( mWidget->songSelection, SIGNAL(clicked(int)), this, SLOT(selected(int)));

	mWidget->songSelection->setColumnLayout(0, Qt::Vertical );
	mWidget->songSelection->layout()->setSpacing( 6 );
	mWidget->songSelection->layout()->setMargin( 11 );
		
	songSelectionLayout = new QVBoxLayout( mWidget->songSelection->layout() );
	songSelectionLayout->setAlignment( Qt::AlignTop );
	
	KStdAction::quit(this, SLOT( close() ), actionCollection());
	KStdAction::preferences(this, SLOT(showSettings()), actionCollection());

	setCentralWidget(mWidget);	
	statusBar()->insertItem("", STATUSBAR_GAMES_ID);
	statusBar()->insertItem("", STATUSBAR_WINS_ID);
	statusBar()->insertItem("", STATUSBAR_TIME_ID);
	// Why is this showing the toolbar?
	setupGUI( KMainWindow::Keys | Create | StatusBar | Save );

	loadSettings();

	
	DCOPRef popupAction( JUK, "juk-mainwindow#1/action/togglePopups" );
	DCOPReply reply = popupAction.call( "checked" );
	if ( !reply.isValid() ){
		kdDebug() << "Error finding out if popups are on!" << endl;
		return;
	}

	jukHadPopup = reply;
	if ( jukHadPopup )
		popupAction.call( "setChecked", false );
	
	DCOPRef jukMainWindow( JUK, "juk-mainwindow#1" );
	jukMainWindow.call( "disableAction", "togglePopups" );
	
	DCOPRef juk( JUK, "Collection" );
	reply = juk.call( "playingPlaylist" );
	if ( !reply.isValid() ){
		reply = juk.call( "playlist" );
		if ( !reply.isValid() )
			kdDebug() << "Error getting current playlist" << endl;
	}

	QString playlist = reply;
	kdDebug() << playlist << endl;
	for(int i=0;i<mWidget->playlist->count(); i++)
		if( 	mWidget->playlist->text(i) == playlist )
			mWidget->playlist->setCurrentItem( i );
	playListSelected( mWidget->playlist->currentText() );
}

MainWindow::~MainWindow()
{
	DCOPRef popupAction( JUK, "juk-mainwindow#1/action/togglePopups" );
	DCOPRef jukMainWindow( JUK, "juk-mainwindow#1" );

	jukMainWindow.call( "enableAction", "togglePopups" );
	if ( jukHadPopup )
		popupAction.call( "setChecked", true );

	saveScore();
}

void MainWindow::saveScore()
{
	if( numberOfGames <= 1 )
		return;

	KConfig *config = kapp->config();
	config->setGroup(QString("Score_Choices_%1_Speed_%1").arg(totalSongs).arg(interval));
	config->writeEntry("numberOfGames", numberOfGames);
	config->writeEntry("numberOfWins", numberOfWins);
	config->writeEntry("lengthToWin", lengthToWin);
}

void MainWindow::updateStats()
{
	statusBar()->changeItem(QString(i18n("Songs: %1")).arg(numberOfGames), STATUSBAR_GAMES_ID);
	statusBar()->changeItem(QString(i18n("Wins: %1")).arg(numberOfWins), STATUSBAR_WINS_ID);
	statusBar()->changeItem(QString(i18n("Average Time: %n second", "Average Time: %n seconds", (100-lengthToWin)/interval)), STATUSBAR_TIME_ID);
}

void MainWindow::loadSettings()
{
	saveScore();
	
	totalSongs = Settings::numberOfChoices();
	interval = Settings::time();
	
	KConfig *config = kapp->config();
	config->setGroup(QString("Score_Choices_%1_Speed_%1").arg(totalSongs).arg(interval));
	numberOfGames = config->readNumEntry("numberOfGames", 1);
	numberOfWins = config->readNumEntry("numberOfWins", 0);
	lengthToWin = config->readNumEntry("lengthToWin", 100);
	updateStats();
		
	makeChoiceList();
	timer->changeInterval( interval*100 );
	
	nextSong();
}

void MainWindow::makeChoiceList()
{
	// Don't refresh if we are all set
	if(totalSongs == list.count())
		return;
	
	// Romove the old tracks
	QRadioButton *button;
	for ( button = list.first(); button; button = list.next() ){
		songSelectionLayout->remove( button );
		button->close( true );
	}
	list.clear();
	for ( QLabel *label = albumList.first(); label; label = albumList.next() ){
		songSelectionLayout->remove( label );
		label->close( true );
	}
	albumList.clear();
	for ( QLabel *label = albumCoverList.first(); label; label = albumCoverList.next() ){
		songSelectionLayout->remove( label );
		label->close( true );
	}
	albumCoverList.clear();
	for ( QGridLayout *layout = trackLayouts.first(); layout; layout = trackLayouts.next() ){
		songSelectionLayout->removeItem( layout );
		delete layout;
	}
	trackLayouts.clear();

	// Add the new tracks
	for( uint i = 0; i < totalSongs; i++ ) {
		QGridLayout *layout = new QGridLayout( 0, 1, 1, 11, 6, "layout"); 
		songSelectionLayout->addLayout(layout);
		trackLayouts.append(layout);
				
		QLabel *songAlbumCover = new QLabel( mWidget->songSelection, "songCover" );
		songAlbumCover->show();
		songAlbumCover->setMinimumSize(80,80);
		songAlbumCover->setMaximumSize(80,80);
		albumCoverList.append( songAlbumCover );
		layout->addMultiCellWidget( songAlbumCover, 0, 1, 0, 0 );
				
		QRadioButton *songChoice = new QRadioButton( mWidget->songSelection, "songChoice" );
		songChoice->show();
		list.append( songChoice );
		layout->addWidget( songChoice, 0, 1 );
		
		QLabel *songAlbum = new QLabel( mWidget->songSelection, "songLabel" );
		songAlbum->show();
		albumList.append( songAlbum );
		layout->addWidget( songAlbum, 1, 1 );
	}
}

void MainWindow::playListSelected( const QString &selection )
{
	DCOPRef juk( JUK, "Collection" );
	DCOPReply reply = juk.call( "setPlaylist", selection );
	if ( !reply.isValid() ){
		kdDebug() << "Error setting Playlist" << endl;
		return;
	}
	
	currentPlaylistSongs = juk.call( "playlistTracks", selection );
	
	if( currentPlaylistSongs.count() < totalSongs+1 )
		KMessageBox::sorry(this, i18n("This collection does not have enough tracks to play the game."),
										i18n("Not Enough Tracks"));
	else
		nextSong();
}

void MainWindow::selected( int selection )
{
	if( (uint)selection == currentSong ){
		if( mWidget->isCorrect->text().isEmpty() )
			numberOfWins++;
		// It is still a win, so update here
		lengthToWin = (lengthToWin*(numberOfGames-1)+mWidget->progressBar->progress())/numberOfGames;
		updateStats();
		
		mWidget->isCorrect->setText( i18n( "Correct!" ) );
		mWidget->image1->setPixmap(SmallIcon("button_ok"));
		mWidget->image2->setPixmap(SmallIcon("button_ok"));
		// Play for 1 more second and then switch
		timer->stop();
		QTimer::singleShot( 1000, this, SLOT(nextSong()) );
	} 
	else{
		mWidget->isCorrect->setText( i18n( "Incorrect!" ) );
		mWidget->image1->setPixmap(SmallIcon("stop"));
		mWidget->image2->setPixmap(SmallIcon("stop"));
	}
}

QString MainWindow::trackProperties( const QString &filename, const QString &property ) const
{
	DCOPRef juk( JUK, "Collection" );
	return juk.call( "trackProperty", filename, property);
}

void MainWindow::fillSong( const QString &filename, int number )
{
	QString songDiscription = QString( "%1 - %2" )
		.arg(trackProperties( filename, "Artist" ) )
		.arg(trackProperties( filename, "Title" ) );
		
	(list.at(number))->setText( songDiscription );
	
	songDiscription = QString( "\t%1 %2, %3 %4" )
		.arg( i18n( "Album:" ) )
		.arg( trackProperties( filename, "Album" ) )
		.arg( i18n( "Track Number:" ) )
		.arg( trackProperties( filename, "Track" ) );
	
	(albumList.at(number))->setText( songDiscription );

	DCOPRef juk( JUK, "Collection" );
	DCOPReply reply = juk.call( "trackCover(QString,QString)", filename, QString("small") );
	if(reply.isValid()){
		QPixmap cover;
		reply.get<QPixmap>( cover, "QPixmap" );
		(albumCoverList.at(number))->setPixmap( cover );
	}
}

/**
 * Select four new songs and start playing one of them
 */
void MainWindow::nextSong()
{
	if( list.count() == 0 || currentPlaylistSongs.count() == 0 )
		return;

	// Yah, you can cheat by just not checking anything.
	if( (list.at(currentSong)) && list.at(currentSong)->isChecked() )
		numberOfGames++;
				
	// Pick the next song
	currentSong = kapp->random()%totalSongs;
	
	for( uint i=0; i < totalSongs; i++ ){
		int choice = kapp->random()%(currentPlaylistSongs.count() );
		fillSong(currentPlaylistSongs[choice], i);
		
		if( currentSong == i ){
			DCOPRef juk( JUK, "Player" );
			DCOPReply reply = juk.call( "play", currentPlaylistSongs[choice]);
			if ( !reply.isValid() )
				kdDebug() << "Error starting song" << endl;
		}
	}

	QRadioButton *button;
	for ( button = list.first(); button; button = list.next() ){
		button->setChecked( false );
		button->setEnabled( true );
	}
	for ( QLabel *label = albumList.first(); label; label = albumList.next() )
		label->setEnabled( true );
	
	mWidget->isCorrect->setText("");
	mWidget->image1->setPixmap(QPixmap());
	mWidget->image2->setPixmap(QPixmap());
		
	mWidget->progressBar->setProgress( 100 );
	timer->start( Settings::time()*100 );
}

/**
 * Decrease the timer and disable options until it is time to start all over again.
 */
void MainWindow::timeOut()
{
	// Decriment the timer
	int progress = mWidget->progressBar->progress();
	progress -= 10;
	mWidget->progressBar->setProgress( progress );

	// Slowly disable items
	if( Settings::disableChoices() && progress > 10 ) {
		int disable = (int)(totalSongs*(1-((float)progress)/100));
		QRadioButton *button;
		for ( button = list.first(); button; button = list.next() ){
			if(!button->isEnabled())
				disable--;
		}

		while( disable > 0 ) {
			uint r = kapp->random()%totalSongs;
			if(r != currentSong){
				if(list.at(r)->isEnabled()){
					albumList.at(r)->setEnabled(false);
					list.at(r)->setEnabled(false);
					list.at(r)->setChecked(false);
					disable--;
				}
			}
		}
	}
	
	// Times up! Go to the next song
	if( progress == -10 )
		nextSong();
}

/**
 * Show Settings dialog.
 */
void MainWindow::showSettings()
{
	if( KConfigDialog::showDialog("settings") )
		return;

	KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self(), KDialogBase::Swallow);
	dialog->addPage(new Options(0, "General"), i18n("General"), "package_settings");
	connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings()));
	dialog->show();
}

#include "mainwindow.moc"

