//=-- Plugin.cpp - Abstract base class for plugins to derive from ------------=
//
// Plugin Class:
//   The Plugin class is the common base class of all of the pluggable 
//   components in MagicStats.  All plugins have a name, a type, and a number
//   of implementation defined behaviors.  Plugins may be either statically 
//   linked into MagicStats, or dynamically loaded at runtime.
//
// PluginDesc:
//   The Descriptor Class for a plugin...  This class holds all of the 
//   information neccesary to create a named plugin and tell what type it is.  
//   "Hey plugin, what's your type?"
//
// PluginInit Class:
//   This class is intended to be used once for each plugin to register it into
//   available plugins table.  With the aid of the INIT_PLUGINS macro, all a 
//   plugin has to do is write INIT_PLUGINS(PluginTable); once in the source 
//   code.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-1999 Chris Lattner
//=---------------------------------------------------------------------------=

#include <iomanip.h>
#include "Plugin.h"
#include "Table.h"
#include "PluginManager.h"
#include "Options.h"

// The current resource location that we are loading plugins from...  this
// information gets copied into each plugin as it is loaded...
//
String      Plugin::PluginSourcePath;
String      Plugin::PluginSourceURL;

// When creating a new plugin, this pointer is set to the plugin descriptor 
// that corresponds to it.  Practically, this is always set to the descriptor
// of the latest plugin created...
//
PluginDesc *Plugin::CurCreatingDesc = 0;

Plugin::~Plugin() {
  PluginManager::RemovePlugin(this);
}


Table<CSString, PluginDesc> &Plugin::GetPlugins() {
  static Table<CSString, PluginDesc> Plugins;
  return Plugins;
}


const char *Plugin::PluginTypeNames[] = { 
    "Page Plugin", "Access Format Plugin", 
    "Access Filter Plugin", "Browser Type Plugin", "File Stream Plugin",
    "Engine Plugin"
};

// Print the list to stdout
void Plugin::PrintPlugins() {
  int PluginPrinted;

  for (int i = 0; i < Plugin::NUMPLUGINTYPES; i++) {
    cout << PluginTypeNames[i] << "s...\n";

    PluginPrinted = 0;
    Table<CSString, PluginDesc>::Iterator I = GetPlugins().GetStart();
    for (; I; I++) {
      PluginDesc &PD = I->GetSecondary();

      if (PD.Type == i) {
        cout << "  " << PD << endl;
        PluginPrinted = 1;
      }
    }

    if (!PluginPrinted)
      cout << "  None found...\n";

    cout << endl;
  }
}

Plugin *Plugin::CreatePlugin(PluginType Type, const String &Name) {
  
  // Make sure a plugin that matches that type and name is in the table...
  if (!GetPlugins().InTable(Name))
    return 0;

  PluginDesc &PD = GetPlugins()[Name];

  if (PD.Type != Type) return 0;  // Not the right type...

  CurCreatingDesc = &PD;

  // Found it... Call the CreateInstance function to get a new object
  Plugin *P = PD.CreateInstance();

  if (P) PluginManager::AddPlugin(P);
  return P;
}

Plugin *Plugin::CreatePlugin(PluginType Type,void *Data) {
  int LastPriority = -1;
  int NextPriority = 0;
  int CurPluginPriority;
  
  Plugin *Return;

  // While we haven't hit the highest priority...
  while (NextPriority != LastPriority) {
    LastPriority = NextPriority;

    // Scan the whole list of registered plugins...
    Table<CSString,PluginDesc>::Iterator I = GetPlugins().GetStart();
    while (I) {
      PluginDesc &CurPlugin = I->GetSecondary();

      // Only include plugins of the type we're looking for...
      if (CurPlugin.Type == Type) {
        CurPluginPriority = CurPlugin.Priority;
        if (CurPluginPriority > LastPriority) {
          if ((CurPluginPriority < NextPriority) || NextPriority == LastPriority)
            NextPriority = CurPluginPriority;
        }
        
        if (CurPluginPriority == LastPriority) {
          // Attempt to create this plugin, it has the current priority.
          CurCreatingDesc = &CurPlugin;  // Set the context to be loaded from
          Return = CurPlugin.CreateInstanceData(Data);
          if (Return) {
            PluginManager::AddPlugin(Return);
            return Return;
          }
        }
      }
      
      I++;
    }
  }

  return 0;
}

void Plugin::AddPluginDescriptor(const PluginDesc &D) {
  // If this is the first time through, create the table...
  GetPlugins().SetElementTo(D.Name, D);     // Associate the Create method with the name...
}

void Plugin::SetPluginSource(const String &SourcePath, const String &SourceURL) {
  PluginSourcePath = SourcePath;
  PluginSourceURL  = SourceURL;
}

void Plugin::ClearPluginSource() {
  PluginSourcePath = MSOptions::GetBuiltInPluginResPath();
  PluginSourceURL  = MSOptions::GetBuiltInPluginResURL ();
}

//=---------------------------------------------------------------------------=
//                   ...Code for the PluginDesc class...
//=---------------------------------------------------------------------------=

PluginDesc::PluginDesc() : PluginSourcePath(Plugin::PluginSourcePath), 
                           PluginSourceURL (Plugin::PluginSourceURL ) {
  CreateInstance = 0; 
  Name = 0;
  Type = Plugin::NONE;
  Version = 0;
  CreateInstanceData = 0; 
  Priority = -1;
}

PluginDesc::PluginDesc(Plugin *(*CI)(void) , const char *N, 
                       Plugin::PluginType T, int version,
                       Plugin *(*CID)(void *), int priority)
                       : PluginSourcePath(Plugin::PluginSourcePath), 
                         PluginSourceURL (Plugin::PluginSourceURL ) {
  CreateInstance = CI;
  CreateInstanceData = CID;
  Priority = priority;
  Name = N;
  Type = T;
  Version = version;
}

int PluginDesc::Compare(const Object &) const {
  return 0;
}

ostream &PluginDesc::print(ostream &O) const {
  String Path, URL;

  if (PluginSourcePath.Valid()) {
    Path = PluginSourcePath;
    URL  = PluginSourceURL;
  } else {
    Path = MSOptions::GetBuiltInPluginResPath();
    URL  = MSOptions::GetBuiltInPluginResURL ();
  }

  return O << setw(25) << setiosflags(ios::left) << Name << "v" << setw(6) 
           << setprecision(2) << setiosflags(ios::fixed) << Version/100.0
           << Path << '\t' << URL;
}

void PluginDesc::RegisterPlugin() const {
  Plugin::AddPluginDescriptor(*this);
}
