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

/* nsDll
 *
 * Abstraction of a Dll. Stores modifiedTime and size for easy detection of
 * change in dll.
 *
 * dp Suresh <dp@netscape.com>
 */

#include "xcDll.h"
#include "plstr.h"	// strdup and strfree
#include "nsIComponentManager.h"

// MAC ONLY
nsDll::nsDll(const char *codeDllName, int type)
  : m_dllName(NULL), m_dllSpec(NULL), m_modDate(0), m_size(0),
    m_instance(NULL), m_status(DLL_OK)
    
{
    if (!codeDllName || !*codeDllName)
    {
        m_status = DLL_INVALID_PARAM;
        return;
    }
    m_dllName = PL_strdup(codeDllName);
    if (!m_dllName)
    {
        m_status = DLL_NO_MEM;
        return;
    }
}

nsDll::nsDll(nsIFileSpec *dllSpec)
  : m_dllName(NULL), m_dllSpec(dllSpec), m_modDate(0), m_size(0),
    m_instance(NULL), m_status(DLL_OK)
{
    Init(dllSpec);
}

nsDll::nsDll(const char *libPersistentDescriptor)
  : m_dllName(NULL), m_dllSpec(NULL), m_modDate(0), m_size(0),
    m_instance(NULL), m_status(DLL_OK)
{
    Init(libPersistentDescriptor);
}

nsDll::nsDll(const char *libPersistentDescriptor, PRUint32 modDate, PRUint32 fileSize)
  : m_dllName(NULL), m_dllSpec(NULL), m_modDate(0), m_size(0),
    m_instance(NULL), m_status(DLL_OK)
{
    Init(libPersistentDescriptor);

    // and overwrite the modData and fileSize
	m_modDate = modDate;
	m_size = fileSize;
}
 
void
nsDll::Init(nsIFileSpec *dllSpec)
{
    // Addref the m_dllSpec
    m_dllSpec = dllSpec;
    NS_ADDREF(m_dllSpec);

    // Make sure we are dealing with a file
    PRBool isFile = PR_FALSE;
    nsresult rv = m_dllSpec->isFile(&isFile);
    if (NS_FAILED(rv))
    {
        m_status = DLL_INVALID_PARAM;
        return;
    }
    if (isFile == PR_FALSE)
    {
      // Not a file. Cant work with it.
      m_status = DLL_NOT_FILE;
      return;
    }
    
    // Populate m_modDate and m_size
    if (NS_FAILED(m_dllSpec->GetModDate(&m_modDate)) ||
        NS_FAILED(m_dllSpec->GetFileSize(&m_size)))
    {
        m_status = DLL_INVALID_PARAM;
        return;
    }

	m_status = DLL_OK;			
}

void
nsDll::Init(const char *libPersistentDescriptor)
{
    nsresult rv;
    m_modDate = 0;
	m_size = 0;
	
	if (libPersistentDescriptor == NULL)
	{
		m_status = DLL_INVALID_PARAM;
		return;
	}

    // Create a FileSpec from the persistentDescriptor
    nsIFileSpec *dllSpec = NULL;
    NS_DEFINE_IID(kFileSpecIID, NS_IFILESPEC_IID);
    rv = nsComponentManager::CreateInstance(NS_FILESPEC_PROGID, NULL, kFileSpecIID, (void **) &dllSpec);
    if (NS_FAILED(rv))
    {
        m_status = DLL_INVALID_PARAM;
        return;
    }

    rv = dllSpec->SetPersistentDescriptorString((char *)libPersistentDescriptor);
    if (NS_FAILED(rv))
    {
        m_status = DLL_INVALID_PARAM;
        return;
    }

    Init(dllSpec);
}


nsDll::~nsDll(void)
{
	if (m_instance != NULL)
		Unload();
    if (m_dllSpec)
        NS_RELEASE(m_dllSpec);
    if (m_dllName)
        PL_strfree(m_dllName);
}

const char *
nsDll::GetNativePath()
{
    if (m_dllName)
        return m_dllName;
    char *nativePath = NULL;
    m_dllSpec->GetNativePath(&nativePath);
    return nativePath;
}

const char *
nsDll::GetPersistentDescriptorString()
{
    if (m_dllName)
        return m_dllName;
    char *persistentDescriptor = NULL;
    m_dllSpec->GetPersistentDescriptorString(&persistentDescriptor);
    return persistentDescriptor;
}

PRBool
nsDll::HasChanged()
{
    if (m_dllName)
        return PR_FALSE;

    // If mod date has changed, then dll has changed
    PRBool modDateChanged = PR_FALSE;
    nsresult rv = m_dllSpec->modDateChanged(m_modDate, &modDateChanged);
    if (NS_FAILED(rv) || modDateChanged == PR_TRUE)
      return PR_TRUE;

    // If size has changed, then dll has changed
    PRUint32 aSize = 0;
    rv = m_dllSpec->GetFileSize(&aSize);
    if (NS_FAILED(rv) || aSize != m_size)
      return PR_TRUE;

    return PR_FALSE;
}




PRBool nsDll::Load(void)
{
	if (m_status != DLL_OK)
	{
		return (PR_FALSE);
	}
	if (m_instance != NULL)
	{
		// Already loaded
		return (PR_TRUE);
	}

    if (m_dllName)
    {
        m_instance = PR_LoadLibrary(m_dllName);
    }
    else
    {
        char *nsprPath = NULL;
        nsresult rv = m_dllSpec->GetNSPRPath(&nsprPath);
        if (NS_FAILED(rv)) return PR_FALSE;

#ifdef	XP_MAC
        // NSPR path is / separated. This works for all NSPR functions
        // except Load Library. Translate to something NSPR can accepts.
        if (nsprPath[0] == '/')
        {
            // convert '/' to ':'
            int c;
            char* str = nsprPath;
            while ((c = *str++) != 0)
            {
                if (c == '/')
                  str[-1] = ':';
            }
            m_instance = PR_LoadLibrary(&nsprPath[1]);		// skip over initial slash
        }
        else
        {
            m_instance = PR_LoadLibrary(nsprPath);
        }
#else
        m_instance = PR_LoadLibrary(nsprPath);
#endif /* XP_MAC */
        nsCRT::free(nsprPath);
    }
    return ((m_instance == NULL) ? PR_FALSE : PR_TRUE);
}

PRBool nsDll::Unload(void)
{
	if (m_status != DLL_OK || m_instance == NULL)
		return (PR_FALSE);
	PRStatus ret = PR_UnloadLibrary(m_instance);
	if (ret == PR_SUCCESS)
	{
		m_instance = NULL;
		return (PR_TRUE);
	}
	else
		return (PR_FALSE);
}

void * nsDll::FindSymbol(const char *symbol)
{
	if (symbol == NULL)
		return (NULL);
	
	// If not already loaded, load it now.
	if (Load() != PR_TRUE)
		return (NULL);

	return (PR_FindSymbol(m_instance, symbol));
}
