//===-------------- Mono.cpp - The Mono interface -------------------------===//
//
//                              N3
//
// This file is licensed under the University of Illinois Open Source License
// and the Mono License. See LICENSE.TXT for details.
// The file also has the following additional copyright:
//
// (C) 2008 Ximian, Inc.  http://www.ximian.com
//
//===----------------------------------------------------------------------===//

#include "mvm/JIT.h"

#include "Assembly.h"
#include "MonoString.h"
#include "MSCorlib.h"
#include "N3.h"
#include "Reader.h"
#include "VMArray.h"
#include "VMClass.h"
#include "VMObject.h"
#include "VMThread.h"

#include <glib.h>

#include "number-formatter.h"

#include "MonoPath.inc"

using namespace n3;

extern "C" int System_Environment_get_Platform (void) {
#if defined (PLATFORM_WIN32)
	/* Win32NT */
	return 2;
#else
	/* Unix */
	return 128;
#endif
}

static const char *encodings [] = {
	(char *) 1,
		"ascii", "us_ascii", "us", "ansi_x3.4_1968",
		"ansi_x3.4_1986", "cp367", "csascii", "ibm367",
		"iso_ir_6", "iso646_us", "iso_646.irv:1991",
	(char *) 2,
		"utf_7", "csunicode11utf7", "unicode_1_1_utf_7",
		"unicode_2_0_utf_7", "x_unicode_1_1_utf_7",
		"x_unicode_2_0_utf_7",
	(char *) 3,
		"utf_8", "unicode_1_1_utf_8", "unicode_2_0_utf_8",
		"x_unicode_1_1_utf_8", "x_unicode_2_0_utf_8",
	(char *) 4,
		"utf_16", "UTF_16LE", "ucs_2", "unicode",
		"iso_10646_ucs2",
	(char *) 5,
		"unicodefffe", "utf_16be",
	(char *) 6,
		"iso_8859_1",
	(char *) 0
};

/*
 * Returns the internal codepage, if the value of "int_code_page" is
 * 1 at entry, and we can not compute a suitable code page number,
 * returns the code page as a string
 */
extern "C" MonoString*
System_Text_Encoding_InternalCodePage (gint32 *int_code_page) 
{
	const char *cset;
	const char *p;
	char *c;
	char *codepage = NULL;
	int code;
	int want_name = *int_code_page;
	int i;
	
	*int_code_page = -1;

	g_get_charset (&cset);
	c = codepage = strdup (cset);
	for (c = codepage; *c; c++){
		if (isascii (*c) && isalpha (*c))
			*c = tolower (*c);
		if (*c == '-')
			*c = '_';
	}
	/* g_print ("charset: %s\n", cset); */
	
	/* handle some common aliases */
	p = encodings [0];
	code = 0;
	for (i = 0; p != 0; ){
		if ((gssize) p < 7){
			code = (gssize) p;
			p = encodings [++i];
			continue;
		}
		if (strcmp (p, codepage) == 0){
			*int_code_page = code;
			break;
		}
		p = encodings [++i];
	}
	
	if (strstr (codepage, "utf_8") != NULL)
		*int_code_page |= 0x10000000;
	free (codepage);
	
	if (want_name && *int_code_page == -1) {
		N3 *vm = (N3*)VMThread::get()->getVM();
		declare_gcroot(ArrayChar*, array_res) = vm->asciizToArray(cset);
		declare_gcroot(MonoString*, res) = (MonoString*)(vm->arrayToString(array_res));
		return res;
	} else
		return NULL;
}

extern "C" void System_Threading_Monitor_Monitor_exit(VMObject* obj) {
  // TODO: There's a bug in the bootstrap, see why
	llvm_gcroot(obj, 0);
  if (LockObj::owner(obj->lockObj)) VMObject::unlock(obj);
}

extern "C" bool System_Threading_Monitor_Monitor_try_enter(VMObject* obj, int ms) {
	llvm_gcroot(obj, 0);
	VMObject::aquire(obj);
  return true;
}


extern "C" void* System_IO_MonoIO_get_ConsoleError() {
  return (void*)stderr;
}

extern "C" void* System_IO_MonoIO_get_ConsoleOutput() {
  return (void*)stdout;
}

extern "C" void* System_IO_MonoIO_get_ConsoleInput() {
  return (void*)stdin;
}

enum MonoFileType {
    Unknown=0x0000,
    Disk=0x0001,
    Char=0x0002,
    Pipe=0x0003,
    Remote=0x8000
};  

extern "C" MonoFileType System_IO_MonoIO_GetFileType(void* handle, int* error) {
  if (handle == (void*)stdin || handle == (void*)stdout || handle == (void*)stderr)
    return Char;
  else
    return Unknown;
}

extern "C" MonoString *
System_Environment_get_NewLine (void)
{
	N3 *vm = (N3*)VMThread::get()->getVM();
#if defined (PLATFORM_WIN32)
	declare_gcroot(ArrayChar*, array) = vm->asciizToArray("\r\n");
#else
	declare_gcroot(ArrayChar*, array) = vm->asciizToArray("\n");
#endif

	declare_gcroot(MonoString*, res) = (MonoString*)vm->arrayToString(array);
	return res;
}

extern "C" void
System_String_InternalCopyTo(MonoString* str, sint32 sindex, VMArray* dest, sint32 destIndex, sint32 count) {
	llvm_gcroot(str, 0);
	llvm_gcroot(dest, 0);
  declare_gcroot(const ArrayChar*, contents) = str->value;
  memcpy(&dest->elements[destIndex], &contents->elements[sindex], count * sizeof(uint16));
}

extern "C" uint16 System_String_get_Chars(MonoString* str, sint32 offset) {
	llvm_gcroot(str, 0);
  return str->value->elements[offset];
}

static sint32 byteLength(VMArray* array) {
	llvm_gcroot(array, 0);
  VMClassArray* cl = (VMClassArray*)array->classOf;
  VMCommonClass* base = cl->baseClass;
  uint32 size = base->naturalType->getPrimitiveSizeInBits() / 8;  
  return array->size * size;
}

extern "C" bool System_Buffer_BlockCopyInternal (VMArray* src, int src_offset, VMArray* dest, int dest_offset, int count) {
	llvm_gcroot(src, 0);
	llvm_gcroot(dest, 0);
  uint8 *src_buf, *dest_buf;

	/* watch out for integer overflow */
	if ((src_offset > byteLength(src) - count) || (dest_offset > byteLength(dest) - count))
		return false;

	src_buf = (uint8 *)src->elements + src_offset;
	dest_buf = (uint8 *)dest->elements + dest_offset;

	if (src != dest)
		memcpy (dest_buf, src_buf, count);
	else
		memmove (dest_buf, src_buf, count); /* Source and dest are the same array */

	return true;

}

extern "C" sint32 System_Buffer_ByteLengthInternal(VMArray* array) {
	llvm_gcroot(array, 0);
  return byteLength(array);
}

extern "C" sint32 
System_IO_MonoIO_Write (void* handle, ArrayUInt8 *src,
				  sint32 src_offset, sint32 count,
				  sint32 *error)
{
	llvm_gcroot(src, 0);
  char* buffer = (char*)alloca( 1024);//(count + 8) * sizeof(uint16));
	uint32 n = 0;

	*error = 0;
	
	if (src_offset + count > src->size)
		return 0;
   
  memcpy(buffer, (char*)&(src->elements[src_offset]), count);
  buffer[count] = 0;
	n = fprintf((FILE*)handle, buffer);

	return (sint32)n;
}

/* These parameters are "readonly" in corlib/System/NumberFormatter.cs */
extern "C" void
System_NumberFormatter_GetFormatterTables (guint64 const **mantissas,
					    gint32 const **exponents,
					    gunichar2 const **digitLowerTable,
					    gunichar2 const **digitUpperTable,
					    gint64 const **tenPowersList,
					    gint32 const **decHexDigits)
{
	*mantissas = Formatter_MantissaBitsTable;
	*exponents = Formatter_TensExponentTable;
	*digitLowerTable = Formatter_DigitLowerTable;
	*digitUpperTable = Formatter_DigitUpperTable;
	*tenPowersList = Formatter_TenPowersList;
	*decHexDigits = Formatter_DecHexDigits;
}

extern "C" VMObject* System_Threading_Thread_CurrentThread_internal() {
	declare_gcroot(VMObject*, appThread) = VMThread::get()->ooo_appThread;
  return appThread;
}

extern "C" VMObject*
System_Threading_Thread_GetCachedCurrentCulture (VMObject *obj)
{
	llvm_gcroot(obj, 0);
	return 0;
}

extern "C" VMObject*
System_Threading_Thread_GetSerializedCurrentCulture (VMObject *obj)
{
	llvm_gcroot(obj, 0);
	return 0;
}

extern "C" VMObject* System_Object_MemberwiseClone(VMObject* obj) {
	llvm_gcroot(obj, 0);
  uint64 size = obj->objectSize();
  declare_gcroot(VMObject*, res) = ((VMClass*)obj->classOf)->doNew();
  memcpy(res, obj, size);
  res->lockObj = 0;
  return res;
}

extern "C" bool
System_Globalization_CultureInfo_construct_internal_locale_from_current_locale (VMObject *ci)
{
	llvm_gcroot(ci, 0);
	return false;
}

extern "C" void
System_Threading_Thread_SetCachedCurrentCulture (VMObject* thread, VMObject *culture)
{
	llvm_gcroot(thread, 0);
	llvm_gcroot(culture, 0);
}

extern "C" void
System_String__ctor(MonoString* str, ArrayChar* array, sint32 startIndex, sint32 count) {
	llvm_gcroot(str, 0);
	llvm_gcroot(array, 0);
  N3* vm = VMThread::get()->getVM();
  declare_gcroot(const ArrayChar*, value) = vm->bufToArray(&(array->elements[startIndex]), count);
  str->length = count;
  str->startChar = array->elements[startIndex];
  str->value = value;
}

extern "C" MonoString * 
System_String_InternalJoin (MonoString *separator, VMArray * value, sint32 sindex, sint32 count)
{
	llvm_gcroot(separator, 0);
	llvm_gcroot(value, 0);
	declare_gcroot(MonoString*, current) = 0;
	sint32 length;
	sint32 pos;
	sint32 insertlen;
	sint32 destpos;
	sint32 srclen;
  const uint16 *insert;
	const uint16 *src;
	uint16 *dest;

	insert = separator->value->elements;
	insertlen = separator->length;

	length = 0;
	for (pos = sindex; pos != sindex + count; pos++) {
		current = (MonoString*)value->elements[pos];
		if (current != NULL)
			length += current->length;

		if (pos < sindex + count - 1)
			length += insertlen;
	}

	dest = (uint16*)alloca(length * sizeof(uint16));
	destpos = 0;

	for (pos = sindex; pos != sindex + count; pos++) {
		current = (MonoString*)value->elements[pos];
		if (current != NULL) {
			src = current->value->elements;
			srclen = current->length;

			memcpy (dest + destpos, src, srclen * sizeof(uint16));
			destpos += srclen;
		}

		if (pos < sindex + count - 1) {
			memcpy(dest + destpos, insert, insertlen * sizeof(uint16));
			destpos += insertlen;
		}
	}
  
  N3* vm = (N3*)VMThread::get()->getVM();
  declare_gcroot(const ArrayChar*, array) = vm->bufToArray(dest, length);
	declare_gcroot(MonoString*, res) = (MonoString*)vm->arrayToString(array);
	return res;
}

extern "C" MonoString *
System_String_InternalAllocateStr (sint32 length)
{
  declare_gcroot(MonoString*, str) = (MonoString*)MSCorlib::pString->doNew();
  str->length = length;
  return str;
}

