/*
 *  This file is part of the TDE Help Center
 *
 *  Copyright (C) 2002 Frerich Raabe (raabe@kde.org)
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "toc.h"

#include "docentry.h"

#include <kiconloader.h>
#include <kprocess.h>
#include <kstandarddirs.h>
#include <kdebug.h>

#include <tqdir.h>
#include <tqfileinfo.h>

#include <sys/stat.h>

using namespace KHC;

class TOCItem : public NavigatorItem
{
	public:
		TOCItem( TOC *parent, TQListViewItem *parentItem, TQListViewItem *after, const TQString &text );

		const TOC *toc() const { return m_toc; }

	private:
		TOC *m_toc;
};

class TOCChapterItem : public TOCItem
{
	public:
		TOCChapterItem( TOC *toc, NavigatorItem *parent, TQListViewItem *after, const TQString &title, 
				const TQString &name );

		virtual TQString url();
			
	private:
		TQString m_name;
};

class TOCSectionItem : public TOCItem
{
	public:
		TOCSectionItem( TOC *toc, TOCChapterItem *parent, TQListViewItem *after, const TQString &title, 
				const TQString &name );

		virtual TQString url();
	
	private:
		TQString m_name;
};

TOC::TOC( NavigatorItem *parentItem )
{
	m_parentItem = parentItem;
}

void TOC::build( const TQString &file )
{
	TQFileInfo fileInfo( file );
	TQString fileName = fileInfo.absFilePath();
	const TQStringList resourceDirs = TDEGlobal::dirs()->resourceDirs( "html" );
	TQStringList::ConstIterator it = resourceDirs.begin();
	TQStringList::ConstIterator end = resourceDirs.end();
	for ( ; it != end; ++it ) {
		if ( fileName.startsWith( *it ) ) {
			fileName.remove( 0, ( *it ).length() );
			break;
		}
	}

	TQString cacheFile = fileName.replace( TQDir::separator(), "__" );
	m_cacheFile = locateLocal( "cache", "help/" + cacheFile );
	m_sourceFile = file;

	if ( cacheStatus() == NeedRebuild )
		buildCache();
	else
		fillTree();
}

TOC::CacheStatus TOC::cacheStatus() const
{
	if ( !TQFile::exists( m_cacheFile ) ||
	     sourceFileCTime() != cachedCTime() )
		return NeedRebuild;

	return CacheOk;
}

int TOC::sourceFileCTime() const
{
	struct stat stat_buf;
	stat( TQFile::encodeName( m_sourceFile ).data(), &stat_buf );

	return stat_buf.st_ctime;
}

int TOC::cachedCTime() const
{
	TQFile f( m_cacheFile );
	if ( !f.open( IO_ReadOnly ) )
		return 0;
	
	TQDomDocument doc;
	if ( !doc.setContent( &f ) )
		return 0;

	TQDomComment timestamp = doc.documentElement().lastChild().toComment();

	return timestamp.data().stripWhiteSpace().toInt();
}

void TOC::buildCache()
{
	TDEProcess *meinproc = new TDEProcess;
	connect( meinproc, TQT_SIGNAL( processExited( TDEProcess * ) ),
	         this, TQT_SLOT( meinprocExited( TDEProcess * ) ) );

	*meinproc << locate( "exe", "meinproc" );
	*meinproc << "--stylesheet" << locate( "data", "khelpcenter/table-of-contents.xslt" );
	*meinproc << "--output" << m_cacheFile;
	*meinproc << m_sourceFile;

	meinproc->start( TDEProcess::NotifyOnExit );
}

void TOC::meinprocExited( TDEProcess *meinproc )
{
	if ( !meinproc->normalExit() || meinproc->exitStatus() != 0 ) {
		delete meinproc;
		return;
	}

	delete meinproc;

	TQFile f( m_cacheFile );
	if ( !f.open( IO_ReadWrite ) )
		return;

	TQDomDocument doc;
	if ( !doc.setContent( &f ) )
		return;

	TQDomComment timestamp = doc.createComment( TQString::number( sourceFileCTime() ) );
	doc.documentElement().appendChild( timestamp );

	f.at( 0 );
	TQTextStream stream( &f );
	stream.setEncoding( TQTextStream::UnicodeUTF8 );
	stream << doc.toString();

	f.close();

	fillTree();
}

void TOC::fillTree()
{
	TQFile f( m_cacheFile );
	if ( !f.open( IO_ReadOnly ) )
		return;

	TQDomDocument doc;
	if ( !doc.setContent( &f ) )
		return;
	
	TOCChapterItem *chapItem = 0;
	TQDomNodeList chapters = doc.documentElement().elementsByTagName( "chapter" );
	for ( unsigned int chapterCount = 0; chapterCount < chapters.count(); chapterCount++ ) {
		TQDomElement chapElem = chapters.item( chapterCount ).toElement();
		TQDomElement chapTitleElem = childElement( chapElem, TQString::fromLatin1( "title" ) );
		TQString chapTitle = chapTitleElem.text().simplifyWhiteSpace();
		TQDomElement chapRefElem = childElement( chapElem, TQString::fromLatin1( "anchor" ) );
		TQString chapRef = chapRefElem.text().stripWhiteSpace();

		chapItem = new TOCChapterItem( this, m_parentItem, chapItem, chapTitle, chapRef );

		TOCSectionItem *sectItem = 0;
		TQDomNodeList sections = chapElem.elementsByTagName( "section" );
		for ( unsigned int sectCount = 0; sectCount < sections.count(); sectCount++ ) {
			TQDomElement sectElem = sections.item( sectCount ).toElement();
			TQDomElement sectTitleElem = childElement( sectElem, TQString::fromLatin1( "title" ) );
			TQString sectTitle = sectTitleElem.text().simplifyWhiteSpace();
			TQDomElement sectRefElem = childElement( sectElem, TQString::fromLatin1( "anchor" ) );
			TQString sectRef = sectRefElem.text().stripWhiteSpace();

			sectItem = new TOCSectionItem( this, chapItem, sectItem, sectTitle, sectRef );
		}
	}

  m_parentItem->setOpen( true );
}

TQDomElement TOC::childElement( const TQDomElement &element, const TQString &name )
{
	TQDomElement e;
	for ( e = element.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement() )
		if ( e.tagName() == name )
			break;
	return e;
}

void TOC::slotItemSelected( TQListViewItem *item )
{
	TOCItem *tocItem;
	if ( ( tocItem = dynamic_cast<TOCItem *>( item ) ) )
		emit itemSelected( tocItem->entry()->url() );

	item->setOpen( !item->isOpen() );
}

TOCItem::TOCItem( TOC *toc, TQListViewItem *parentItem, TQListViewItem *after, const TQString &text )
	: NavigatorItem( new DocEntry( text ), parentItem, after )
{
        setAutoDeleteDocEntry( true );
	m_toc = toc;
}

TOCChapterItem::TOCChapterItem( TOC *toc, NavigatorItem *parent, TQListViewItem *after,
				const TQString &title, const TQString &name )
	: TOCItem( toc, parent, after, title ),
	m_name( name )
{
	setOpen( false );
	entry()->setUrl(url());
}

TQString TOCChapterItem::url()
{
	return "help:" + toc()->application() + "/" + m_name + ".html";
}

TOCSectionItem::TOCSectionItem( TOC *toc, TOCChapterItem *parent, TQListViewItem *after,
				const TQString &title, const TQString &name )
	: TOCItem( toc, parent, after, title ),
	m_name( name )
{
	setPixmap( 0, SmallIcon( "text-x-generic" ) );
	entry()->setUrl(url());
}

TQString TOCSectionItem::url()
{
	if ( static_cast<TOCSectionItem *>( parent()->firstChild() ) == this )
		return static_cast<TOCChapterItem *>( parent() )->url() + "#" + m_name;
	
	return "help:" + toc()->application() + "/" + m_name + ".html";
}

#include "toc.moc"
// vim:ts=2:sw=2:et