/***************************************************************************
 *   Copyright (C) 2004 by Alexander Dymo                                  *
 *   cloudtemple@mksat.net                                                 *
 *                                                                         *
 *   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 "docdevhelpplugin.h"

#include <unistd.h>

#include <tqdom.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqdialog.h>

#include <kurl.h>
#include <tdeaboutdata.h>
#include <tdeconfig.h>
#include <tdelocale.h>
#include <kstandarddirs.h>

#include <kdevgenericfactory.h>
#include <kdevplugininfo.h>

#include "../../../../config.h"

class DevHelpDocumentationCatalogItem: public DocumentationCatalogItem
{
public:
    DevHelpDocumentationCatalogItem(const TQString &devHelpFile, DocumentationPlugin* plugin,
        TDEListView *parent, TDEListViewItem *after, const TQString &name)
        :DocumentationCatalogItem(plugin, parent, after, name), m_devHelpFile(devHelpFile)
    {
    }
    DevHelpDocumentationCatalogItem(const TQString &devHelpFile, DocumentationPlugin* plugin,
        DocumentationItem *parent, const TQString &name)
        :DocumentationCatalogItem(plugin, parent, name), m_devHelpFile(devHelpFile)
    {
    }
    TQString devHelpFile() const { return m_devHelpFile; }

    virtual TQString cacheVersion() const {
        unsigned int checksum=0;
        for(int a=0;a<m_devHelpFile.length(); a++) {
            checksum += (a+1) * (int)m_devHelpFile[a];
        }
        TQString str;
        TQTextOStream( &str ) << checksum;
        return str;
    }
    
protected:

private:
    TQString m_devHelpFile;
};


static const KDevPluginInfo data("docdevhelpplugin");
typedef KDevGenericFactory<DocDevHelpPlugin> DocDevHelpPluginFactory;
K_EXPORT_COMPONENT_FACTORY( libdocdevhelpplugin, DocDevHelpPluginFactory(data) )

DocDevHelpPlugin::DocDevHelpPlugin(TQObject* parent, const char* name,
    const TQStringList /*args*/)
    :DocumentationPlugin(DocDevHelpPluginFactory::instance()->config(), parent, name)
{
    setCapabilities(Index | FullTextSearch | ProjectDocumentation);
    autoSetup();
}

DocDevHelpPlugin::~DocDevHelpPlugin()
{
}

DocumentationCatalogItem* DocDevHelpPlugin::createCatalog(TDEListView* contents, TDEListViewItem *after, const TQString& title, const TQString& url)
{
    return new DevHelpDocumentationCatalogItem(url, this, contents, after, title);
}

TQPair<KFile::Mode, TQString> DocDevHelpPlugin::catalogLocatorProps()
{
    return TQPair<KFile::Mode, TQString>(KFile::File, "*.devhelp");
}

TQString DocDevHelpPlugin::catalogTitle(const TQString& url)
{
    TQFileInfo fi(url);
    if (!fi.exists())
        return TQString();

    TQFile f(url);
    if (!f.open(IO_ReadOnly))
        return TQString();
    
    TQDomDocument doc;
    if (!doc.setContent(&f))
        return TQString();
    f.close();

    TQDomElement docEl = doc.documentElement();

    return docEl.attribute("title", TQString());
}

TQString DocDevHelpPlugin::pluginName() const
{
    return i18n("Devhelp Documentation Collection");
}

TQStringList DocDevHelpPlugin::fullTextSearchLocations()
{
    TQStringList locs;
        
    TQMap<TQString, TQString> entryMap = config->entryMap("Locations");

    for (TQMap<TQString, TQString>::const_iterator it = entryMap.begin();
        it != entryMap.end(); ++it)
    {
        config->setGroup("Search Settings");
        if (config->readBoolEntry(it.key(), false))
        {
            config->setGroup("Locations");
            TQFileInfo fi(config->readPathEntry(it.key()));
            locs << fi.dirPath(true);
        }
    }
    
    return locs;
}

bool DocDevHelpPlugin::needRefreshIndex(DocumentationCatalogItem* item)
{
    DevHelpDocumentationCatalogItem *dhItem = dynamic_cast<DevHelpDocumentationCatalogItem *>(item);
    if (!dhItem)
        return false;
    
    TQFileInfo fi(dhItem->devHelpFile());
    config->setGroup("Index");
    if (fi.lastModified() > config->readDateTimeEntry(dhItem->text(0), new TQDateTime()))
    {
        kdDebug() << "need rebuild index for " << dhItem->text(0) << endl;
        config->writeEntry(item->text(0), fi.lastModified());
        return true;
    }
    else
        return false;
}

void DocDevHelpPlugin::autoSetupPlugin()
{
    // Clear groups, to allow for re-autosetup calls
    config->deleteGroup("Index");
    config->deleteGroup("Index Settings");
    config->deleteGroup("Locations");
    config->deleteGroup("Search Settings");
    config->deleteGroup("TOC Settings");
    config->sync();
    
    TQValueStack<TQString> scanStack;
    
    pushToScanStack(scanStack, getenv("DEVHELP_SEARCH_PATH"));
    pushToScanStack(scanStack, TQString(getenv("HOME")) + "/.devhelp/books");
    
    TQString dhexepath = DocDevHelpPluginFactory::instance()->dirs()->findExe("devhelp");
    if (!dhexepath.isEmpty())
    {
        TQFileInfo fi(dhexepath);
        TQString path = KURL(fi.dirPath(true)).upURL().path(1);
        pushToScanStack(scanStack, path + "share/devhelp/books");
        pushToScanStack(scanStack, path + "share/gtk-doc/html");
    }
    
    pushToScanStack(scanStack, "/usr/share/gtk-doc/html");
    pushToScanStack(scanStack, "/usr/share/devhelp/books/");
    pushToScanStack(scanStack, "/usr/local/share/devhelp/books");
    pushToScanStack(scanStack, "/usr/local/share/gtk-doc/html");
    pushToScanStack(scanStack, "/opt/gnome/share/devhelp/books");
    pushToScanStack(scanStack, "/opt/gnome/share/gtk-doc/html");
    pushToScanStack(scanStack, "/opt/gnome2/share/devhelp/books");
    pushToScanStack(scanStack, "/opt/gnome2/share/gtk-doc/html");

    //fill the list of scan dirs (with subdirectories)
    TQStringList scanList;
    TQDir dir;
    do 
    {
        dir.setPath(scanStack.pop());
        if (!dir.exists())
            continue;
        scanList << dir.path();

        const TQFileInfoList *dirEntries = dir.entryInfoList();
        if ( !dirEntries ) continue;
        TQPtrListIterator<TQFileInfo> it(*dirEntries);
        for (; it.current(); ++it) 
        {
            TQString fileName = it.current()->fileName();
            if (fileName == "." || fileName == "..")
                continue;
            TQString path = it.current()->absFilePath();
            if (it.current()->isDir()) 
            {
                scanStack.push(path);
            }
        }
    } while (!scanStack.isEmpty());

    for (TQStringList::const_iterator it = scanList.begin(); it != scanList.end(); ++it)
    {
        scanDevHelpDir(*it);
    }

    config->sync();
}

void DocDevHelpPlugin::scanDevHelpDir(const TQString &path)
{
    TQDir d(path);
    if (! d.exists() || !d.isReadable())
        return;
    
    d.setFilter(TQDir::Files);
    //scan for *.devhelp files
    const TQFileInfoList *list = d.entryInfoList();
    TQFileInfoListIterator it( *list );
    TQFileInfo *fi;
    while ( (fi = it.current()) != 0 ) 
    {
        if (fi->extension() == "devhelp")
        {
            config->setGroup("Locations");
            config->writePathEntry(catalogTitle(fi->absFilePath()), fi->absFilePath());
        }
        ++it;
    }
}

void DocDevHelpPlugin::pushToScanStack(TQValueStack<TQString> &stack, const TQString &value)
{
    if ( (!value.isEmpty()) && (!stack.contains(value)) )
    {
        stack << value;
        kdDebug() << "Devhelp scan stack: +: " << value << endl;
    }
}

void DocDevHelpPlugin::createIndex(IndexBox* index, DocumentationCatalogItem* item)
{
    DevHelpDocumentationCatalogItem *dhItem = dynamic_cast<DevHelpDocumentationCatalogItem *>(item);
    if (!dhItem)
        return;
    
    TQFileInfo fi(dhItem->devHelpFile());

    TQFile f(dhItem->devHelpFile());
    if (!f.open(IO_ReadOnly))
    {
        kdDebug(9002) << "Could not read" << dhItem->devHelpFile() << endl;
        return;
    }

    TQDomDocument doc;
    if (!doc.setContent(&f))
    {
        kdDebug() << "Not a valid devhelp file: " << dhItem->devHelpFile() << endl;
        return;
    }
    f.close();
    
    TQString baseUrl = KURL(dhItem->devHelpFile()).directory();
    
    TQDomElement docEl = doc.documentElement();
    TQDomElement chaptersEl = docEl.namedItem("functions").toElement();
    TQDomElement childEl = chaptersEl.firstChild().toElement();
    while (!childEl.isNull())
    {
        if (childEl.tagName() == "function")
        {
            TQString name = childEl.attribute("name");
            TQString url = childEl.attribute("link");
            
            IndexItemProto *ii = new IndexItemProto(this, item, index, name, item->text(0));
            ii->addURL(KURL(baseUrl+"/"+url));
        }
        childEl = childEl.nextSibling().toElement();
    }
}

void DocDevHelpPlugin::createTOC(DocumentationCatalogItem* item)
{
    DevHelpDocumentationCatalogItem *dhItem = dynamic_cast<DevHelpDocumentationCatalogItem *>(item);
    if (!dhItem)
        return;
    
    TQFileInfo fi(dhItem->devHelpFile());

    TQFile f(dhItem->devHelpFile());
    if (!f.open(IO_ReadOnly))
    {
        kdDebug(9002) << "Could not read" << dhItem->devHelpFile() << endl;
        return;
    }

    TQDomDocument doc;
    if (!doc.setContent(&f))
    {
        kdDebug() << "Not a valid devhelp file: " << dhItem->devHelpFile() << endl;
        return;
    }
    f.close();
    
    TQDomElement docEl = doc.documentElement();
    TQDomElement chaptersEl = docEl.namedItem("chapters").toElement();

    TQDomElement childEl = chaptersEl.lastChild().toElement();
    TQString baseUrl = KURL(dhItem->devHelpFile()).directory();
    addTocSect(dhItem, childEl, baseUrl, true);
}

void DocDevHelpPlugin::addTocSect(DocumentationItem *parent, TQDomElement childEl, 
    TQString baseUrl, bool book)
{
    while (!childEl.isNull())
    {
        if ( (childEl.tagName() == "sub") || (childEl.tagName() == "chapter"))
        {
            TQString name = childEl.attribute("name");
            TQString url = childEl.attribute("link");
            
            if (name.isEmpty() && url.contains("ix"))
                name = "Index";
            
            DocumentationItem *item = new DocumentationItem(
                book ? DocumentationItem::Book : DocumentationItem::Document, parent, name);
            item->setURL(KURL(baseUrl+"/"+url));
            
            TQDomElement grandchildEl = childEl.lastChild().toElement();
            addTocSect(item, grandchildEl, baseUrl);
        }
        childEl = childEl.previousSibling().toElement();
    }
}

void DocDevHelpPlugin::setCatalogURL(DocumentationCatalogItem* item)
{
    DevHelpDocumentationCatalogItem *dhItem = dynamic_cast<DevHelpDocumentationCatalogItem *>(item);
    if (!dhItem)
        return;
    
    TQFileInfo fi(dhItem->devHelpFile());

    TQFile f(dhItem->devHelpFile());
    if (!f.open(IO_ReadOnly))
    {
        kdDebug(9002) << "Could not read" << dhItem->devHelpFile() << endl;
        return;
    }
    TQDomDocument doc;
    if (!doc.setContent(&f))
    {
        kdDebug(9002) << "Not a valid Devhelp file: " << dhItem->devHelpFile() << endl;
        return;
    }
    f.close();

    TQDomElement docEl = doc.documentElement();
    TQDomElement titleEl = docEl.namedItem("book").toElement();

    if (item->url().isEmpty())
    {
        KURL url(fi.dirPath(true) + "/" + docEl.attribute("link", TQString()));
        item->setURL(url);
    }
}

ProjectDocumentationPlugin *DocDevHelpPlugin::projectDocumentationPlugin(ProjectDocType type)
{
    if (type == APIDocs)
        return new ProjectDocumentationPlugin(this, type);
    return DocumentationPlugin::projectDocumentationPlugin(type);
}

#include "docdevhelpplugin.moc"
