/***************************************************************************
*   Copyright (C) 2005 by Alexander Nemish  *
*                                                                         *
*   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 <tqpainter.h>
#include <tqevent.h>
#include <tqimage.h>
#include <kdebug.h>
#include <kprogress.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqtextcodec.h>
#include <tqstring.h>
#include <tdeglobal.h>
#include <kstandarddirs.h>
#include "bookwidget.h"
#include "theme.h"
#include "settings.h"
#include "renderer.h"
//#include "ktextprogressdlg.h"

class TQStringList;
class TQRect;
class TQPixmap;

BookWidget::BookWidget(TQWidget *parent, const char *name)
		: TQWidget(parent, name),
		m_modified(true),
		m_currentPage(0),
		m_left_margin(0),
		m_right_margin(0),
		m_top_margin(0),
		m_bottom_margin(0),
		m_middle_margin(0),
		m_encoding(0),
		m_renderer(new Renderer)
{
	//setupPageSize();
	//setupSlots();
	m_left_margin = 30;
	m_right_margin = 30;
	m_top_margin = 20;
	m_bottom_margin = 15;
	m_middle_margin = 20;

	setFont(Settings::font());

	Theme::self()->loadTheme("default");

	setWFlags(TQt::WNoAutoErase);
	setFocusPolicy(TQWidget::StrongFocus);
	
	connect(&m_timer, TQ_SIGNAL(timeout()), this, TQ_SLOT(loadLine()));
	connect(m_renderer.get(), TQ_SIGNAL(renderingFinished()), this, TQ_SLOT(renderingFinished()));
}


BookWidget::~BookWidget()
{
}

void BookWidget::openURL(const KURL & url)
{
	m_url = url;
	m_file.setName(m_url.path());
	if (! m_file.open(IO_ReadOnly)) return;
	
	///@todo Write encoding detection
	m_stream.reset(new TQTextStream(&m_file));
	m_textLines.reset(new TQStringList);
	TQString name(encoding());
	TQTextCodec *codec = TQTextCodec::codecForName(encoding().latin1()); // get the codec
	if (codec)
 		m_stream->setCodec(codec);
	//show progress dialog
	m_progressDlg.reset(new KProgressDialog(this, "progressDlg", "Loading...", 
						"Loading file ", true));
	m_progressDlg->setLabel("Loading file " + m_url.path());
	m_progressDlg->setAllowCancel(true);
	m_progressDlg->progressBar()->setTotalSteps(0);
	connect(m_progressDlg.get(), TQ_SIGNAL(cancelClicked()), TQ_SLOT(cancelLoading()));
	KDialog::centerOnScreen(m_progressDlg.get());
 	m_progressDlg->show();
	//start timer for processing gui events
	m_timer.start(0, false);
	//load bookmark for the book
	loadBookmarks();
	// just for fun, set the status bar
	emit signalChangeStatusbar(m_url.prettyURL());
}

TQString BookWidget::currentURL()
{
	return m_url.path();
}

void BookWidget::drawContent(TQPainter& paint)
{
	const TQRect lrect (rectLeftPage());
	const TQRect rrect(rectRightPage());
	const unsigned int curPage = currentPage();
	//draw pages
	m_renderer->drawPage(paint, lrect, curPage);
	m_renderer->drawPage(paint, rrect, curPage + 1);
}

void BookWidget::drawPageNumbers(TQPainter & paint)
{
	if (m_renderer->isEmpty())
		return;
	const TQRect lrect (rectLeftPage());
	const TQRect rrect(rectRightPage());
	const TQString number = TQString::number(currentPage() + 2);
	// setup font
	TQFont pageFont(font());
	int pointSize = 10;
	pageFont.setPointSize(pointSize);
	pageFont.setBold(false);
	pageFont.setItalic(false);
	pageFont.setUnderline(false);
	paint.setFont(pageFont);
	const TQFontMetrics fm(pageFont);
	const int width = fm.width(number);
	//paint page numbers
	const int offset = 8;
	paint.drawText(lrect.left(), TQWidget::height() - offset,
				TQString::number(currentPage() + 1));
	paint.drawText(rrect.left() + rrect.width() - width,
	               TQWidget::height() - offset, number);
}

void BookWidget::drawBookmark(TQPainter & paint, Bookmark const& bm)
{
	const TQRect lrect (rectLeftPage());
	const TQRect rrect(rectRightPage());
	const TQPixmap & bookmarkImage = Theme::self()->bookmarkPixmap();
	const int width = bookmarkImage.width();
	paint.drawPixmap(TQWidget::width()/2 - width/2, 0, bookmarkImage);
	if (!bm.name().isEmpty())
	{
		paint.save();
		paint.translate(TQWidget::width()/2., 20.);
		paint.rotate(90.);
		const TQString text = bm.name() + " (" + bm.dateTime().toString(TQt::LocalDate) + ")";
		paint.drawText(0, 0, text);
		paint.restore();
	}
}

void BookWidget::paintEvent(TQPaintEvent* event)
{
	// use cached page pixmap if the page wasn't modified
	if (!modified())
	{
		TQRect rect(event->rect());
		bitBlt(this, rect.x(), rect.y(), &m_cachePixmap, rect.x(), rect.y(), rect.width(), rect.height());
		return;
	}
	// do full painting otherwise
	setupPageSize();
	const TQRect lrect (rectLeftPage());
	const TQRect rrect(rectRightPage());
	const unsigned int curPage = currentPage();
	TQPixmap pm(size());
	TQPainter paint(&pm);
	//draw background image
	const TQPixmap & image = Theme::self()->bgPixmap(TQWidget::size());
	paint.drawPixmap(0, 0, image);
	if (m_renderer->busy())
	{
		TQFont f(font());
		f.setPointSize(20);
		paint.setFont(f);
		const TQFontMetrics fm(f);
		TQString text = tr("Rendering...");
		const int w = fm.width(text);
		const int h = fm.height();
		const int dx = (lrect.width() - w) / 2;
		const int dy = (lrect.height() - h) / 2;
		paint.drawText(lrect.x() + dx, lrect.y() + dy, text);
		paint.drawText(rrect.x() + dx, rrect.y() + dy, text);
	}
	else
	{
		paint.setFont(font());
		//draw pages
		drawContent(paint);
		drawPageNumbers(paint);
		//draw bookmark if it is
		typedef Bookmarks::const_iterator IT;
		IT it = std::lower_bound(m_bookmarks.begin(), m_bookmarks.end(), curPage);
		if (it != m_bookmarks.end() && (*it).page() == curPage)
			drawBookmark(paint, *it);
	}
	paint.end();
	bitBlt(this, 0, 0, &pm);
	// store painted pixmap as cache
	m_cachePixmap = pm;
	m_modified = false;
}

void BookWidget::setupPageSize()
{
	TQSize size((width() - m_left_margin - m_right_margin - 2 * m_middle_margin) / 2,
				(height() - m_top_margin - m_bottom_margin));
	m_renderer->setPageSize(size);
}

void BookWidget::mousePressEvent(TQMouseEvent * event)
{
	if (event->button() == TQt::LeftButton)
	{
		if (rectLeftPage().contains(event->pos()))
		{
			prevPage();
		}
		else if (rectRightPage().contains(event->pos()))
		{
			nextPage();
		}
	}
	TQWidget::mousePressEvent(event);
}

const TQRect BookWidget::rectLeftPage() const
{
	return TQRect(m_left_margin, m_top_margin,
	              m_renderer->pageSize().width(),
	              m_renderer->pageSize().height());
}

const TQRect BookWidget::rectRightPage() const
{
	return TQRect(m_left_margin + 2 * m_middle_margin + m_renderer->pageSize().width(),
	              m_top_margin,
	              m_renderer->pageSize().width(),
	              m_renderer->pageSize().height());
}

void BookWidget::clearAll()
{
	setCurrentPage(0);
}


void BookWidget::prevPage()
{
	setCurrentPage(currentPage() - 2);
}

void BookWidget::nextPage()
{
	setCurrentPage(currentPage() + 2);
}

void BookWidget::firstPage()
{
	setCurrentPage(0);
}

void BookWidget::lastPage()
{
	setCurrentPage(m_renderer->pageCount() - 1);
}

void BookWidget::wheelEvent(TQWheelEvent * e)
{
	e->accept();
	if (e->delta() > 0)
		prevPage();
	else nextPage();
}

void BookWidget::resizeEvent(TQResizeEvent * e)
{
	m_modified = true;
	setupPageSize();
	m_renderer->render();
// 	//if size become lower recalc current page
// 	if (currentPage() > m_renderer->pageCount())
// 		lastPage();
	TQWidget::resizeEvent(e);
}

TQSize BookWidget::minimumSizeHint() const
{
	return TQSize(400, 300);
}

void BookWidget::setFont(const TQFont & font)
{
	m_modified = true;
	TQWidget::setFont(font);
	m_renderer->setFont(font);
	update();
}

void BookWidget::setCurrentPage(int number)
{
	// do nothing while renderer is working
	if (m_renderer->busy())
		return;
	if (number >= m_renderer->pageCount())
		number = m_renderer->pageCount() - 1;
	if (number < 0) number = 0;
	//only even page numbers allowed
	int newPage = (number % 2) ? number - 1 : number;
	if (newPage != m_currentPage)
		m_modified = true;
	m_currentPage = newPage;
	update();
}

void BookWidget::keyPressEvent(TQKeyEvent * e)
{
	if (e->key() == TQt::Key_Right || e->key() == TQt::Key_Down 
		|| e->key() == TQt::Key_Space)
	{
		nextPage();
	}
	else if (e->key() == TQt::Key_Left || e->key() == TQt::Key_Up)
	{
		prevPage();
	}
	else if (e->key() == TQt::Key_Home)
	{
		firstPage();
	}
	else if (e->key() == TQt::Key_End)
	{
		lastPage();
	}

	TQWidget::keyPressEvent(e);
}

void BookWidget::loadLine()
{
	static int lineCount = 0;
	if (!m_stream->eof())
	{
		*m_textLines << m_stream->readLine();
		//progress update after every 50 lines
		if (!(++lineCount % 50))
			m_progressDlg->progressBar()->setProgress(
				m_progressDlg->progressBar()->progress() + 1);
		return;
	}
	m_timer.stop();
	//render
	m_renderer->load(*m_textLines);
	//setup and show
	firstPage();
	//clean up
	m_progressDlg.reset();
	m_stream.reset();
	m_textLines.reset();
	m_file.close();
	m_modified = true;
	emit loadingFinished();
}

void BookWidget::cancelLoading()
{
	m_timer.stop();
	if (parentWidget())
		parentWidget()->setCaption("");
	//clean up
	m_progressDlg->hide();
	m_stream.reset();
	m_textLines.reset();
	m_file.close();
	//setup and show
	m_renderer->clear();
	firstPage();
}

void BookWidget::setParaOffset(int offset)
{
	m_modified = true;
	m_renderer->setParaOffset(offset);
	update();
}

void BookWidget::setEncoding(int a_encoding)
{
	m_encoding = a_encoding;
}

void BookWidget::setEncodings(const TQStringList & a_encodings)
{
	m_encodings = a_encodings;
}

void BookWidget::setEncoding(const TQString & a_encoding)
{
	m_encoding = (m_encodings.findIndex(a_encoding));
}

void BookWidget::addBookmark(const TQString& name)
{
	m_bookmarks.push_back(Bookmark(currentPage(), name));
	std::sort(m_bookmarks.begin(), m_bookmarks.end());
	
// 	TDEAction menu = new TDEAction()
	
	saveBookmarks();
}

void BookWidget::saveBookmarks( )
{
	const TQString fileName = "kbookreader/" + m_url.fileName();
	TQFile::remove(TDEGlobal::dirs()->findResource("appdata", m_url.fileName()));
	TDEConfig config(fileName, false, false, "data");
	for (Bookmarks::size_type i = 0; i < m_bookmarks.size(); ++i)
	{
		const Bookmark & bm = m_bookmarks[i];
		config.setGroup(tr("Bookmark %1").arg(i));
		config.writeEntry("page", bm.page());
		config.writeEntry("name", bm.name());
		config.writeEntry("dateTime", bm.dateTime());
	}
}

void BookWidget::loadBookmarks()
{
	m_bookmarks.clear();
	const TQString fileName = "kbookreader/" + m_url.fileName();
	TDEConfig config(fileName, true, false, "data");
	TQStringList groups = config.groupList();
	typedef TQStringList::const_iterator IT;
	for (IT it = groups.constBegin(); it != groups.constEnd(); ++it)
	{
		config.setGroup(*it);
		const TQString name = config.readEntry("name");
		const unsigned int page = config.readUnsignedNumEntry("page");
		const TQDateTime dateTime = config.readDateTimeEntry("dateTime");
		const Bookmark bm(page, name, dateTime);
		m_bookmarks.push_back(bm);
	}
	std::sort(m_bookmarks.begin(), m_bookmarks.end());
}

void BookWidget::setBookmarks(const Bookmarks & bms)
{
	m_bookmarks.clear();
	m_bookmarks = bms;
	std::sort(m_bookmarks.begin(), m_bookmarks.end());
	saveBookmarks();
}

void BookWidget::renderingFinished()
{
	m_modified = true;
	setCurrentPage(currentPage());
	update();
}

#include "bookwidget.moc"
