/***************************************************************************
 *   Copyright (C) 2005, 2006 by Carsten Niehaus <cniehaus@kde.org>        *
 *   Copyright (C) 2005 - 2007 by Pino Toscano <pino@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 "kdeeduglossary.h"

#include <kdebug.h>
#include <klocale.h>
#include <khtml_part.h>
#include <khtmlview.h>
#include <ktreewidgetsearchline.h>
#include <kactioncollection.h>

#include <qevent.h>
#include <qfile.h>
#include <qheaderview.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qlist.h>
#include <qregexp.h>
#include <qsplitter.h>
#include <qstringlist.h>
#include <qtreewidget.h>
#include <QDomDocument>

static const int FirstLetterRole = 0x00b00a00;

static const int GlossaryTreeItemType = QTreeWidgetItem::UserType + 1;

class GlossaryTreeItem : public QTreeWidgetItem
{
    public:
        GlossaryTreeItem( Glossary * g, GlossaryItem * gi )
            : QTreeWidgetItem( GlossaryTreeItemType ), m_g( g ), m_gi( gi )
        {
            setText( 0, m_gi->name() );
        }

        Glossary *glossary() const
        {
            return m_g;
        }

        GlossaryItem *glossaryItem() const
        {
            return m_gi;
        }

    private:
        Glossary *m_g;
        GlossaryItem *m_gi;
};


struct GlossaryInfo
{
    GlossaryInfo( Glossary* g )
        : glossary( g ), folded( true )
    {
    }

    Glossary *glossary;
    bool folded;
};


class GlossaryDialog::Private
{
    public:
        Private( GlossaryDialog *qq )
            : q( qq )
        {
        }

        ~Private()
        {
            QList< GlossaryInfo >::ConstIterator it = m_glossaries.constBegin(), itEnd = m_glossaries.constEnd();
            for ( ; it != itEnd; ++it )
            {
                delete (*it).glossary;
            }

            delete m_htmlpart;
        }

        void rebuildTree();
        QTreeWidgetItem* createItem( const GlossaryInfo& gi ) const;
        QTreeWidgetItem* findTreeWithLetter( const QChar& l, QTreeWidgetItem* item ) const;

        // slots
        void itemActivated( QTreeWidgetItem * item, int column );
        // The user clicked on a href. Find and display the right item
        void displayItem( const KUrl &url, const KParts::OpenUrlArguments& arguments, const KParts::BrowserArguments &browserArguments );

        GlossaryDialog *q;

        QList< GlossaryInfo > m_glossaries;

        KHTMLPart *m_htmlpart;
        QTreeWidget *m_glosstree;
        KTreeWidgetSearchLine *m_search;
        QString m_htmlbasestring;

        KActionCollection* m_actionCollection;
};


Glossary::Glossary( const KUrl& url, const QString& path )
{
	init( url, path );
}

Glossary::Glossary()
{
	init( KUrl(), QString() );
}

Glossary::~Glossary()
{
    qDeleteAll(m_itemlist);
}

void Glossary::init( const KUrl& url, const QString& path )
{
	// setting a generic name for a new glossary
	m_name = i18n( "Glossary" );

	setPicturePath( path );

	if ( !url.isEmpty() )
	{

		QDomDocument doc( "document" );

		if ( loadLayout( doc, url ) )
		{
			setItemlist( readItems( doc ) );
			if ( !m_picturepath.isEmpty() )
				fixImagePath();
		}
	}
}

bool Glossary::loadLayout( QDomDocument &Document, const KUrl& url )
{
	QFile layoutFile( url.path() );

	if ( !layoutFile.exists() )
	{
		kDebug() << "no such file: " << layoutFile.fileName();
		return false;
	}

	if ( !layoutFile.open( QIODevice::ReadOnly ) )
		return false;

	// check if document is well-formed
	if ( !Document.setContent( &layoutFile ) )
	{
		kDebug() << "wrong xml of " << layoutFile.fileName();
		layoutFile.close();
		return false;
	}
	layoutFile.close();

	return true;
}

bool Glossary::isEmpty() const
{
	return m_itemlist.count() == 0;
}

void Glossary::setName( const QString& name )
{
	if ( name.isEmpty())
		return;
	m_name = name;
}

void Glossary::setPicturePath( const QString& path )
{
	if ( path.isEmpty())
		return;
	m_picturepath = path;
}

void Glossary::setBackgroundPicture( const QString& filename )
{
	if ( filename.isEmpty())
		return;
	m_backgroundpicture = filename;
}

void Glossary::fixImagePath()
{
	QString imgtag = "<img src=\"" + m_picturepath + '/' + "\\1\" />";
	QRegExp exp( "\\[img\\]([^[]+)\\[/img\\]" );

  foreach (GlossaryItem * item, m_itemlist) {
      QString tmp = item->desc();
      while ( exp.indexIn( tmp ) > -1 )
      {
          tmp = tmp.replace( exp, imgtag );
      }
      item->setDesc( tmp );
  }
}

QList<GlossaryItem*> Glossary::readItems( QDomDocument &itemDocument )
{
	QList<GlossaryItem*> list;

	QDomNodeList itemList;
	QDomNodeList refNodeList;
	QDomElement itemElement;
	QStringList reflist;

	itemList = itemDocument.elementsByTagName( "item" );

	const uint num = itemList.count();
	for ( uint i = 0; i < num; ++i )
	{
		reflist.clear();
		GlossaryItem *item = new GlossaryItem();
		
		itemElement = ( const QDomElement& ) itemList.item( i ).toElement();
		
		QDomNode nameNode = itemElement.namedItem( "name" );
		QDomNode descNode = itemElement.namedItem( "desc" );
		
		QString picName = itemElement.namedItem( "picture" ).toElement().text();
		QDomElement refNode = ( const QDomElement& ) itemElement.namedItem( "references" ).toElement();

		QString desc = i18n( descNode.toElement().text().toUtf8() );
		if ( !picName.isEmpty() )
			desc.prepend("[img]"+picName +"[/img][brclear][br]" );

		item->setName( i18n( nameNode.toElement( ).text().toUtf8() ) );
		
		desc = desc.replace("[b]", "<b>" );
		desc = desc.replace("[/b]", "</b>" );
		desc = desc.replace("[i]", "<i>" );
		desc = desc.replace("[/i]", "</i>" );
		desc = desc.replace("[sub]", "<sub>" );
		desc = desc.replace("[/sub]", "</sub>" );
		desc = desc.replace("[sup]", "<sup>" );
		desc = desc.replace("[/sup]", "</sup>" );
		desc = desc.replace("[br]", "<br />" );
		desc = desc.replace("[brclear]", "<br clear=\"left\"/>" );
		item->setDesc( desc );

		refNodeList = refNode.elementsByTagName( "refitem" );
		for ( int it = 0; it < refNodeList.count(); it++ )
		{
			reflist << i18n( refNodeList.item( it ).toElement().text().toUtf8() );
		}
		item->setRef( reflist );
		
		list.append( item );
	}
	
	return list;
}

void Glossary::addItem( GlossaryItem* item )
{
    m_itemlist.append( item );
}

QList<GlossaryItem*> Glossary::itemlist()const
{
    return m_itemlist;
}

void Glossary::clear()
{
    m_itemlist.clear();
}

QString Glossary::name()const
{
    return m_name;
}

void Glossary::setItemlist( QList<GlossaryItem*> list )
{
    m_itemlist = list;
}

QString Glossary::picturePath()const
{
    return m_picturepath;
}

QString Glossary::backgroundPicture()const
{
    return m_backgroundpicture;
}

GlossaryDialog::GlossaryDialog( QWidget *parent )
    : KDialog( parent ), d( new Private( this ) )
{
	setCaption( i18n( "Glossary" ) );
	setButtons( Close );
	setDefaultButton( Close );

	//this string will be used for all items. If a backgroundpicture should
	//be used call Glossary::setBackgroundPicture().
	d->m_htmlbasestring = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><body%1>" ;

	QWidget *main = new QWidget( this );
	setMainWidget( main );
	QVBoxLayout *vbox = new QVBoxLayout( main );
	vbox->setMargin( 0 );

	QHBoxLayout *hbox = new QHBoxLayout();

	QLabel *lbl = new QLabel( main );
	lbl->setText( i18n( "&Search:" ) );
	hbox->addWidget( lbl );

	d->m_search = new KTreeWidgetSearchLine( main );
	d->m_search->setObjectName( "search-line" );
	lbl->setBuddy( lbl );
	hbox->addWidget( d->m_search );
	vbox->addLayout( hbox );
	setFocusProxy(d->m_search);
 
	QSplitter *vs = new QSplitter( main );
	vbox->addWidget( vs );

	d->m_glosstree = new QTreeWidget( vs );
	d->m_glosstree->setObjectName( "treeview" );
	d->m_glosstree->setHeaderLabel( "entries" );
	d->m_glosstree->header()->hide();
	d->m_glosstree->setRootIsDecorated( true );
 
	d->m_search->addTreeWidget( d->m_glosstree );
 
	d->m_htmlpart = new KHTMLPart( vs );

    connect( d->m_htmlpart->browserExtension(), SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
             this, SLOT(displayItem(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)) );
	connect( d->m_glosstree, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(itemActivated(QTreeWidgetItem*,int)) );

	resize( 600, 400 );
}

GlossaryDialog::~GlossaryDialog()
{
    delete d;
}

void GlossaryDialog::keyPressEvent(QKeyEvent* e)
{
	if (e->key() == Qt::Key_Return) {
		e->ignore();
	}
	KDialog::keyPressEvent(e);
}

void GlossaryDialog::Private::displayItem( const KUrl &url, const KParts::OpenUrlArguments &, const KParts::BrowserArguments & )
{
	// using the "path" part of a kurl as reference
	QString myurl = url.path().toLower();

    QTreeWidgetItemIterator it( m_glosstree );
    while ( *it )
    {
        if ( (*it)->type() == GlossaryTreeItemType && (*it)->text( 0 ).toLower() == myurl )
        {
             // force the item to be selected
             m_glosstree->setCurrentItem( *it );
             // display its content
             itemActivated( (*it), 0 );
             break;
        }
        else
            ++it;
     }
}

void GlossaryDialog::Private::rebuildTree()
{
    m_glosstree->clear();

    QList< GlossaryInfo >::ConstIterator it = m_glossaries.constBegin(), itEnd = m_glossaries.constEnd();
    for ( ; it != itEnd; ++it )
    {
        m_glosstree->addTopLevelItem( createItem( *it ) );
    }
}

QTreeWidgetItem* GlossaryDialog::Private::createItem( const GlossaryInfo& gi ) const
{
    Glossary *glossary = gi.glossary;
    bool folded = gi.folded;
    QTreeWidgetItem *main = new QTreeWidgetItem();
    main->setText( 0, glossary->name() );
    main->setFlags( Qt::ItemIsEnabled );
    foreach ( GlossaryItem * item, glossary->itemlist() )
    {
        if ( folded )
        {
            QChar thisletter = item->name().toUpper().at(0);
            QTreeWidgetItem *thisletteritem = findTreeWithLetter( thisletter, main );
            if ( !thisletteritem )
            {
                thisletteritem = new QTreeWidgetItem( main );
                thisletteritem->setText( 0, QString( thisletter ) );
                thisletteritem->setFlags( Qt::ItemIsEnabled );
                thisletteritem->setData( 0, FirstLetterRole, thisletter );
            }
            thisletteritem->addChild( new GlossaryTreeItem( glossary, item ) );
        }
        else
        {
            main->addChild( new GlossaryTreeItem( glossary, item ) );
        }

    }
    return main;
}

void GlossaryDialog::addGlossary( Glossary* newgloss, bool folded )
{
    if ( !newgloss || newgloss->isEmpty() )
        return;

    GlossaryInfo gi( newgloss );
    gi.folded = folded;
    d->m_glossaries.append( gi );

    d->m_glosstree->addTopLevelItem( d->createItem( gi ) );
    d->m_glosstree->sortItems( 0, Qt::AscendingOrder );
}

QTreeWidgetItem* GlossaryDialog::Private::findTreeWithLetter( const QChar& l, QTreeWidgetItem* item ) const
{
    int count = item->childCount();
    for ( int i = 0; i < count; ++i )
    {
        QTreeWidgetItem *itemchild = item->child( i );
        if ( itemchild->data( 0, FirstLetterRole ).toChar() == l )
            return itemchild;
    }
    return 0;
}

void GlossaryDialog::Private::itemActivated( QTreeWidgetItem * item, int column )
{
    Q_UNUSED(column)
    if ( !item || item->type() != GlossaryTreeItemType )
        return;

    GlossaryTreeItem *glosstreeitem = static_cast< GlossaryTreeItem * >( item );
    GlossaryItem * glossitem = glosstreeitem->glossaryItem();
    QString html;
    QString bg_picture = glosstreeitem->glossary()->backgroundPicture();
    if ( !bg_picture.isEmpty() )
    {
            html = " background=\"" + bg_picture + "\"";
    }

    html = m_htmlbasestring.arg( html );
    html += glossitem->toHtml() + "</body></html>";

    m_htmlpart->begin();
    m_htmlpart->write( html );
    m_htmlpart->end();
}

void GlossaryItem::setRef( const QStringList& s )
{
	m_ref = s;
	m_ref.sort();
}

QString GlossaryItem::toHtml() const
{
	QString code = "<h1>" + m_name + "</h1>" + m_desc + parseReferences();

	return code;
}

QString GlossaryItem::parseReferences() const
{
	if ( m_ref.isEmpty() )
		return QString();

	QString htmlcode = "<h3>" + i18n( "References" ) + "</h3><ul type=\"disc\">";
	static QString basehref = QString( "<li><a href=\"item:%1\" title=\"%2\">%3</a></li>" );
	
	foreach ( const QString& ref, m_ref )
	{
		htmlcode += basehref.arg( KUrl::toPercentEncoding( ref ), i18n( "Go to '%1'", ref ), ref );
	}
	htmlcode += "</ul>";

	return htmlcode;
}

void GlossaryItem::setName( const QString& s )
{
    m_name = s;
}

void GlossaryItem::setDesc( const QString& s)
{
    m_desc = s;
}

void GlossaryItem::setPictures( const QString& s ){
    m_pic = QStringList(s);
}

QString GlossaryItem::name() const 
{
    return m_name;
}

QString GlossaryItem::desc() const 
{
    return m_desc;
}

QStringList GlossaryItem::ref() const 
{
    return m_ref;
}

QStringList GlossaryItem::pictures() const 
{
    return m_pic;
}

#include "kdeeduglossary.moc"
