/***************************************************************************
*   Copyright (C) 2005 by Alexander Nemish   *
*   atlanter@gmail.com   *
*                                                                         *
*   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 <tdeaccel.h>
#include <tdeaction.h>
#include <kcharsets.h>
#include <tdeconfig.h>
#include <tdeconfigdialog.h>
#include <kdebug.h>
#include <tdeversion.h>
#include <kencodingfiledialog.h>
#include <tdefiledialog.h>
#include <tdeglobal.h>
#include <kinputdialog.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdemenubar.h>
#include <tdemessagebox.h>
#include <kprinter.h>
#include <kstatusbar.h>
#include <ksplashscreen.h>
#include <kstandarddirs.h>
#include <tdestdaccel.h>
#include <kstdaction.h>
#include <kurl.h>
#include <kurldrag.h>
#include <kurlrequesterdlg.h>
#include <tdeio/netaccess.h>
#include <tqdragobject.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqpixmap.h>
#include <tqtextcodec.h>
#include <tqsignalmapper.h>
#include <assert.h>
#include "bookreader.h"
#include "settings.h"
#include "settingsdlg.h"
#include "bookmarksdlg.h"

namespace {
TQStringList listEncodings()
{
	const TQStringList encodings(TDEGlobal::charsets()->availableEncodingNames());
	TQStringList availEncodings;
	for (unsigned int i=0; i < encodings.count(); ++i)
	{
		bool found = false;
		TDEGlobal::charsets()->codecForName(encodings[i], found);
		if (found)
			availEncodings << encodings[i];
	}
	return availEncodings;
}
}


BookReader::BookReader()
		: TDEMainWindow(0, "KBookReader"),
		m_view(new BookWidget(this)),
		m_fullScreenAction(0),
		m_splash(0),
		m_printer(0)
{

	TQPixmap splash(TDEGlobal::dirs()->findResource("appdata",
							"themes/default/splash.png"));
	m_splash = new KSplashScreen(splash);
	m_splash->show();
	// accept dnd
	setAcceptDrops(true);

	// tell the TDEMainWindow that this is indeed the main widget
	setCentralWidget(m_view);

	// then, setup our actions
	setupActions();

	// and a status bar
	statusBar()->show();

	// Apply the create the main window and ask the mainwindow to
	// automatically save settings if changed: window size, toolbar
	// position, icon size, etc.  Also to add actions for the statusbar
	// toolbar, and keybindings if necessary.
	KStdAction::keyBindings(guiFactory(), TQ_SLOT(configureShortcuts()), actionCollection());
	createStandardStatusBarAction();
	setStandardToolBarMenuEnabled( true );
	KStdAction::configureToolbars(this, TQ_SLOT(configureToolbars() ), actionCollection());
	createGUI(TQString(), false);
	initialGeometrySet();
	setAutoSaveSettings();

	// allow the view to change the statusbar and caption
	connect(m_view, TQ_SIGNAL(signalChangeStatusbar(const TQString&)),
	         this, TQ_SLOT(changeStatusbar(const TQString&)));
	connect(m_view, TQ_SIGNAL(signalChangeCaption(const TQString&)),
	         this, TQ_SLOT(changeCaption(const TQString&)));

	readSettings();
	m_splash->finish(m_view);
}

BookReader::~BookReader()
{
	writeSettings();
	delete m_splash;
}

void BookReader::load(const KURL& url)
{
	TQString target;
	// download the contents
	if(!TDEIO::NetAccess::download(url, target, this))
	{
		KMessageBox::error(this, TDEIO::NetAccess::lastErrorString());
		return;
	}
	recentFilesAction->addURL(url);
	setCaption(url.fileName());
	m_view->openURL(url);
	updateBookmarks();
	TDEIO::NetAccess::removeTempFile(target);
}

void BookReader::setupActions()
{
// 	KStdAction::openNew(this, TQ_SLOT(fileNew()), actionCollection());
	KStdAction::open(this, TQ_SLOT(fileOpen()), actionCollection());
	recentFilesAction = KStdAction::openRecent(this, TQ_SLOT(slotURLSelected(const KURL &)), 
								actionCollection());
	KStdAction::save(this, TQ_SLOT(fileSave()), actionCollection());
	KStdAction::saveAs(this, TQ_SLOT(fileSaveAs()), actionCollection());
	KStdAction::print(this, TQ_SLOT(filePrint()), actionCollection());
	KStdAction::quit(kapp, TQ_SLOT(quit()), actionCollection());
	
	KStdAction::firstPage(this, TQ_SLOT(gotoFirstPage()), actionCollection());
	KStdAction::prior(this, TQ_SLOT(prevPage()), actionCollection());
	KStdAction::next(this, TQ_SLOT(nextPage()), actionCollection());
	KStdAction::lastPage(this, TQ_SLOT(gotoLastPage()), actionCollection());

	KStdAction::addBookmark(this, TQ_SLOT(addBookmark()), actionCollection());
	KStdAction::editBookmarks(this, TQ_SLOT(editBookmarks()), actionCollection());

	KStdAction::gotoPage(this, TQ_SLOT(gotoPage()), actionCollection());
	
	m_fullScreenAction = KStdAction::fullScreen(this, TQ_SLOT(fullScreen()), 
							actionCollection(), this);

	KStdAction::preferences(this, TQ_SLOT(optionsPreferences()), actionCollection());

	TDEConfig *config = kapp->config();
	recentFilesAction->loadEntries(config);

	// this doesn't do anything useful.  it's just here to illustrate
	// how to insert a custom menu and menu item
	/*
	TDEAction *custom = new TDEAction( i18n( "Cus&tom Menuitem" ), 0,
	                               this, TQ_SLOT( optionsPreferences() ),
	                               actionCollection(), "custom_action" );*/
}

void BookReader::saveProperties(TDEConfig *config)
{
	// the 'config' object points to the session managed
	// config file.  anything you write here will be available
	// later when this app is restored
	if (!m_view->currentURL().isEmpty())
	{
		config->writeEntry("lastURL", m_view->currentURL());
		config->writeEntry("currentPage", m_view->currentPage());
	}
}

void BookReader::readProperties(TDEConfig */*config*/)
{
	// the 'config' object points to the session managed
	// config file.  this function is automatically called whenever
	// the app is being restored.  read in here whatever you wrote
	// in 'saveProperties'

// 	TQString url = config->readPathEntry("lastURL");
// 	int currentPage = config->readEntry("currentPage").toInt();
	/* Disable forawhile
	if (!url.isEmpty())
	{
		m_view->openURL(KURL(url));
		m_view->setCurrentPage(currentPage);
	}
	*/
}

void BookReader::dragEnterEvent(TQDragEnterEvent *event)
{
	// accept uri drops only
	event->accept(KURLDrag::canDecode(event));
}

void BookReader::dropEvent(TQDropEvent *event)
{
	// this is a very simplistic implementation of a drop event.  we
	// will only accept a dropped URL.  the TQt dnd code can do *much*
	// much more, so please read the docs there
	KURL::List urls;

	// see if we can decode a URI.. if not, just ignore it
	if (KURLDrag::decode(event, urls) && !urls.isEmpty())
	{
		// okay, we have a URI.. process it
		const KURL & url = urls.first();

		// load in the file
		load(url);
	}
}

void BookReader::fileOpen()
{
	// this slot is called whenever the File->Open menu is selected,
	// the Open shortcut is pressed (usually CTRL+O) or the Open toolbar
	// button is clicked
	/*
	    // this brings up the generic open dialog
	    KURL url = KURLRequesterDlg::getURL(TQString(), this, i18n("Open Location") );
	*/
	// standard filedialog
	KEncodingFileDialog::Result res = 
			KEncodingFileDialog::getOpenURLAndEncoding(
				listEncodings()[Settings::defaultEncoding()]);
	KURL url = res.URLs.front();
	m_view->setEncoding(res.encoding);
	if (!url.isEmpty())
		load(url);
}

void BookReader::fileSave()
{
	// this slot is called whenever the File->Save menu is selected,
	// the Save shortcut is pressed (usually CTRL+S) or the Save toolbar
	// button is clicked

	// save the current file
}

void BookReader::fileSaveAs()
{
	// this slot is called whenever the File->Save As menu is selected,
	KURL file_url = KFileDialog::getSaveURL();
	if (!file_url.isEmpty() && file_url.isValid())
	{
		// save your info, here
	}
}

void BookReader::filePrint()
{
	// this slot is called whenever the File->Print menu is selected,
	// the Print shortcut is pressed (usually CTRL+P) or the Print toolbar
	// button is clicked
	if (!m_printer) m_printer = new KPrinter;
	if (m_printer->setup(this))
	{
		// setup the printer.  with TQt, you always "print" to a
		// TQPainter.. whether the output medium is a pixmap, a screen,
		// or paper
		TQPainter p;
		p.begin(m_printer);

		// we let our view do the actual printing
		TQPaintDeviceMetrics metrics(m_printer);
		//m_view->print( &p, metrics.height(), metrics.width() );

		// and send the result to the printer
		p.end();
	}
}

void BookReader::optionsPreferences()
{
	if (TDEConfigDialog::showDialog("settings"))
		return ;

	TDEConfigDialog *dialog = new TDEConfigDialog(this, "settings", Settings::self(),
	                        KDialogBase::IconList);
	SettingsWidget *general = new SettingsWidget(0, "General");
	
	const TQStringList encodings(listEncodings());
	
	TQString curEncoding(encodings[Settings::defaultEncoding()]);
	if (curEncoding.isEmpty())
		curEncoding = TQString::fromLatin1(TDEGlobal::locale()->encoding());
	
	general->kcfg_DefaultEncoding->clear();
	general->kcfg_DefaultEncoding->insertStringList(encodings);
	for (unsigned int i=0; i < encodings.count(); ++i)
		if (encodings[i] == curEncoding)
			general->kcfg_DefaultEncoding->setCurrentItem(i);

	dialog->addPage(general, i18n("General"), "settings");
	connect(dialog, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(loadSettings()));
	dialog->show();
}

void BookReader::changeStatusbar(const TQString& text)
{
	// display the text on the statusbar
	statusBar()->message(text);
}

void BookReader::changeCaption(const TQString& text)
{
	// display the text on the caption
	setCaption(text);
}

void BookReader::loadSettings()
{
	m_view->setFont(Settings::font());
	m_view->setParaOffset(Settings::paraOffset());
	m_view->setEncoding(Settings::defaultEncoding());
}

void BookReader::slotURLSelected(const KURL & url)
{
	load(url);
}

bool BookReader::queryExit()
{
	writeSettings();
	return true;
}

void BookReader::writeSettings()
{
	TDEConfig * config = kapp->config();
	config->writeEntry("size", m_view->size());
	config->writeEntry("lastURL", m_view->currentURL());
	config->writeEntry("lastURLPage", m_view->currentPage());
	recentFilesAction->saveEntries(TDEGlobal::config());
}

void BookReader::readSettings()
{
	recentFilesAction->loadEntries(TDEGlobal::config());
	recentFilesAction->setEnabled(true); // force enabling
	
	m_view->setParaOffset(Settings::paraOffset());
	m_view->setEncodings(listEncodings());
	m_view->setEncoding(Settings::defaultEncoding());

	if (Settings::loadLastUrl())
	{
 		loadLastUrl();
	}
}

void BookReader::fullScreen()
{
	if (m_fullScreenAction->isChecked())
	{
		menuBar()->hide();
		setWindowState(windowState() | WindowFullScreen);
	}
	else
	{
		menuBar()->show();
		setWindowState(windowState() & ~WindowFullScreen);
	}
}

void BookReader::loadLastUrl()
{
	TDEConfig * config = kapp->config();
	TQSize size = config->readSizeEntry("size");
	lastURL = config->readEntry("lastURL");
	connect(m_view, TQ_SIGNAL(loadingFinished()), this, TQ_SLOT(loadLastURLSetPage()));
	if (!lastURL.isEmpty())
	{
// 		kdDebug() << "loadLastUrl: initial size = " << m_view->size() << endl;
		m_view->resize(size);
// 		kdDebug() << "loadLastUrl: resize = " << m_view->size() << endl;
		m_view->setupPageSize();
// 		kdDebug() << "render = 1" << endl;
		load(lastURL);
	}
}

void BookReader::loaded(int loaded)
{
	//m_loaded = loaded;
	m_splash->message(tr("Loading: %1 - %2%").arg(lastURL.fileName()).arg(loaded));
}

void BookReader::gotoFirstPage()
{
	m_view->firstPage();
}

void BookReader::nextPage()
{
	m_view->nextPage();
}

void BookReader::prevPage()
{
	m_view->prevPage();
}

void BookReader::gotoLastPage( )
{
	m_view->lastPage();
}

void BookReader::gotoPage()
{
	bool isOk;
	int page = KInputDialog::getInteger(tr("Goto page"), tr("Page number"), 
					m_view->currentPage(), 1, 
					m_view->pageCount(), 1, &isOk);
	m_view->setCurrentPage(page - 1);
}

void BookReader::loadLastURLSetPage()
{
	int lastURLPage = kapp->config()->readNumEntry("lastURLPage");
	m_view->setCurrentPage(lastURLPage);
}

void BookReader::addBookmark()
{
	bool isOk;
	TQString name = KInputDialog::getText(tr("Add bookmark"),
		tr("Bookmark name"), tr("Here"), &isOk);
	if (isOk)
	{
		m_view->addBookmark(name);
		updateBookmarks();
	}
}

/*!
    \fn BookReader::updateBookmarks()
 */
void BookReader::updateBookmarks()
{
	unplugActionList("my_bookmarks");
	m_bookmarkActions.clear();
	m_bookmarkActions.setAutoDelete(true);
	const Bookmarks & bms = m_view->bookmarks();
	
	TQSignalMapper *bookmarkMapper = new TQSignalMapper(this);
	connect(bookmarkMapper, TQ_SIGNAL(mapped(int)), this, TQ_SLOT(gotoBookmark(int)));
	for (Bookmarks::size_type i = 0; i < 9 && i < bms.size(); ++i)
	{
		const Bookmark & bm = bms[i];
		TDEAction * action = new TDEAction(bm.name(), ALT+Key_1 + i);
		connect(action, TQ_SIGNAL(activated()), bookmarkMapper, TQ_SLOT(map()));
		m_bookmarkActions.append(action);
		bookmarkMapper->setMapping(action, i);
	}
	plugActionList("my_bookmarks", m_bookmarkActions);
}


/*!
    \fn BookReader::gotoBookmark(int index)
 */
void BookReader::gotoBookmark(int index)
{
	const Bookmarks & bms = m_view->bookmarks();
	assert(index < bms.size());
	
	unsigned int page = bms[index].page();
	m_view->setCurrentPage(page);
}



/*!
    \fn BookReader::editBookmarks()
 */
void BookReader::editBookmarks()
{
    /// @todo implement me
	BookmarksDlg dialog(m_view->bookmarks());
	if (dialog.exec() == TQDialog::Accepted)
	{
		m_view->setBookmarks(dialog.bookmarks());
		updateBookmarks();
	}
}


#include "bookreader.moc"
