/*
 * support.c
 * ...
 *
 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
 */

#define	DBG(s)
#define	FDBG(s) s

#include "config.h"
#include "config-std.h"
#include "config-mem.h"
#include "config-io.h"
#include "jtypes.h"
#include "constants.h"
#include "file.h"
#include "access.h"
#include "readClassConfig.h"
#include "readClass.h"
#include "zipfile.h"
#include "kaffeh-support.h"

#if !defined(S_ISDIR)
#define	S_ISDIR(m)	((m) & S_IFDIR)
#endif

#if defined(__WIN32__) || defined (__amigaos__)
#define	PATHSEP	';'
#else
#define	PATHSEP	':'
#endif

void findClass(char*);

extern char realClassPath[];
extern char className[];
extern FILE* include;
extern FILE* stub;

extern char* translateSig(char*, char**, int*);
extern char* translateSigType(char*, char*);

static int objectDepth = -1;
static int argpos = 0;

/*
 * Init stub file.
 */
void
initStub(void)
{
	if (stub == 0) {
		return;
	}

	fprintf(stub,"/* DO NOT EDIT THIS FILE - it is machine generated */\n");
	fprintf(stub,"#include <StubPreamble.h>\n");
}

/*
 * Init include file.
 */
void
initInclude(void)
{
	argpos = 0;

	if (include == 0) {
		return;
	}

	fprintf(include, "/* DO NOT EDIT THIS FILE - it is machine generated */\n");
	fprintf(include, "#include <native.h>\n");
	fprintf(include, "\n");
	fprintf(include, "#ifndef _Included_%s\n", className);
	fprintf(include, "#define _Included_%s\n", className);
	fprintf(include, "\n");
	fprintf(include, "#ifdef __cplusplus\n");
	fprintf(include, "extern \"C\" {\n");
	fprintf(include, "#endif\n");
}

/*
 * Start stub file.
 */
void
startStub(void)
{
	if (stub == 0) {
		return;
	}

	fprintf(stub,"\n");
	fprintf(stub,"/* Stubs for class %s */\n", className);
}

/*
 * Start include file.
 */
void
startInclude(void)
{
	argpos = 0;

	if (include == 0) {
		return;
	}

	fprintf(include, "\n");
	fprintf(include, "/* Header for class %s */\n", className);
	fprintf(include, "\n");
	fprintf(include, "typedef struct Class%s {\n", className);
}

/*
 * End include file.
 */
void
endInclude(void)
{
	if (include == 0) {
		return;
	}

	fprintf(include, "\n");
	fprintf(include, "#ifdef __cplusplus\n");
	fprintf(include, "}\n");
	fprintf(include, "#endif\n");
	fprintf(include, "\n");
	fprintf(include, "#endif\n");
}

/*
 * Add a class.
 */
void
addClass(u2 this, u2 super, u2 access, constants* cpool)
{
	if (super == 0) {
		return;
	}

	findClass((char*)cpool->data[cpool->data[super]]);
}

/*
 * Finish processing a class's fields.
 */
void
readFieldEnd(void)
{
	if (include == 0) {
		return;
	}

	if (objectDepth == 0) {
		if (argpos == 0) {
			fprintf(include, "\tint __DUMMY__;\n");
		}
		fprintf(include, "} Class%s;\n", className);
		fprintf(include, "HandleTo(%s);\n\n", className);
	}
}

/*
 * Read and process a class field.
 */
void
readField(FILE* fp, Hjava_lang_Class* this, constants* cpool)
{
	field_info f;
	int argsize = 0;
	char* arg;
	int i;
	u2 cnt;
	u2 idx;
	u4 len;
	char cval[1000];
	union { jint i[2]; jdouble d; } u;

	readu2(&f.access_flags, fp);
	readu2(&f.name_index, fp);
	readu2(&f.signature_index, fp);

	/* Read in attributes */
	cval[0] = 0;
	readu2(&cnt, fp);
	for (i = 0; i < cnt; i++) {
		readu2(&idx, fp);
		readu4(&len, fp);
		if (strcmp((char*)cpool->data[idx], "ConstantValue") == 0) {
			assert(len == 2);
			readu2(&idx, fp);
			switch (cpool->tags[idx]) {
			case CONSTANT_Integer:
				sprintf(cval, "%d", (int)cpool->data[idx]);
				break;
			case CONSTANT_Float:
				sprintf(cval, "%g", *(float*)&cpool->data[idx]);
				break;
			case CONSTANT_Long:
#if SIZEOF_VOIDP == 8
				sprintf(cval, "0x%016lx", cpool->data[idx]);
#else
#if defined(WORDS_BIGENDIAN)
				sprintf(cval, "0x%08x%08x", cpool->data[idx], cpool->data[idx+1]);
#else
				sprintf(cval, "0x%08x%08x", cpool->data[idx+1], cpool->data[idx]);
#endif
#endif
				break;
			case CONSTANT_Double:
				u.i[0] = cpool->data[idx];
				u.i[1] = cpool->data[idx + 1];
				sprintf(cval, "%g", u.d);
				break;
			case CONSTANT_String:
				sprintf(cval, "\"%s\"", (char*)cpool->data[cpool->data[idx]]);
				break;
			default:
				sprintf(cval, "?unsupported type?");
				break;
			}
		}
		else {
			fseek(fp, len, SEEK_CUR);
		}
	}

	if (include != 0) {

		arg = translateSig((char*)cpool->data[f.signature_index], 0, &argsize);
		if (f.access_flags & ACC_STATIC) {
			if ((f.access_flags & (ACC_PUBLIC|ACC_FINAL)) == (ACC_PUBLIC|ACC_FINAL) && cval[0] != 0) {
				fprintf(include, "#define %s_%s %s\n", className, (char*)cpool->data[f.name_index], cval);
			}
		}
		else {
			argpos += argsize;
			fprintf(include, "  %s %s;\n", arg, (char*)cpool->data[f.name_index]);
		}
FDBG(		printf("Field: %s %s\n", arg, (char*)cpool->data[f.name_index]);			)
	}
}

/*
 * Read and process a method.
 */
void
readMethod(FILE* fp, Hjava_lang_Class* this, constants* cpool)
{
	method_info m;
	char* name;
	char* sig;
	char* str;
	char* ret;
	char* tsig;
	char type;
	int j;
	char rtype;
	int args;

	readu2(&m.access_flags, fp);
	readu2(&m.name_index, fp);
	readu2(&m.signature_index, fp);

	/* If we shouldn't generate method prototypes, quit now */
	if (objectDepth > 0) {
		return;
	}

DBG(	printf("Method %s%s\n", (char*)cpool->data[m.name_index], (char*)cpool->data[m.signature_index]);	)

	/* Only generate stubs for native methods */
	if (!(m.access_flags & ACC_NATIVE)) {
		return;
	}
	args = 0;

	/* Generate method prototype */
	name = (char*)cpool->data[m.name_index];
	sig = (char*)cpool->data[m.signature_index];
	ret = strchr(sig,')');
	ret++;

	if (include != 0) {
		fprintf(include, "extern %s", translateSig(ret, 0, 0));
		fprintf(include, " %s_%s(", className, name);
		if (!(m.access_flags & ACC_STATIC)) {
			fprintf(include, "struct H%s*", className);
			if (sig[1] != ')') {
				fprintf(include, ", ");
			}
		}
	}

	str = sig + 1;
	args++;
	while (str[0] != ')') {
		tsig = translateSig(str, &str, &args);
		if (include != 0) {
			fprintf(include, "%s", tsig);
			if (str[0] != ')') {
				fprintf(include, ", ");
			}
		}
	}
	if (include != 0) {
		fprintf(include, ");\n");
	}

	if (stub != 0) {
		/* Generate method stub */
		fprintf(stub, "\n/* SYMBOL: %s_%s%s */\n", className, name, sig);
		translateSigType(ret, &rtype);
		fprintf(stub, "void\n");
		fprintf(stub, "Kaffe_%s_%s_stub(stack_item* _P_, stack_item* _R_)\n", className, name);
		fprintf(stub, "{\n");
		fprintf(stub, "\textern %s", translateSig(ret, 0, 0));
		fprintf(stub, " %s_%s(", className, name);
		str = sig + 1;

		/* If not static, we pass the object */
		if (!(m.access_flags & ACC_STATIC)) {
			fprintf(stub, "void*");
			if (str[0] != ')') {
				fprintf(stub, ", ");
			}
		}
		while (str[0] != ')') {
			tsig = translateSig(str, &str, 0);
			if (strncmp(tsig, "struct H", 8) == 0) {
				fprintf(stub, "void*");
			}
			else {
				fprintf(stub, "%s", tsig);
			}
			if (str[0] != ')') {
				fprintf(stub, ", ");
			}
		}
		fprintf(stub, ");\n");
		fprintf(stub, "\t");
		if (rtype != 'v') {
			fprintf(stub, "%s ret = ", translateSig(ret, 0, 0));
		}
		fprintf(stub, "%s_%s(", className, name);
		str = sig + 1;
		j = args - 1;
		if (!(m.access_flags & ACC_STATIC)) {
			fprintf(stub, "_P_[%d].p", j);
			if (str[0] != ')') {
				fprintf(stub, ", ");
			}
		}
		j--;
		for (; str[0] != ')'; j--) {
			str = translateSigType(str, &type);
			/* This is horrid - but basically just copy */
			/* the data one-for-one.		    */
			switch(type) {
			case 'f':
				fprintf(stub, "_P_[%d].f", j);
				break;
			case 'd':
				j--;
				fprintf(stub, "_P_[%d].d", j);
				break;
			case 'l':
				j--;
				fprintf(stub, "_P_[%d].l", j);
				break;
			case 'b':
			case 'c':
			case 'i':
			case 's':
			case 'z':
				fprintf(stub, "_P_[%d].i", j);
				break;
			case 'p':
				fprintf(stub, "_P_[%d].p", j);
				break;
			case 'v':
				break;
			}
			if (str[0] != ')') {
				fprintf(stub, ", ");
			}
		}
		fprintf(stub, ");\n");
		switch (rtype) {
		case 'f':
			fprintf(stub, "\treturn_float(ret);\n");
			break;
		case 'd':
			fprintf(stub, "\treturn_double(ret);\n");
			break;
		case 'l':
			fprintf(stub, "\treturn_long(ret);\n");
			break;
		case 'b':
		case 'c':
		case 'i':
		case 's':
		case 'z':
			fprintf(stub, "\treturn_int(ret);\n");
			break;
		case 'p':
			fprintf(stub, "\treturn_ref(ret);\n");
			break;
		case 'v':
			break;
		}
		fprintf(stub, "}\n");
	}
}

/*
 * Locate class specified and process it.
 */
void
findClass(char* nm)
{
	FILE* fp;
	ZipFile zipf;
	ZipDirectory* zipd;
	char superName[100];
	struct stat sbuf;
	char* start;
	char* end = (char*)1;
	int j;
	constants* savepool;

	/* If classpath isn't set, get it from the environment */
	if (realClassPath[0] == 0) {
		start = getenv("KAFFE_CLASSPATH");
		if (start == 0) {
			start = getenv("CLASSPATH");
		}
		if (start == 0) {
			fprintf(stderr, "CLASSPATH not set!\n");
			exit(1);
		}
		strcpy(realClassPath, start);
	}

	for (start = realClassPath; end != 0; start = end + 1) {
		end = strchr(start, PATHSEP);
		if (end == 0) {
			strcpy(superName, start);
		}
		else {
			strncpy(superName, start, end-start);
			superName[end-start] = 0;
		}

		if (stat(superName, &sbuf) < 0) {
			/* Ignore */
		}
		else if (S_ISDIR(sbuf.st_mode)) {
			strcat(superName, "/");
			strcat(superName, nm);
			strcat(superName, ".class");
			fp = fopen(superName, "rb");
			if (fp != 0) {
				goto found;
			}
		}
		else {
			/* Zip file */
			fp = fopen(superName, "rb");
			zipf.fd = fileno(fp);
			if (fp == 0 || read_zip_archive(&zipf) != 0) {
				continue;
			}

			strcpy(superName, nm);
			strcat(superName, ".class");

			zipd = (ZipDirectory*)zipf.central_directory;
			for (j = 0; j < zipf.count; j++, zipd = ZIPDIR_NEXT(zipd)) {
				if (strcmp(superName, ZIPDIR_FILENAME(zipd)) == 0) {
					fseek(fp, zipd->filestart, SEEK_SET);
					goto found;
				}
			}
			fclose(fp);
		}
	}
	fprintf(stderr, "Failed to open object '%s'\n", nm);
	exit(1);

	found:
	objectDepth++;
	savepool = constant_pool;
	readClass(NULL, fp, NULL);
	constant_pool = savepool;
	objectDepth--;
	fclose(fp);
}
