/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbUtils module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include "hbdocumentloader_p.h"
#include "hbdocumentloaderactions_p.h"
#include "hbdocumentloadersyntax_p.h"
#include "hbdocumentloader.h"

#include <hbxmlloaderbinaryactions_p.h>
#include <hbxmlloaderbinarysyntax_p.h>

#ifndef HB_BOOTSTRAPPED

#include <QGraphicsWidget>
#include <QCoreApplication>
#include <QDir>
#include <QPluginLoader>
#include "hbdocumentloaderplugin.h"
#include <hbmainwindow.h>
#include <hbinstance.h>
#include <QBuffer>

#endif // HB_BOOTSTRAPPED

/*
    \class HbDocumentLoaderPrivate
    \internal
    \proto
*/

//#define DEBUG_TIMES

#ifdef DEBUG_TIMES
#include <QTime>
#include <QDebug>
static QTime debugTime;

#ifdef Q_OS_SYMBIAN
#include <e32debug.h>
#endif

void doDebugPrintX(const char* text)
{
#ifdef Q_OS_SYMBIAN
    RDebug::Printf(text);
#else
    qDebug() << text;
#endif
}

// Takes standard c-format.
void debugPrintX(const char* cformat, ...)
{
    va_list ap;
    va_start(ap, cformat);
    QString str = QString().vsprintf(cformat, ap);
    va_end(ap);
    doDebugPrintX(str.toAscii().constData());
}

#endif // DEBUG_TIMES


HbDocumentLoaderPrivate::HbDocumentLoaderPrivate(const HbMainWindow *window)
: q_ptr(0)
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(window);
#else
    actions = new HbDocumentLoaderActions(this, window);
    binarysyntax = new HbXmlLoaderBinarySyntax(actions);
    pluginManager = new HbDocumentLoaderPluginManager;
#endif
    binaryactions = new HbXmlLoaderBinaryActions();
    syntax = new HbDocumentLoaderSyntax(binaryactions);
}

HbDocumentLoaderPrivate::~HbDocumentLoaderPrivate()
{
    delete binaryactions;
    delete syntax;
#ifndef HB_BOOTSTRAPPED
    delete pluginManager;
    delete actions;
    delete binarysyntax;
#endif
}

bool HbDocumentLoaderPrivate::createBinary( QIODevice *srcDevice, QIODevice *dstDevice )
{
    bool result = true;
    syntax->setActions( binaryactions );
#ifdef DEBUG_TIMES
    debugTime.restart();
    debugPrintX("MYTRACE: DocML create binary, start");
#endif
    binaryactions->setOutputDevice( dstDevice );
    // TODO: Has to process all sections!
    QList<QString> sectionsList;
    QHash< QString, qint64 > sectionsPositionList;
    qint64 startPos = srcDevice->pos();
    if( syntax->scanForSections( srcDevice, sectionsList ) ) {
        srcDevice->seek( startPos );
        result = syntax->load( srcDevice, "" );
        if( !sectionsList.isEmpty() ) {
            for( int i = 0; i < sectionsList.size(); i++ ) {
                sectionsPositionList[ sectionsList.at( i ) ] = dstDevice->pos();
                srcDevice->seek( startPos );
                result &= syntax->load( srcDevice, sectionsList.at( i ) );
            }
        }
    } else {
        result = false;
    }
    qint64 sectionsMetaDataPos = dstDevice->pos();
    QDataStream stream( dstDevice );
    stream << sectionsPositionList;
    stream << sectionsMetaDataPos;
#ifdef DEBUG_TIMES
    debugPrintX("MYTRACE: DocML create binary, end: %d", debugTime.elapsed());
#endif
    return result;
}

bool HbDocumentLoaderPrivate::load( QIODevice *device, const QString &section )
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(device);
    Q_UNUSED(section);
    return false;
#else
    bool result(true);

    const bool originalTextMode = device->isTextModeEnabled();

    if (binarysyntax->isBinary(device)) {
        device->setTextModeEnabled( false );    
        binarysyntax->setActions(actions);
#ifdef DEBUG_TIMES
        debugTime.restart();
        debugPrintX("MYTRACE: DocML load binary, start");
#endif
        result = binarysyntax->load( device, section );
#ifdef DEBUG_TIMES
        debugPrintX("MYTRACE: DocML load binary, end: %d", debugTime.elapsed());
#endif
    } else {
        device->setTextModeEnabled( true );    
        syntax->setActions(actions);
#ifdef DEBUG_TIMES
        debugTime.restart();
        debugPrintX("MYTRACE: DocML load plain text, start");
#endif
        result = syntax->load( device, section );
#ifdef DEBUG_TIMES
        debugPrintX("MYTRACE: DocML load plain text, end: %d", debugTime.elapsed());
#endif
    }
    device->setTextModeEnabled( originalTextMode );    
    return result;
#endif
}

QList<QObject *> HbDocumentLoaderPrivate::takeAll()
{
#ifdef HB_BOOTSTRAPPED
    QList<QObject *> list;
    return list;
#else
    return actions->takeAll();
#endif
}

QGraphicsWidget* HbDocumentLoaderPrivate::findWidget(const QString &name) const
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(name);
    return 0;
#else
    return actions->findWidget( name );
#endif
}

QObject* HbDocumentLoaderPrivate::findObject(const QString &name) const
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(name);
    return 0;
#else
    return actions->findObject( name );
#endif
}


QObject *HbDocumentLoaderPrivate::lookUp(const QString& type, const QString &name, const QString &plugin)
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(type);
    Q_UNUSED(name);
    Q_UNUSED(plugin);
    return 0;
#else
    if ( plugin.isEmpty() ) {
        return q_ptr->createObject( type, name );
    } else {
        QObject *object = pluginManager->lookUp( type, name, plugin );
        if ( !object ) {
            // use factory loader for fallback
            object = q_ptr->createObject( type, name );
        }
        return object;
    }
#endif
}

void HbDocumentLoaderPrivate::reset()
{
#ifndef HB_BOOTSTRAPPED
    actions->reset();
#endif
}

bool HbDocumentLoaderPrivate::setObjectTree( QList<QObject *> roots )
{
#ifdef HB_BOOTSTRAPPED
    Q_UNUSED(roots);
    return false;
#else
    return actions->setObjectTree( roots );
#endif
}

QString HbDocumentLoaderPrivate::version()
{
#ifdef HB_BOOTSTRAPPED
    return QString();
#else
    return HbDocumentLoaderSyntax::version();
#endif
}


#ifndef HB_BOOTSTRAPPED
HbDocumentLoaderPluginManager::HbDocumentLoaderPluginManager()
{
}

HbDocumentLoaderPluginManager::~HbDocumentLoaderPluginManager()
{
    while ( mPlugins.count() ) {
        QPluginLoader *loader = mPlugins.at(0);
        mPlugins.removeAt( 0 );
        //loader->unload();
        delete loader;
    }
}

QStringList HbDocumentLoaderPluginManager::pluginPathList() const
{
    return hbInstance->libraryPaths();
}

QString HbDocumentLoaderPluginManager::pluginFileNameFilter() const
{
#if defined(Q_OS_MAC)
    return QString("*.dylib");
#elif defined(Q_OS_WIN32)
    return QString("*plugin.dll");
#elif defined(Q_OS_SYMBIAN)
    return QString("*.qtplugin");
#else
    return QString("*.so");
#endif
}

void HbDocumentLoaderPluginManager::scanPlugins()
{
    const QStringList pathList = pluginPathList();
    const QString fileNameFilter = pluginFileNameFilter();

    foreach (const QString &path, pathList) {
        QDir pluginDir(path, fileNameFilter, QDir::Unsorted, QDir::Files | QDir::Readable);

        foreach (const QString &fileName, pluginDir.entryList()) {
            QPluginLoader *loader = new QPluginLoader(pluginDir.absoluteFilePath(fileName));
            QObject *pluginInstance = loader->instance();

            if (pluginInstance) {
                    HbDocumentLoaderPlugin *plugin =
                        qobject_cast<HbDocumentLoaderPlugin*>(pluginInstance);
                // plugin found. Call function handle it.
                if (plugin) {
                    mPlugins.append( loader );
                    continue;
                }
                loader->unload();
            }
            delete loader;
        }
    }
}

QObject *HbDocumentLoaderPluginManager::lookUp(const QString& type, const QString &name, const QString &plugin)
{
    QObject *result = 0;
    QPluginLoader *loader = lookUpPlugin( plugin );
    if ( loader ) {
        result = qobject_cast<HbDocumentLoaderPlugin*>(loader->instance())->createObject( type, name );
    } else {
        HB_DOCUMENTLOADER_PRINT( QString( "Cannot find plug-in: " ) + plugin );
    }
    return result;
}

QPluginLoader *HbDocumentLoaderPluginManager::lookUpPlugin( const QString &plugin )
{
    // check the exising plug-ins
    foreach( QPluginLoader *loader, mPlugins ) {
        const QFileInfo fileInfo( loader->fileName() );
        QString compareName;
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
        compareName.append( "lib" );
#endif
        compareName.append( plugin );
        if ( fileInfo.baseName() == compareName ) {
            return loader;
        }
    }

    // not found -> try to find it.
    const QStringList pathList = pluginPathList();
    QString fileNameFilter;
#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
    fileNameFilter.append( "lib" );
#endif
    fileNameFilter.append( plugin );
    fileNameFilter.append( "*" );

    foreach (const QString &path, pathList) {
        QDir pluginDir(path, fileNameFilter, QDir::Unsorted, QDir::Files | QDir::Readable);

        foreach (const QString &fileName, pluginDir.entryList()) {

            QPluginLoader *loader = new QPluginLoader(pluginDir.absoluteFilePath(fileName));
            QObject *pluginInstance = loader->instance();

            if (pluginInstance) {
                HbDocumentLoaderPlugin *plugin =
                    qobject_cast<HbDocumentLoaderPlugin*>(pluginInstance);
                if (plugin) {
                    mPlugins.append( loader );
                    return loader;
                } else {
                    loader->unload();
                }
            }
        }
    }
    return 0;
}

#endif // HB_BOOTSTRAPPED

// end of file

