/***************************************************************************
                          pluginloader.cpp  -  description
                             -------------------
    begin                : Sun Dec 30 2001
    copyright            : (C) 2001 by Dominik Seichter
    email                : domseichter@web.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/*
 * Parts of this code are copied from
 * Franz Schmid's <Franz.Schmid@altmuehlnet.de>
 * Scribus.
 */

// Own includes
#include "pluginloader.h"
#include "fileplugin.h"
#include "datetime.h"
#include "mydirplugin.h"
#include "permission.h"
#include "pictureplugin.h"
#include "commandplugin.h"
#include "dateplugin.h"
#include "encodingplugin.h"
#include "translitplugin.h"

// TQt includes
#include <tqregexp.h>
#include <tqstringlist.h>

// TDE includes
#include <tdeapplication.h>
#include <kservice.h>

/* use a prime number here */
#define DICT_SIZE 43
#define BRACKET_CACHE_COUNT 19

PluginLoader::PluginLoader()
{
    libs.setAutoDelete( true );
    m_loaded = false;
    m_file = false;
}

PluginLoader::~PluginLoader()
{ }

PluginLoader* PluginLoader::instance()
{
    if( !m_plugin )
        m_plugin = new PluginLoader();

    return m_plugin;
}

PluginLoader* PluginLoader::m_plugin = NULL;

void PluginLoader::loadPlugins( bool fileplugins )
{
    int i;

    // make sure all plugins with display
    // get deleted and the GUI is rebuild
    if( m_loaded )
    {
        for( i=0;i<NUM_INTERNAL_PLUGINS;i++ )
        {
            for( unsigned int z=0;z<libs.count();z++ )
                if( libs.at(z)->plugin == m_internal_plugins[i] )
                {
                    libs.remove( z );
                    break;
                }
        }
    }

    m_loaded = true;
    m_internal_plugins[0] = new CommandPlugin();
    m_internal_plugins[1] = new MyPermPlugin();
    m_internal_plugins[2] = new MyDatePlugin();
    m_internal_plugins[3] = new MyDirPlugin();
    m_internal_plugins[4] = new PicturePlugin();
    m_internal_plugins[5] = new EncodingPlugin();        
    m_internal_plugins[6] = new DatePlugin();
    m_internal_plugins[7] = new TranslitPlugin();

    for( i=0;i<NUM_INTERNAL_PLUGINS;i++ )
        addPlugin( m_internal_plugins[i] );

    if( fileplugins && !m_file )
        loadFilePlugins();
    
    m_bracket_map.clear();
    TQPtrListIterator<PluginLoader::PluginLibrary> it( libs );
    for( ; it.current(); ++it ) {
        if( (*it)->plugin->type() == TYPE_BRACKET ) {
            const TQStringList list = (*it)->plugin->getKeys();
            for( unsigned int i = 0; i < list.count(); i++ )
                m_bracket_map.insert( list[i].lower(), (*it)->plugin );
        }
    }

}

void PluginLoader::clearCache()
{
    TQPtrListIterator<PluginLoader::PluginLibrary> it( libs );
    for( ; it.current(); ++it )
        (*it)->plugin->clearCache();
}

Plugin* PluginLoader::findPlugin( const TQString & token )
{
    // Optimize a little bit for speed and convert only once to lower()
    TQString lower = token.lower();
    
    if( m_bracket_cache.contains( lower ) )
        return m_bracket_cache[lower];
        
    /* The new version is slower than the old one :-(
     */
    TQMap<TQString,Plugin*>::Iterator it;
    for ( it = m_bracket_map.begin(); it != m_bracket_map.end(); ++it ) 
    {
        if( TQRegExp( it.key() ).exactMatch( lower ) )
        {
            m_bracket_cache.insert( lower, it.data(), true );
            if( m_bracket_cache.count() > BRACKET_CACHE_COUNT )
                m_bracket_cache.remove( m_bracket_cache.begin() );
                
            return it.data();
        }
    }

    // add typos to the cache, too:
    // So that we find immediately that this key is not supported.
    m_bracket_cache.insert( lower, NULL, true );
    if( m_bracket_cache.count() > BRACKET_CACHE_COUNT )
        m_bracket_cache.remove( m_bracket_cache.begin() );
        
    return NULL;
}

void PluginLoader::addPlugin( Plugin* plugin )
{
    PluginLibrary* listitem=new PluginLibrary;
    listitem->plugin = plugin;
    listitem->check = NULL;
    listitem->usePlugin = plugin->alwaysUsed();
    libs.append( listitem );
}

void PluginLoader::loadFilePlugins()
{
    KService::List list = KService::allServices();
    for( unsigned int i = 0; i < list.count(); i++ ) {
        KService* s = (KService*)list[i];
        if( !s->terminal() && s->type() == "Service" && s->hasServiceType( "KFilePlugin" ) ) {
            FilePlugin* tdefileplugin = new FilePlugin( s );
            if( tdefileplugin->isValid() ) {
                addPlugin( tdefileplugin );
                kapp->processEvents();
            } else
                delete tdefileplugin;
        }
    }
    
    m_file = true;
}

#include "pluginloader.moc"
