#ifndef _OTALK_H_
#define _OTALK_H_

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#ifdef OTALK_TOP
#define EXTERN extern
#else
#define EXTERN
#endif

/*#define STACK_PUSH(S_POINTER,VALUE)    { T_OBJECT v; v=VALUE; ++S_POINTER; *S_POINTER = v; }*/
#define STACK_PUSH(S_POINTER,VALUE)    (*(++S_POINTER))=(VALUE)
#define STACK_POP(STACKPOINTER,VALUE)  STACKPOINTER-=VALUE
#define STACK_POP_1(STACKPOINTER)      STACKPOINTER--


#define METHOD_HASHTABLE_LENGTH (4096)

typedef signed char t_byte;


typedef struct t_object* T_OBJECT;

typedef t_byte* t_codevector;

typedef T_OBJECT* t_stackpointer;

typedef t_stackpointer t_stack;

EXTERN t_stack         g_stack;
EXTERN t_stackpointer  g_stackpointer;

EXTERN T_OBJECT        doesNotUnderstand;
EXTERN T_OBJECT        sym_abort;
EXTERN T_OBJECT        sym_return;
EXTERN T_OBJECT        sym_arithmetic;
EXTERN T_OBJECT        sym_mustBeInteger;
EXTERN T_OBJECT        sym_handle_OCP;
EXTERN T_OBJECT        VALUE_T;
EXTERN T_OBJECT        VALUE_F;
EXTERN T_OBJECT        VALUE_NIL;

EXTERN T_OBJECT g_symbols;
EXTERN T_OBJECT g_classes;

EXTERN T_OBJECT g_top_object_class;
EXTERN T_OBJECT g_top_class_class;
EXTERN T_OBJECT g_primitive_class_number;
EXTERN T_OBJECT g_primitive_class_string;
EXTERN T_OBJECT g_primitive_class_bytevector;
EXTERN T_OBJECT g_primitive_class_symbol;
EXTERN T_OBJECT g_primitive_class_vector;
EXTERN T_OBJECT g_primitive_class_method;
EXTERN T_OBJECT g_primitive_class_nil;
EXTERN T_OBJECT g_primitive_class_true;
EXTERN T_OBJECT g_primitive_class_false;
EXTERN T_OBJECT g_primitive_class_false;
EXTERN T_OBJECT g_primitive_class_meta;
EXTERN T_OBJECT g_primitive_class_exception;
EXTERN T_OBJECT g_primitive_class_incarnation;
EXTERN T_OBJECT g_primitive_class_meta_meta;



/* LISTE IP_ */
#define IP_RETURN                  0
#define IP_PUSH_ARGS               1
#define IP_PUSH_SELF               2 
#define IP_PUSH_ARG                3 
#define IP_PUSH_CONST              4 
#define IP_PUSH_CONST_LONG         5 
#define IP_POP_N                   6 
#define IP_GOTO_N                  7 
#define IP_GOTO_LONG_N             8 
#define IP_GOTOREL_LONG_N          9 
#define IP_IF                      10
#define IP_CALL                    11
#define IP_CALL_SUPER              12
#define IP_CALL_PRIM               13
#define IP_CALL_PRIM_LONG          14
#define IP_POP                     15 
#define IP_SET_ARG                 16
#define IP_PUSH_SLOT               17
#define IP_SET_SLOT                18
#define IP_PUSH_GLOBAL             19
#define IP_PUSH_SLOT_0             20
#define IP_PUSH_SLOT_1             21
#define IP_PUSH_SLOT_2             22
#define IP_PUSH_SLOT_3             23
#define IP_PUSH_SLOT_4             24
#define IP_PUSH_SLOT_5             25
#define IP_PUSH_SLOT_6             26
#define IP_PUSH_TMP_1              27
#define IP_PUSH_TMP_2              28
#define IP_PUSH_TMP_3              29
#define IP_PUSH_TMP_4              30
#define IP_PUSH_ARG_0              31
#define IP_PUSH_ARG_1              32
#define IP_PUSH_ARG_2              33
#define IP_PUSH_ARG_3              34
#define IP_PUSH_ARG_4              35 
#define IP_PUSH_CONST_0            36
#define IP_PUSH_CONST_1            37
#define IP_PUSH_CONST_2            38
#define IP_PUSH_CONST_3            39
#define IP_PUSH_CONST_4            40
#define IP_PUSH_CONST_5            41
#define IP_PUSH_CONST_6            42
#define IP_PUSH_CONST_7            43
#define IP_CALL_WITH_ZERO          44

struct t_vector_def {
  int       length;
  T_OBJECT  slots[1];
};

struct t_bytevector_def {
  int           length;
  t_codevector  bytes;
};

struct t_string_def {
  int    length;
  char*  string;
};

struct t_number_def {
  long value;
};

struct t_object {
  T_OBJECT class;
  union t_object_struct {
    struct t_vector_def      vector;
    struct t_number_def      number;
    struct t_bytevector_def  bytevector;
    struct t_string_def      string;
  } structure;
};


typedef struct incarnation_def {
  T_OBJECT        method;       /* aufgerufene Methode */
  t_stackpointer  stackpointer; /* Ende des Stacks     */
  unsigned int    args;         /* Anzahl Argumente    */
} t_incarnation;  


/*#define CLASS(OBJ) ((OBJ)->class)*/
#define CLASS(OBJ) (isInt(OBJ)?g_primitive_class_number:((OBJ)->class))

#define CODE_VECTOR(METHOD)    (BV_BYTES(O_METHOD_VECTOR(METHOD)))
#define CODE_CONSTANTS(METHOD) (&SLOT_N(O_METHOD_CONSTANTS(METHOD),0))
#define CODE_NAME(METHOD)      (O_METHOD_NAME(METHOD))

#define O_METHOD_VECTOR(METHOD)        ((METHOD)->structure.vector.slots[0])
#define O_METHOD_CONSTANTS(METHOD)     ((METHOD)->structure.vector.slots[1])
#define O_METHOD_NAME(METHOD)          ((METHOD)->structure.vector.slots[2])
#define O_METHOD_OWNER(METHOD)         ((METHOD)->structure.vector.slots[3])
#define O_METHOD_N_ARGS(METHOD)        ((METHOD)->structure.vector.slots[4])
#define O_METHOD_ERROR_HANDLER(METHOD) ((METHOD)->structure.vector.slots[5])

#define EXCEPTION_KIND(OBJ)         (SLOT_N(OBJ,0))
#define EXCEPTION_SUBKIND(OBJ)      (SLOT_N(OBJ,1))
#define EXCEPTION_RECEIVER(OBJ)     (SLOT_N(OBJ,2))
#define EXCEPTION_METHOD(OBJ)       (SLOT_N(OBJ,3))
#define EXCEPTION_ARGS(OBJ)         (SLOT_N(OBJ,4))
#define EXCEPTION_RETURN_VALUE(OBJ) (SLOT_N(OBJ,5))

#define INCARNATION_METHOD(OBJ)         (SLOT_N(OBJ,0))
#define INCARNATION_RECEIVER(OBJ)       (SLOT_N(OBJ,1))
#define INCARNATION_ARGS(OBJ)           (SLOT_N(OBJ,2))


#define PC_START(CODEVECTOR)   (CODEVECTOR)

/* #define SET_NUMBER(OBJ,N)      ((OBJ)->structure.number.value = n) */
/* #define NUMBER(OBJ)            ((OBJ)->structure.number.value) */
#define SET_NUMBER(OBJ,N)      ((OBJ)->structure.number.value = N)
#define NUMBER(OBJ)            (((int)(OBJ)>>1))
#define OBJECTtoInt(OBJ)       (((int)(OBJ)>>1))
#define intToOBJECT(I)         ((T_OBJECT)(((I)<<1)|1))
#define isInt(OBJ)             ((int)(OBJ)&1)

#define BV_LENGTH(OBJ)         ((OBJ)->structure.bytevector.length)
#define BV_BYTES(OBJ)          ((OBJ)->structure.bytevector.bytes)

#define CLASS_METHODS(OBJ)     ((OBJ)->structure.vector.slots[0])
#define CLASS_SUPER(OBJ)       ((OBJ)->structure.vector.slots[1])
#define CLASS_NAME(OBJ)        ((OBJ)->structure.vector.slots[2])
#define CLASS_SLOTS(OBJ)       ((OBJ)->structure.vector.slots[3])
#define CLASS_CLASS_SLOTS(OBJ)       ((OBJ)->structure.vector.slots[4])
#define CLASS_CLASS_SLOT_VALUES(OBJ) ((OBJ)->structure.vector.slots[5])



#define STRING_LENGTH(OBJ)     ((OBJ)->structure.string.length)
#define STRING_CHARS(OBJ)      ((OBJ)->structure.string.string)

#define OBJ_LENGTH(OBJ)        ((OBJ)->structure.vector.length)
#define OBJ_SLOTS(OBJ)         ((OBJ)->structure.vector.slots)

#define VECTOR_LENGTH(OBJ)     ((OBJ)->structure.vector.length)
#define VECTOR_ENTRY(OBJ,N)    ((OBJ)->structure.vector.slots[N])
#define VECTOR_FILL(OBJ)       ((OBJ)->structure.vector.slots[0])

#define SET_SLOT_N(OBJ,N,V)    ((OBJ)->structure.vector.slots[N] = V)
#define SLOT_N(OBJ,N)          ((OBJ)->structure.vector.slots[N])

/* Funktionsdeklarationen */

char* mymalloc (unsigned m);

void otalk_error     (char *fmt, ...);
t_stack create_stack (unsigned int );


int primitive_name_index     (char* str);
int primitive_mandatory_args (int i);

T_OBJECT make_incarnation_object();
T_OBJECT make_exception(T_OBJECT ,T_OBJECT , T_OBJECT , t_stackpointer , int );
T_OBJECT make_method     (char* , char* , T_OBJECT* ,int , t_codevector , int , int ,char*);
T_OBJECT make_class      (char* , T_OBJECT ,char*);
T_OBJECT make_symbol     (char* );
T_OBJECT make_vector     (int );
T_OBJECT make_string_with(char *);
T_OBJECT make_string     (char *);
T_OBJECT make_bytevector (int ,t_byte* );
/*T_OBJECT make_number     (long );*/
#define make_number(I) intToOBJECT(I)
T_OBJECT symbol_for      (char* str);
void     init_objects    (void);

T_OBJECT class_for       (char* );

T_OBJECT install_class_hierarchy    ();
T_OBJECT make_method_for_bytevector (int length, t_byte* bytecode);

T_OBJECT interpreter_main_loop (t_stackpointer stackpointer, T_OBJECT method,long);

void print_class(T_OBJECT class);

void write_byte_code (int length, t_codevector vector, T_OBJECT,T_OBJECT*);
void print_method    (T_OBJECT method);

void init_hashtables(void);

T_OBJECT global_value_for(T_OBJECT symbol);
void set_global_value_for(T_OBJECT symbol,T_OBJECT value);

#endif

/*eof*/
