// Windows/DLL.cpp

#include "StdAfx.h"

#ifdef __APPLE_CC__
#include <mach-o/dyld.h>
#elif ENV_BEOS
#include <kernel/image.h>
#include <Path.h>
#else
#include <dlfcn.h>  // dlopen ...
#endif

#include "DLL.h"
#include "Defs.h"
#ifndef _UNICODE
#include "../Common/StringConvert.h"
#endif

#define NEED_NAME_WINDOWS_TO_UNIX
#include "myPrivate.h"

// #define TRACEN(u) u;
#define TRACEN(u)  /* */

namespace NWindows {
namespace NDLL {

CLibrary::~CLibrary()
{
  Free();
}

bool CLibrary::Free()
{
TRACEN((printf("CLibrary::Free(%p)\n",(void *)_module)))
  if (_module == 0)
    return true;

#ifdef __APPLE_CC__
  int ret = NSUnLinkModule ((NSModule)_module, 0);
#elif ENV_BEOS
  int ret = unload_add_on((image_id)_module);
#else
  int ret = dlclose(_module);
#endif
TRACEN((printf("CLibrary::Free dlclose(%p)=%d\n",(void *)_module,ret)))
  if (ret != 0) return false;
  _module = 0;
  return true;
}

static FARPROC local_GetProcAddress(HMODULE module,LPCSTR lpProcName)
{
  void *ptr = 0;
  TRACEN((printf("local_GetProcAddress(%p,%s)\n",(void *)module,lpProcName)))
  if (module) {
#ifdef __APPLE_CC__
    char name[MAX_PATHNAME_LEN];
    sprintf(name,"_%s",lpProcName);
    TRACEN((printf("NSLookupSymbolInModule(%p,%s)\n",(void *)module,name)))
    NSSymbol sym;
    sym = NSLookupSymbolInModule((NSModule)module, name);
    if (sym) {
      ptr = NSAddressOfSymbol(sym);
    } else {
      ptr = 0;
    }
#elif ENV_BEOS
    if (get_image_symbol((image_id)module, lpProcName, B_SYMBOL_TYPE_TEXT, &ptr) != B_OK)
      ptr = 0;
#else
    ptr = dlsym (module, lpProcName);
#endif
	TRACEN((printf("CLibrary::GetProcAddress : dlsym(%p,%s)=%p\n",(void *)module,lpProcName,ptr)))
  }
  return (FARPROC)ptr;
}

FARPROC CLibrary::GetProcAddress(LPCSTR lpProcName) const
{
  TRACEN((printf("CLibrary::GetProcAddress(%p,%s)\n",(void *)_module,lpProcName)))
  return local_GetProcAddress(_module,lpProcName);
}

bool CLibrary::LoadOperations(HMODULE newModule)
{
  if (newModule == NULL)
    return false;
  if(!Free())
    return false;
  _module = newModule;
  return true;
}

// bool CLibrary::LoadEx(LPCTSTR fileName, DWORD flags)
// {
//   /* return LoadOperations(::LoadLibraryEx(fileName, NULL, flags)); */
//   return this->Load(fileName);
// }

typedef BOOL (*t_DllMain)(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved);

bool CLibrary::Load(LPCTSTR lpLibFileName)
{
  // HMODULE handler = ::LoadLibrary(fileName);
  void *handler = 0;
  char  name[MAX_PATHNAME_LEN+1];
  strcpy(name,nameWindowToUnix(lpLibFileName));
  
  // replace ".dll" with ".so"
  size_t len = strlen(name);
  if ((len >=4) && (strcmp(name+len-4,".dll") == 0)) {
    strcpy(name+len-4,".so");
  }

#ifdef __APPLE_CC__
  NSObjectFileImage image;
  NSObjectFileImageReturnCode nsret;

  nsret = NSCreateObjectFileImageFromFile (name, &image);
  if (nsret == NSObjectFileImageSuccess) {
     TRACEN((printf("NSCreateObjectFileImageFromFile(%s) : OK\n",name)))
     handler = (HMODULE)NSLinkModule(image,lpLibFileName,NSLINKMODULE_OPTION_RETURN_ON_ERROR
           | NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW);
  } else {
     TRACEN((printf("NSCreateObjectFileImageFromFile(%s) : ERROR\n",name)))
  }
#elif ENV_BEOS
  // normalize path (remove things like "./", "..", etc..), otherwise it won't work
  BPath p(name, NULL, true);
  status_t err = B_OK;
  handler = (HMODULE)load_add_on(p.Path());
  if (handler < 0) {
    err = (image_id)handler;
    handler = 0;
  }
#else
  int options_dlopen = 0;
#ifdef RTLD_LOCAL
  options_dlopen |= RTLD_LOCAL;
#endif
#ifdef RTLD_NOW
  options_dlopen |= RTLD_NOW;
#endif
#ifdef RTLD_GROUP
  options_dlopen |= RTLD_GROUP; // for solaris
#endif
  handler = dlopen(name,options_dlopen);
#endif // __APPLE_CC__
  TRACEN((printf("LoadLibraryA(%s)=%p\n",name,handler)))
  if (handler) {

    // Call DllMain() like in Windows
    t_DllMain p_DllMain = (t_DllMain)local_GetProcAddress (handler, "DllMain");
    if (p_DllMain)
    {
      p_DllMain(0,DLL_PROCESS_ATTACH,0);
    }
    // Propagate the value of global_use_utf16_conversion into the plugins
    int *tmp = (int *)local_GetProcAddress(handler,"global_use_utf16_conversion");
    if (tmp) *tmp = global_use_utf16_conversion;

    // test construtors calls
    void (*fctTest)(void) = (void (*)(void))local_GetProcAddress(handler,"sync_TestConstructor");
    if (fctTest) fctTest();

  } else {
#ifdef __APPLE_CC__
    NSLinkEditErrors c;
    int num_err;
    const char *file,*err;
    NSLinkEditError(&c,&num_err,&file,&err);
    printf("Can't load '%s' (%s)\n", lpLibFileName,err);
#elif ENV_BEOS
    printf("Can't load '%s' (%s)\n", lpLibFileName,strerror(err));
#else
    printf("Can't load '%s' (%s)\n", lpLibFileName,dlerror());
#endif
  } 

  return LoadOperations(handler);
}

#ifndef _UNICODE
// bool CLibrary::LoadEx(LPCWSTR fileName, DWORD flags)
// {
//   return LoadEx(UnicodeStringToMultiByte(fileName, CP_ACP), flags);
// }


bool CLibrary::Load(LPCWSTR fileName)
{
  return Load(UnicodeStringToMultiByte(fileName, CP_ACP));
}
#endif

}}

