/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

/*
	nsPluginsDirUNIX.cpp
	
	UNIX implementation of the nsPluginsDir/nsPluginsFile classes.
	
	by Alex Musil
 */

#include "xp_core.h"
#include "nsplugin.h"
#include "ns4xPlugin.h"
#include "ns4xPluginInstance.h"
#include "nsIServiceManager.h"
#include "nsIMemory.h"
#include "nsIPluginStreamListener.h"
#include "nsPluginsDir.h"
#include "nsSpecialSystemDirectory.h"
#include "prmem.h"

/* Local helper functions */
 
static PRUint32 CalculateVariantCount(const char* mimeTypes)
{
        PRUint32 variants = 0;
        const char* ptr = mimeTypes;
        while (*ptr)
        {
                if (*ptr == ';')
                        variants++;

                ++ptr; 
        }
        return variants;
}

///////////////////////////////////////////////////////////////////////////

/* nsPluginsDir implementation */

nsPluginsDir::nsPluginsDir(PRUint16 location)
{
    // this is somewhat lacking, in that it doesn't fall back to any 
    // other directories. then again, I'm not sure we should be falling 
    // back at all.  plugins have been (and probably should continue to be) 
    // loaded from both <libdir>/plugins and ~/.mozilla/plugins.  There
    // doesn't seem to be any way to do this in the current nsPluginsDir 
    // code, which is disheartening.

    // use MOZILLA_FIVE_HOME/plugins

  nsSpecialSystemDirectory sysdir(nsSpecialSystemDirectory::OS_CurrentProcessDirectory); 
  sysdir += "plugins"; 
  const char *pluginsDir = sysdir.GetCString(); // native path
  if (pluginsDir != NULL)
  {
      *(nsFileSpec*)this = pluginsDir;
  }
}

nsPluginsDir::~nsPluginsDir()
{
	// do nothing
}

PRBool nsPluginsDir::IsPluginFile(const nsFileSpec& fileSpec)
{
    const char* pathname = fileSpec.GetCString();


#ifdef NS_DEBUG
	printf("IsPluginFile(%s)\n", pathname);
#endif

	return PR_TRUE;
}

///////////////////////////////////////////////////////////////////////////

/* nsPluginFile implementation */

nsPluginFile::nsPluginFile(const nsFileSpec& spec)
	:	nsFileSpec(spec)
{
	// nada
}

nsPluginFile::~nsPluginFile()
{
	// nada
}

/**
 * Loads the plugin into memory using NSPR's shared-library loading
 * mechanism. Handles platform differences in loading shared libraries.
 */
nsresult nsPluginFile::LoadPlugin(PRLibrary* &outLibrary)
{
    PRLibSpec libSpec;
    PRLibSpec tempSpec;
    PRLibrary * handle;

    ///////////////////////////////////////////////////////////
    // Normally, Mozilla isn't linked against libXt and libXext
    // since it's a Gtk/Gdk application.  On the other hand,
    // legacy plug-ins expect the libXt and libXext symbols
    // to already exist in the global name space.  This plug-in
    // wrapper is linked against libXt and libXext, but since
    // we never call on any of these libraries, plug-ins still
    // fail to resolve Xt symbols when trying to do a dlopen
    // at runtime.  Explicitly opening Xt/Xext into the global
    // namespace before attempting to load the plug-in seems to
    // work fine.

    tempSpec.type = PR_LibSpec_Pathname;
    tempSpec.value.pathname = "libXt.so";
    handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);

    tempSpec.value.pathname = "libXext.so";
    handle = PR_LoadLibraryWithFlags(tempSpec, PR_LD_NOW|PR_LD_GLOBAL);

    libSpec.type = PR_LibSpec_Pathname;
    libSpec.value.pathname = this->GetCString();
    pLibrary = outLibrary = PR_LoadLibraryWithFlags(libSpec, 0);
    
#ifdef NS_DEBUG
    printf("LoadPlugin() %s returned %lx\n", 
           libSpec.value.pathname, (unsigned long)pLibrary);
#endif
    
    return NS_OK;
}

/**
 * Obtains all of the information currently available for this plugin.
 */
nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info)
{
    nsresult rv;
    const char* mimedescr;
    char *mdesc,*start,*nexttoc,*mtype,*exten,*descr;
    int i,num;

    // No, this doesn't leak. GetGlobalServiceManager() doesn't addref
    // it's out pointer. Maybe it should.
    nsIServiceManager* mgr;
    nsServiceManager::GetGlobalServiceManager(&mgr);

    nsFactoryProc nsGetFactory =
        (nsFactoryProc) PR_FindSymbol(pLibrary, "NSGetFactory");

    nsCOMPtr<nsIPlugin> plugin;

    if (nsGetFactory) {
        // It's an almost-new-style plugin. The "truly new" plugins
        // are just XPCOM components, but there are some Mozilla
        // Classic holdovers that live in the plugins directory but
        // implement nsIPlugin and the factory stuff.
        static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID);

        nsCOMPtr<nsIFactory> factory;
        rv = nsGetFactory(mgr, kPluginCID, nsnull, nsnull, getter_AddRefs(factory));
        if (NS_FAILED(rv)) return rv;

        plugin = do_QueryInterface(factory);
    }
    else {
        // It's old sk00l
        rv = ns4xPlugin::CreatePlugin(mgr, this->GetCString(), pLibrary, getter_AddRefs(plugin));
        if (NS_FAILED(rv)) return rv;
    }

    if (plugin) {
        plugin->GetValue(nsPluginVariable_NameString, &info.fName);
        plugin->GetValue(nsPluginVariable_DescriptionString, &info.fDescription);
        plugin->GetMIMEDescription(&mimedescr);
    }
    else {
        info.fName = PL_strdup(this->GetCString()); // XXXwaterson LEAK
        info.fDescription = "";
        info.fMimeDescription = "";
    }

#ifdef NS_DEBUG
    printf("GetMIMEDescription() returned \"%s\"\n", mimedescr);
#endif

    // copy string
    
    mdesc = (char *)PR_Malloc(strlen(mimedescr)+1);
    strcpy(mdesc,mimedescr);
    num=CalculateVariantCount(mimedescr)+1;
    info.fVariantCount = num;

    info.fMimeTypeArray =(char **)PR_Malloc(num * sizeof(char *));
    info.fMimeDescriptionArray =(char **)PR_Malloc(num * sizeof(char *));
    info.fExtensionArray =(char **)PR_Malloc(num * sizeof(char *));

    start=mdesc;
    for(i=0;i<num && *start;i++) {
        // search start of next token (separator is ';')

        if(i+1 < num) {
            if((nexttoc=PL_strchr(start, ';')) != 0)
                *nexttoc++=0;
            else
                nexttoc=start+strlen(start);
        } else
            nexttoc=start+strlen(start);

        // split string into: mime type ':' extensions ':' description

        mtype=start;
        exten=PL_strchr(start, ':');
        if(exten) {
            *exten++=0;
            descr=PL_strchr(exten, ':');
        } else
            descr=NULL;

        if(descr)
            *descr++=0;

#ifdef NS_DEBUG
        printf("Registering plugin for: \"%s\",\"%s\",\"%s\"\n", mtype,descr ? descr : "null",exten ? exten : "null");
#endif

        if(i==0) {
            info.fMimeType = mtype ? mtype : (char *)"";
            info.fMimeDescription = descr ? descr : (char *)"";
            info.fExtensions = exten ? exten : (char *)"";
        }

        if(!*mtype && !descr && !exten) {
            i--;
            info.fVariantCount--;
        } else {
            info.fMimeTypeArray[i] = mtype ? mtype : (char *)"";
            info.fMimeDescriptionArray[i] = descr ? descr : (char *)"";
            info.fExtensionArray[i] = exten ? exten : (char *)"";
        }
        start=nexttoc;
    }

	return NS_OK;
}


nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
{
  return NS_OK;
}

