/***************************************************************************
 *   Copyright (C) 2002-2003 Andi Peredri                                  *
 *   andi@ukr.net                                                          *
 *   Copyright (C) 2004-2005 Artur Wiebe                                   *
 *   wibix@gmx.de                                                          *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QLayout>
#include <QApplication>
#include <QMenuBar>
#include <QToolBar>
#include <QDir>
#include <QMessageBox>
#include <QFileDialog>
#include <QCloseEvent>
#include <QFontDialog>
#include <QDebug>

#include "pdn.h"
#include "toplevel.h"
#include "newgamedlg.h"
#include "view.h"
#include "common.h"

#define CFG_THEME_PATH	CFG_KEY"ThemePath"
#define CFG_FILENAME	CFG_KEY"Filename"
#define CFG_KEEPDIALOG	CFG_KEY"ShowKeepDialog"
#define CFG_NOTATION	CFG_KEY"Notation"
#define CFG_NOT_ABOVE	CFG_KEY"NotationAbove"
#define CFG_NOT_FONT	CFG_KEY"NotationFont"
#define CFG_CLEAR_LOG	CFG_KEY"ClearLogOnNewRound"


myTopLevel::myTopLevel()
{
	setWindowTitle(APPNAME);
	setWindowIcon(QIcon(":/icons/biglogo.png"));

	m_newgame = new myNewGameDlg(this);
	make_central_widget();
	make_actions();
	restore_settings();

	/*
	 * new computer game.
	 */
	m_view->newGame(m_newgame->rules(), m_newgame->freePlacement(),
			m_newgame->name(),
			m_newgame->isWhite(), m_newgame->opponent(),
			m_newgame->opponentName(), m_newgame->skill());

	if(layout())
		layout()->setSizeConstraint(QLayout::SetFixedSize);
}


void myTopLevel::make_actions()
{
	// game menu actions
	gameNew = new QAction(QIcon(":/icons/logo.png"), tr("&New..."), this);
	gameNew->setShortcut(tr("CTRL+N", "File|New"));
	connect(gameNew, SIGNAL(triggered()), this, SLOT(slot_new_game()));

	gameNextRound = new QAction(QIcon(":/icons/next.png"),
			tr("&Next Round"), this);
	connect(gameNextRound, SIGNAL(triggered()),
			this, SLOT(slot_next_round()));

	gameStop = new QAction(QIcon(":/icons/stop.png"), tr("&Stop"), this);
	connect(gameStop, SIGNAL(triggered()), m_view, SLOT(slotStopGame()));

	gameOpen = new QAction(QIcon(":/icons/fileopen.png"), tr("&Open..."),
			this);
	gameOpen->setShortcut(tr("CTRL+O", "File|Open"));
	connect(gameOpen, SIGNAL(triggered()), this, SLOT(slot_open_game()));

	gameSave = new QAction(QIcon(":/icons/filesave.png"), tr("&Save..."),
			this);
	gameSave->setShortcut(tr("CTRL+S", "File|Save"));
	connect(gameSave, SIGNAL(triggered()), this, SLOT(slot_save_game()));

	QAction* gameQuit = new QAction(QIcon(":/icons/exit.png"), tr("&Quit"),
		this);
	gameQuit->setShortcut(tr("CTRL+Q", "File|Quit"));
	connect(gameQuit, SIGNAL(triggered()),
		this, SLOT(close()));
//		QApplication::instance(), SLOT(quit()));

	// view menu actions
	viewNotation = new QAction(tr("&Show Notation"), this);
	viewNotation->setCheckable(true);
	connect(viewNotation, SIGNAL(toggled(bool)),
		this, SLOT(slot_notation(bool)));

	viewNotationAbove = new QAction(tr("Show notation &above men"), this);
	viewNotationAbove->setCheckable(true);
	connect(viewNotationAbove, SIGNAL(toggled(bool)),
		this, SLOT(slot_notation(bool)));

	// settings menu
	settingsKeep = new QAction(tr("&Confirm aborting current game"), this);
	settingsKeep->setCheckable(true);
	//
	settingsClearLog = new QAction(tr("Clear &log on new round"), this);
	settingsClearLog->setCheckable(true);
	connect(settingsClearLog, SIGNAL(toggled(bool)),
		m_view, SLOT(slotClearLog(bool)));
	//
	QAction* settingsNotationFont = new QAction(tr("&Notation font..."),
			this);
	connect(settingsNotationFont, SIGNAL(triggered()),
		this, SLOT(slot_notation_font()));

	// help menu actions
	QAction* helpRules = new QAction(tr("&Rules of Play"), this);
	helpRules->setShortcut(tr("F1", "Help|Help"));
	connect(helpRules, SIGNAL(triggered()), this, SLOT(slot_help()));

	QAction* helpAbout = new QAction(QIcon(":/icons/logo.png"),
		tr("&About")+" "APPNAME, this);
	connect(helpAbout, SIGNAL(triggered()), this, SLOT(slot_about()));

	QAction* helpAboutQt = new QAction(tr("About &Qt"), this);
	connect(helpAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));


	/*
	 * toolbar
	 */
	QToolBar* tb = addToolBar(tr("&Toolbar"));
	tb->setMovable(false);
	tb->addAction(gameNew);
	tb->addAction(gameOpen);
	tb->addAction(gameSave);
	tb->addSeparator();
	tb->addAction(gameNextRound);
	tb->addAction(gameStop);


	/*
	 * menus
	 */
	QMenu* gameMenu = menuBar()->addMenu(tr("&Game"));
	gameMenu->addAction(gameNew);
	gameMenu->addAction(gameOpen);
	gameMenu->addAction(gameSave);
	gameMenu->addSeparator();
	gameMenu->addAction(gameNextRound);
	gameMenu->addAction(gameStop);
	gameMenu->addSeparator();
	gameMenu->addAction(gameQuit);

	viewMenu = menuBar()->addMenu(tr("&View"));
	viewMenu->addAction(tb->toggleViewAction());
	viewMenu->addSeparator();
	viewMenu->addAction(viewNotation);
	viewMenu->addAction(viewNotationAbove);

	QMenu* settingsMenu = menuBar()->addMenu(tr("&Settings"));
	settingsMenu->addAction(settingsKeep);
	settingsMenu->addSeparator();
	settingsMenu->addAction(settingsNotationFont);
	settingsMenu->addSeparator();
	settingsMenu->addAction(settingsClearLog);

	QMenu* helpMenu = menuBar()->addMenu(tr("&Help"));
	helpMenu->addAction(helpRules);
	helpMenu->addSeparator();
	helpMenu->addAction(helpAbout);
	helpMenu->addAction(helpAboutQt);


	/*
	 * THEMES. create a default theme.
	 */
	QActionGroup* themes_grp = new QActionGroup(this);
	themes_grp->setExclusive(true);

	QAction* default_theme = new QAction(tr(DEFAULT_THEME), themes_grp);
	default_theme->setCheckable(true);
	m_themes[default_theme] = DEFAULT_THEME;
	default_theme->setChecked(true);
	set_theme(default_theme);//FIXME TODO
	themes_grp->addAction(default_theme);

	viewMenu->addSeparator();
	viewMenu->addAction(default_theme);
	read_themes(themes_grp,
			viewMenu,QDir::homePath()+"/"USER_PATH"/"THEME_DIR);
	//TODO-hardcoded
	read_themes(themes_grp,
			viewMenu, PREFIX"/share/kcheckers/"THEME_DIR);

	connect(themes_grp, SIGNAL(triggered(QAction*)),
		this, SLOT(set_theme(QAction*)));
}


void myTopLevel::read_themes(QActionGroup* menu_grp, QMenu* menu, 
	const QString& path)
{
	QDir sharedir(path);
	QStringList themes = sharedir.entryList(QDir::Dirs|QDir::Readable);
	themes.removeAll(".");
	themes.removeAll("..");

	for(QStringList::iterator it=themes.begin(); it!=themes.end(); ++it) {
		sharedir.cd(*it);

		QString theme_dir = sharedir.absolutePath();
		QString errtext;

		QStringList files
			= sharedir.entryList(QDir::Files|QDir::Readable);

		// all files are there.
		if(files.count()!=8 ||
			files.indexOf(THEME_TILE1) == -1 ||
			files.indexOf(THEME_TILE2) == -1 ||
			files.indexOf(THEME_FRAME) == -1 ||
			files.indexOf(THEME_MANBLACK) == -1 ||
			files.indexOf(THEME_MANWHITE) == -1 ||
			files.indexOf(THEME_KINGBLACK) == -1 ||
			files.indexOf(THEME_KINGWHITE) == -1 ||
			files.indexOf(THEME_FILE) == -1) {
			// TODO not translated.
			errtext += "Wrong number of files. ";
		}
		files.removeAll(THEME_FILE);

		// check pic size.
		QSize size = QPixmap(theme_dir+"/"+files[0]).size();
		if(size.width()>MAX_TILE_SIZE || size.height()>MAX_TILE_SIZE)
			// TODO not translated.
			errtext += "Picture(s) too big. ";

		if(size.width()==0 || size.height()==0)
			// TODO not translated.
			errtext += "Width/Height is zero. ";

		// check pics all the same size.
		foreach(QString file, files) {
			if(QPixmap(theme_dir+"/"+file).size() != size) {
				// TODO not translated.
				errtext += "Pictures are different in size. ";
				break;
			}
		}

		if(errtext.length()) {
			// TODO not translated.
			qWarning() << endl << "Theme in"
				<< sharedir.absolutePath() << endl << errtext;

		} else {
			QString themename = sharedir.dirName();

			/*TODO
			QFile file(theme_dir+"/"+THEME_FILE);
			if(file.open(QFile::ReadOnly)) {
			QTextStream ts(&file);
			if(!ts.atEnd())
				themename = ts.readLine();
			// try go get locale theme name
			// TODO
			QString locale = QString("[%1]=").arg(QTextCodec::locale());
			while(!ts.atEnd()) {
				QString line = ts.readLine();
				if(line.startsWith(locale)) {
					themename = line.mid(locale.length());
					break;
				}
			}
			file.close();
			}
			*/
		//
			QAction* action = new QAction(themename, this);
			menu_grp->addAction(action);
			menu->addAction(action);
			action->setCheckable(true);
			m_themes[action] = theme_dir;
		}

		sharedir.cdUp();
	}
}


void myTopLevel::restore_settings()
{
	QSettings cfg(APPNAME, APPNAME);

	QString theme_path = cfg.value(CFG_THEME_PATH, DEFAULT_THEME)
		.toString();
	for(myThemeMap::iterator it = m_themes.begin(); it!=m_themes.end();it++)
		if(it.value() == theme_path) {
			it.key()->setChecked(true);
			set_theme(it.key());
			break;
		}

	filename = cfg.value(CFG_FILENAME).toString();

	viewNotation->setChecked(cfg.value(CFG_NOTATION, false).toBool());
	viewNotationAbove->setChecked(cfg.value(CFG_NOT_ABOVE, true).toBool());
	slot_notation(true);	// here: arg is ignored by the function

	bool clear_log = cfg.value(CFG_CLEAR_LOG, true).toBool();
	settingsClearLog->setChecked(clear_log);
	m_view->slotClearLog(clear_log);

	//
	settingsKeep->setChecked(cfg.value(CFG_KEEPDIALOG, true).toBool());

	QFont cfont;
	if(cfont.fromString(cfg.value(CFG_NOT_FONT, "").toString()))
	m_view->setNotationFont(cfont);

	// new game
	m_newgame->readSettings(&cfg);
}


void myTopLevel::make_central_widget()
{
	m_view = new myView(this);

	connect(m_view, SIGNAL(working(bool)),
			this, SLOT(slot_working(bool)));

	setCentralWidget(m_view);
}


void myTopLevel::warning(const QString& text)
{
	QMessageBox::warning(this, tr("Error")+" - "APPNAME, text);
}


void myTopLevel::information(const QString& caption, const QString& text)
{
	QDialog* dlg = new QDialog(this);
	dlg->setModal(true);
	dlg->setWindowTitle(caption+" - "APPNAME);

	// logo left
	QLabel* logo = new QLabel(dlg);
	logo->setPixmap(QPixmap(":/icons/dialog.png"));

	// text editor
	QTextEdit* te = new QTextEdit(text, dlg);
	te->setReadOnly(true);
	te->setMinimumWidth(m_view->width()-100);
	te->setMinimumHeight(m_view->height()-200);

	// close button
	QPushButton* button = new QPushButton(tr("&Close"), dlg);
	connect(button, SIGNAL(clicked()), dlg, SLOT(accept()));

	// Layout.
	QHBoxLayout* hb = new QHBoxLayout();
	hb->addWidget(logo, 0, Qt::AlignTop);
	hb->addWidget(te, 1);

	QHBoxLayout* hb_button = new QHBoxLayout();
	hb_button->addStretch();
	hb_button->addWidget(button);

	QVBoxLayout* vb = new QVBoxLayout(dlg);
	vb->addLayout(hb, 1);
	vb->addSpacing(5);
	vb->addLayout(hb_button);

	// go
	dlg->exec();
	delete dlg;
}


void myTopLevel::slot_save_game()
{
	QString fn = QFileDialog::getSaveFileName(this,
		tr("Save Game")+" - "APPNAME, filename, "PDN Files (*."EXT")");
	if(!fn.isEmpty()) {
		if(fn.right(3)!=EXT)
			fn += "."EXT;

		if(m_view->savePdn(fn))
			filename = fn;
		else
			warning(tr("Could not save: ")+filename);
	}
}


void myTopLevel::slot_open_game()
{
	QString fn = QFileDialog::getOpenFileName(this,
		tr("Open Game")+" - "APPNAME, filename, "PDN Files (*."EXT")");
	if(!fn.isEmpty())
		open(fn);
}


void myTopLevel::open(const QString& fn)
{
	if(m_view->openPdn(fn))
		filename = fn;
}


void myTopLevel::closeEvent(QCloseEvent* e)
{
	if(!keep_game()) {
		store_settings();
		e->accept();
	} else {
		e->ignore();
	}
}


void myTopLevel::store_settings()
{
	QSettings config(APPNAME, APPNAME);

	for(myThemeMap::iterator it=m_themes.begin(); it!=m_themes.end(); it++)
		if(it.key()->isChecked()) {
			config.setValue(CFG_THEME_PATH, it.value());
			break;
		}

	config.setValue(CFG_FILENAME, filename);

	config.setValue(CFG_NOTATION, viewNotation->isChecked());
	config.setValue(CFG_NOT_ABOVE, viewNotationAbove->isChecked());

	config.setValue(CFG_KEEPDIALOG, settingsKeep->isChecked());
	config.setValue(CFG_NOT_FONT, m_view->notationFont().toString());
	config.setValue(CFG_CLEAR_LOG, settingsClearLog->isChecked());

	// new game
	m_newgame->writeSettings(&config);
}


void myTopLevel::set_theme(QAction* action)
{
	QString path = m_themes[action];
	m_view->setTheme(path);
}


void myTopLevel::slot_help()
{
	QString text = 
		tr("<p>In the beginning of game you have 12 checkers (men). "
		"The men move forward only. The men can capture:"
		"<ul>"
		"<li>by jumping forward only (english rules);"
		"<li>by jumping forward or backward (russian rules)."
		"</ul>"
		"<p>A man which reaches the far side of the board becomes a king. "
		"The kings move forward or backward:"
		"<ul>"
		"<li>to one square only (english rules);"
		"<li>to any number of squares (russian rules)."
		"</ul>"
		"<p>The kings capture by jumping forward or backward. "
		"Whenever a player is able to make a capture he must do so.");

	information(tr("Rules of Play"), text);
}


void myTopLevel::slot_about()
{
	QString text =
		APPNAME", a board game.<br>Version "VERSION"<br><br>"
		COPYRIGHT"<br>"
		  	"<a href=\""HOMEPAGE"\">"HOMEPAGE"</a><br><br>"
		"Contributors:<br>"CONTRIBS"<br><br>"
		"This program is distributed under the terms "
		"of the GNU General Public License.";

	information(tr("About"), text);
}


void myTopLevel::slot_new_game()
{
	if(keep_game())
		return;

	if(m_newgame->exec()==QDialog::Accepted) {
		m_view->newGame(m_newgame->rules(), m_newgame->freePlacement(),
				m_newgame->name(),
				m_newgame->isWhite(), m_newgame->opponent(),
				m_newgame->opponentName(), m_newgame->skill());
	}
}


void myTopLevel::slot_working(bool working)
{
	bool disable = !working;

	gameNew->setEnabled(disable);
	gameSave->setEnabled(disable);

	gameNextRound->setEnabled(disable);// FIXME !m_view->isAborted());
	gameOpen->setEnabled(disable);
	gameStop->setEnabled(!disable);

	m_view->setEnabled(disable);
}


bool myTopLevel::keep_game()
{
	if(!settingsKeep->isChecked() || m_view->isAborted())
		return false;

	int answer = QMessageBox::question(this, tr("Abort Game?")+" - "APPNAME,
		tr("Current game will be lost if you continue.\n"
		"Do you really want to discard it?"),
		QMessageBox::Yes, QMessageBox::No);

	if(answer == QMessageBox::Yes)
		return false;

	return true;
}


void myTopLevel::slot_next_round()
{
	if(!keep_game())
		m_view->slotNextRound();
}


void myTopLevel::slot_notation_font()
{
	bool ok;
	QFont font = QFontDialog::getFont(&ok, m_view->notationFont(), this);
	if(ok)
		m_view->setNotationFont(font);
}


void myTopLevel::slot_notation(bool)
{
	m_view->setNotation(viewNotation->isChecked(),
			viewNotationAbove->isChecked());
}