//===------- NativeUtil.cpp - Methods to call native functions --------------===//
//
//                              N3
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <dlfcn.h>
#include <string.h>

#include "llvm/DerivedTypes.h"

#include "NativeUtil.h"
#include "N3.h"
#include "VMArray.h"
#include "VMClass.h"
#include "VMThread.h"

using namespace n3;

static void cliToInternal(char* buf) {
  uint32 i = 0;
  while (buf[i] != 0) {
    if (buf[i] == '.') buf[i] = '_';
    ++i;
  }
}

static void* makeFull(VMCommonClass* cl, VMMethod* meth) {
  char* buf = (char*)alloca(4096);
  sprintf(buf, 
					"%s_%s_%s", 
					mvm::PrintBuffer(cl->nameSpace).cString(), 
					mvm::PrintBuffer(cl->name).cString(),
					mvm::PrintBuffer(meth->name).cString());

  std::vector<VMCommonClass*>::iterator i = meth->parameters.begin(),
                                        e = meth->parameters.end();
  
  // Remove return type;
  ++i;
  for ( ; i!= e; ++i) {
    VMCommonClass* cl = *i;
    sprintf(buf, "%s_%s_%s", buf, mvm::PrintBuffer(cl->nameSpace).cString(), mvm::PrintBuffer(cl->name).cString());
  }

  cliToInternal(buf);
  void* res = dlsym(0, buf);
  
  if (!res) {
    VMThread::get()->getVM()->error("unable to find native method %s",
                               mvm::PrintBuffer(meth).cString());
  }

  return res;
}

void* NativeUtil::nativeLookup(VMCommonClass* cl, VMMethod* meth) {
	mvm::PrintBuffer _name(cl->name);
	mvm::PrintBuffer _nameSpace(cl->nameSpace);
	mvm::PrintBuffer _methName(meth->name);
  char* name = _name.cString();
  char* nameSpace = _nameSpace.cString();
  char* methName = _methName.cString();

  char* buf = (char*)alloca(6 + strlen(name) + strlen(nameSpace) +
                            strlen(methName));
  sprintf(buf, "%s_%s_%s", nameSpace, name, methName);
  cliToInternal(buf);
  void* res = dlsym(0, buf);
  if (!res) {
    buf = (char*)alloca(6 + strlen(name) + strlen(nameSpace) +
                        strlen(methName) + 10);
    sprintf(buf, "%s_%s_%s_%d", nameSpace, name, methName, 
                meth->getSignature(NULL)->getNumParams());
    cliToInternal(buf);
    res = dlsym(0, buf);
    if (!res) {
      return makeFull(cl, meth);
    }
  }
  return res;
}
