/* (PD) 2001 The Bitzi Corporation
 * Please see file COPYING or http://bitzi.com/publicdomain 
 * for more info.
 *
 * $Id: plugin_man.c,v 1.1 2001/04/12 21:01:16 robert Exp $
 */
/*------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <dlfcn.h>

#include "bitcollider.h"
#include "plugin_man.h"

/*------------------------------------------------------------------------- */

#define MAX_PATH    1024
#define DB printf("%s:%d\n", __FILE__, __LINE__);

/*------------------------------------------------------------------------- */
#define ERROR_FILENOTFOUND   "File not found."

/*------------------------------------------------------------------------- */

Bitcollider *init_plugins(void)
{
   Bitcollider *bc;

   bc = malloc(sizeof(Bitcollider));
   memset(bc, 0, sizeof(Bitcollider));

   return bc;
}

void shutdown_plugins(Bitcollider *bc)
{
   free(bc);
}

int load_plugins(Bitcollider *bc, const char *path, b_bool printDebugInfo)
{
   DIR           *dir;
   struct dirent *entry;
   int            count = 0, j;
   char          *ptr, file[MAX_PATH];
   PluginMethods *(*init_function)(void);

   dir = opendir(path);
   if (dir == NULL)
      return 0;

   for(;;)
   {
       /* Scan the given dir for plugins */
       entry = readdir(dir);
       if (!entry)
          break;

       ptr = strrchr(entry->d_name, '.');
       if (!ptr || strcasecmp(ptr, ".bcp"))
          continue;

       if (printDebugInfo)
           printf("  %s: ", entry->d_name);
       sprintf(file, "%s/%s", path, entry->d_name);

       /* Found one, lets open it */
       bc->plugins[bc->numPluginsLoaded].handle = dlopen(file, RTLD_NOW);
       if (bc->plugins[bc->numPluginsLoaded].handle == NULL)
       {
           if (printDebugInfo)
               printf("Cannot load plugin %s. (%s)\n", file, dlerror());
           continue;
       }
       bc->plugins[bc->numPluginsLoaded].file = strdup(entry->d_name);

       /* Opened plugin ok, now locate our entry function */
       init_function = dlsym(bc->plugins[bc->numPluginsLoaded].handle, 
                             "init_plugin");
       if (init_function == NULL)
       {
           dlclose(bc->plugins[bc->numPluginsLoaded].handle);
           if (printDebugInfo)
               printf("Cannot find entry point in %s.\n", file);
           continue;
       }

       /* Init the plugin and get the methods provided by the plugin */
       bc->plugins[bc->numPluginsLoaded].methods = (*init_function)();
       if (bc->plugins[bc->numPluginsLoaded].methods == NULL)
       {
           dlclose(bc->plugins[bc->numPluginsLoaded].handle);
           if (printDebugInfo)
               printf("Cannot retrieve supported methods from %s.\n", file);
           continue;
       }

       /* Now get the formats handled by the plugin */
       bc->plugins[bc->numPluginsLoaded].formats = 
           bc->plugins[bc->numPluginsLoaded].methods->get_supported_formats();

       if (printDebugInfo)
       {
           printf("%s ", bc->plugins[bc->numPluginsLoaded].methods->get_name());
           printf("(%s)\n", bc->plugins[bc->numPluginsLoaded].methods->
                               get_version());

       }

       /* Check to make sure that the given plugin hasnt already been loaded */
       for(j = 0; j < bc->numPluginsLoaded; j++)
       {
           if (!strcmp(bc->plugins[j].file, 
                       bc->plugins[bc->numPluginsLoaded].file))
           {
               if (printDebugInfo)
                  printf("  [Plugin %s has already been loaded. Skipping.]\n",
                    bc->plugins[bc->numPluginsLoaded].file);
                
               bc->plugins[bc->numPluginsLoaded].methods->shutdown_plugin();
               dlclose(bc->plugins[bc->numPluginsLoaded].handle);
               bc->plugins[bc->numPluginsLoaded].handle = NULL;
               bc->plugins[bc->numPluginsLoaded].methods = NULL;
               free(bc->plugins[bc->numPluginsLoaded].file);
               bc->plugins[bc->numPluginsLoaded].file = NULL;

               break;
           }
       }

       /* If we didn't already this plugin loaded, increment our counters */
       if (j == bc->numPluginsLoaded)
       {
          bc->numPluginsLoaded++;
          count++;
       }
   }
   closedir(dir);

   return count;
}

void unload_plugins(Bitcollider *bc)
{
   for(;;)
   {
       bc->numPluginsLoaded--;
       if (bc->numPluginsLoaded < 0)
          break;

       if (bc->plugins[bc->numPluginsLoaded].handle)
       {
           bc->plugins[bc->numPluginsLoaded].methods->shutdown_plugin();
           dlclose(bc->plugins[bc->numPluginsLoaded].handle);
           bc->plugins[bc->numPluginsLoaded].handle = NULL;
           bc->plugins[bc->numPluginsLoaded].methods = NULL;
           free(bc->plugins[bc->numPluginsLoaded].file);
           bc->plugins[bc->numPluginsLoaded].file = NULL;
       }
   }
}

int get_num_plugins(Bitcollider *bc)
{
   return bc->numPluginsLoaded;
}

PluginMethods *get_plugin(Bitcollider *bc, const char *extension)
{
   int              i;
   SupportedFormat *format;

   for(i = 0; i < bc->numPluginsLoaded; i++)
   {
      for(format = bc->plugins[i].formats; format && 
          format->fileExtension; format++)
      {
         if (strcasecmp(format->fileExtension, extension) == 0)
             return bc->plugins[i].methods;
      }
   }

   return NULL;
}
