#include "otalk.h"
#include <assert.h>


#define malloc_object() ((T_OBJECT)mymalloc(sizeof(struct t_object)))


void pre_install_class_hierarchy();
void make_symbols(void);

T_OBJECT  make_true_object();
T_OBJECT  make_false_object();
T_OBJECT  make_nil_object();
T_OBJECT  make_slots_vector_from(char* slots);

char* mymalloc(unsigned m)
{
  char* res = malloc(m);

  if (res == NULL) {
    fprintf(stderr,"\nKein Mem mehr\n");
    exit(10);
  }
  return res;
}

void init_hashtables(void)
{
  extern T_OBJECT *method_hashtable;
  extern T_OBJECT *method_hashtable_class;

  method_hashtable       = (T_OBJECT*)mymalloc(sizeof(T_OBJECT)*METHOD_HASHTABLE_LENGTH);
  method_hashtable_class = (T_OBJECT*)mymalloc(sizeof(T_OBJECT)*METHOD_HASHTABLE_LENGTH);
}


void init_objects(void)
{
  make_true_object();
  make_false_object();
  make_nil_object();

  pre_install_class_hierarchy();

  CLASS(VALUE_T)   = g_primitive_class_true;
  CLASS(VALUE_F)   = g_primitive_class_false;
  CLASS(VALUE_NIL) = g_primitive_class_nil;

  g_symbols = make_vector(2000);
  SET_SLOT_N(g_symbols,0,make_number(0));

  g_classes = make_vector(200);
  SET_SLOT_N(g_classes,0,make_number(0));

  g_top_object_class = install_class_hierarchy();
/*   make_true_object(); */
/*   make_false_object(); */
/*   make_nil_object(); */
  CLASS_SUPER(g_top_object_class) = VALUE_NIL;
  make_symbols();
}

void make_symbols(void)
{
  doesNotUnderstand = make_symbol("doesNotUnderstand()");
  sym_abort         = make_symbol("abort");
  sym_return        = make_symbol("return");
  sym_arithmetic    = make_symbol("arithmetic");
  sym_mustBeInteger = make_symbol("mustBeInteger");
  sym_handle_OCP    = make_symbol("handle()");
}


void add_object_to_liste(T_OBJECT liste, T_OBJECT o)
{
  int i;
  
  i = NUMBER(SLOT_N(liste,0));
  
  SET_SLOT_N(liste,i+1,o); /* + 1, da 0 belegt ist. */
  SET_SLOT_N(liste,0,make_number(i+1));
}


void add_symbol(T_OBJECT symbol)
{
  add_object_to_liste(g_symbols,symbol);
}

void add_class(T_OBJECT class)
{
  add_object_to_liste(g_classes,class);
}

T_OBJECT find_chars(char* str)
{
  int i,max = NUMBER(SLOT_N(g_symbols,0));
  /* fprintf(stderr,"max: %d\n",max); */
  for (i=1; i <=max; ++i)
    if (!strcmp(STRING_CHARS(SLOT_N(g_symbols,i)),str))
      return SLOT_N(g_symbols,i);

  return NULL;
}

t_byte* copy_bytevector(int length, t_byte* bytes)
{
  t_byte* result = (t_byte*)mymalloc(sizeof(t_byte)*length);
  t_byte* p=result;

  while (length-- > 0)
    *p++ = *bytes++;

  return result;
}

T_OBJECT make_true_object()
{
  T_OBJECT result;

  result = malloc_object();

  CLASS(result) = g_primitive_class_true;
  VALUE_T = result;

  return result;
}

T_OBJECT make_false_object()
{
  T_OBJECT result;

  result = malloc_object();

  CLASS(result) = g_primitive_class_false;
  VALUE_F = result;

  return result;
}

T_OBJECT make_nil_object()
{
  T_OBJECT result;

  result = malloc_object();

  CLASS(result) = g_primitive_class_nil;
  VALUE_NIL = result;

  return result;
}

T_OBJECT make_vector(int length)
{
  T_OBJECT result;

  result = (T_OBJECT)mymalloc(sizeof(struct t_object)  + (length-1)*sizeof(T_OBJECT));

  OBJ_LENGTH(result) = length;
  CLASS(result) = g_primitive_class_vector;
  --length;
  while (length >= 0) {
    SET_SLOT_N(result,length,VALUE_NIL);
    if (VALUE_NIL == NULL) {
      printf("VALUE_NIL ist NULL\n");
      assert(1==2);
      exit(10);
    }
    --length;
  }
  return result;
}


T_OBJECT make_bytevector(int length,t_byte* bytes)
{
  T_OBJECT result;

  result = malloc_object();
  CLASS(result) = g_primitive_class_bytevector;

  BV_LENGTH(result) = length;

  if (bytes != NULL) {
    BV_BYTES(result) = copy_bytevector(length,bytes);
  } 
  else {
    BV_BYTES(result) = NULL;
  }

  return result;
}


/*
static T_OBJECT static_numbers[2048];
T_OBJECT make_number(long n)
{
  T_OBJECT result;

  if ((n >= 0) && (n < 2048) && (static_numbers[n] != NULL)) {
    return static_numbers[n];
  }
  result = malloc_object();
  CLASS(result) = g_primitive_class_number;

  SET_NUMBER(result,n);

  if ((n >= 0) && (n < 2048))
    static_numbers[n] = result;

  return result;
}
T_OBJECT make_number(long n)
{
  return intToOBJECT(n);
}
*/

T_OBJECT make_string(char *str)
{
  T_OBJECT result;
  int length = strlen(str);

  result = malloc_object();
  CLASS(result) = g_primitive_class_string;
  
  STRING_LENGTH(result) = length;
  STRING_CHARS(result)  = (char*)mymalloc(length+1);
  strcpy(STRING_CHARS(result),str);

  return result;
}

T_OBJECT make_string_with(char *str)
{
  T_OBJECT result;

  result = malloc_object();
  CLASS(result) = g_primitive_class_string;
  
  STRING_CHARS(result) = str;

  return result;
}

T_OBJECT make_symbol(char* str)
{
  T_OBJECT found = find_chars(str);

  if (found != NULL)
    return found;

  found = make_string(str);
  CLASS(found) = g_primitive_class_symbol;

  add_symbol(found);
  /* fprintf(stderr,"Neues Symbol: \"%s\"\n",str); */
  return found;
}

T_OBJECT symbol_for(char* str)
{
  return make_symbol(str);
}

T_OBJECT class_for(char* str)
{
  int i,max = NUMBER(SLOT_N(g_classes,0));

  for (i=1; i <=max; ++i)
    if (!strcmp(STRING_CHARS(CLASS_NAME(SLOT_N(g_classes,i))),str))
      return SLOT_N(g_classes,i);

  return NULL;
}


T_OBJECT make_empty_class()
{
  return make_vector(6);
}


T_OBJECT fill_created_class(T_OBJECT result, char* name, T_OBJECT super, char* slots)
{
  T_OBJECT o_name = make_string(name);

  CLASS_SUPER(result)   = super;
  CLASS(result)         = g_primitive_class_meta;
  CLASS_METHODS(result) = make_vector(300);
  CLASS_NAME(result)    = o_name;
  CLASS_CLASS_SLOTS(result)       = make_vector(0);
  CLASS_CLASS_SLOT_VALUES(result) = make_vector(0);
  CLASS_SLOTS(result)   = make_slots_vector_from(slots);

  VECTOR_FILL(CLASS_METHODS(result)) = make_number(0);

  add_class(result);

  return result;
}

T_OBJECT make_slots_vector_from(char* slots)
{
  T_OBJECT res;
  int      count=1,i=0;
  char*    p,*last;
  char     buffer[400];
  char*    strings[40];

  /*printf("Slots %s\n",slots);*/
  if ((slots == NULL) || (!strcmp("",slots)))
    return make_vector(0);

  p=last=buffer;
  strcpy(buffer,slots);
  while (*p != 0) {
    if (*p == ' ') {
      *p = 0;
      strings[i] = last;
      /*printf("Found: %s\n",last);*/
      last = p+1;
      ++i;
      ++count;
    }
    ++p;
  }
  strings[i] = last;

  res = make_vector(count);
  for (i=0; i<count; ++i) {
    /*printf("Erzeuge Slot %s\n",strings[i]);*/
    VECTOR_ENTRY(res,i) = make_string(strings[i]);
  }
  
  return res;
}

T_OBJECT make_class(char* name, T_OBJECT super,char* slots)
{
  return fill_created_class(make_empty_class(),name,super,slots);
}

T_OBJECT install_class_class(T_OBJECT class, char* name)
{
  T_OBJECT class_class = make_empty_class();
  T_OBJECT super;

  if (CLASS_SUPER(class) == NULL)
    super = g_top_class_class;
  else
    super = CLASS(CLASS_SUPER(class));

  fill_created_class(class_class,name,super,NULL);
  CLASS(class_class) = g_primitive_class_meta;
  CLASS(class) = class_class;

  printf("Installiere Klasse %s für Klasse %s (Super %s). Superklasse ist %s\n",
	 STRING_CHARS(CLASS_NAME(class_class)),
	 STRING_CHARS(CLASS_NAME(class)),
	 CLASS_SUPER(class)==NULL?"<Kein>":STRING_CHARS(CLASS_NAME(CLASS_SUPER(class))),
	 STRING_CHARS(CLASS_NAME(super)));
	 
  return class_class;
}

void pre_install_class_hierarchy()
{
  g_top_class_class             = make_empty_class();
  g_primitive_class_meta        = make_empty_class();
  g_primitive_class_meta_meta   = make_empty_class();
  g_primitive_class_number      = make_empty_class();
  g_primitive_class_string      = make_empty_class();
  g_primitive_class_bytevector  = make_empty_class();
  g_primitive_class_symbol      = make_empty_class();
  g_primitive_class_vector      = make_empty_class();
  g_primitive_class_method      = make_empty_class();
  g_primitive_class_true        = make_empty_class();
  g_primitive_class_false       = make_empty_class();
  g_primitive_class_nil         = make_empty_class();
  g_primitive_class_exception   = make_empty_class();
  g_primitive_class_incarnation = make_empty_class();
}

T_OBJECT install_class_hierarchy()
{
  T_OBJECT object_class,classObject;
  T_OBJECT sys;

  object_class = make_class("Object",NULL,NULL);
  g_top_object_class = object_class;

  sys = make_class("Sys",object_class,NULL);

  fill_created_class(g_primitive_class_number,"Integer",sys,NULL);
  fill_created_class(g_primitive_class_symbol,"Symbol",sys,NULL);
  fill_created_class(g_primitive_class_bytevector,"Bytevector",sys,NULL);
  fill_created_class(g_primitive_class_string,"String",sys,NULL);
  fill_created_class(g_primitive_class_vector,"FixedVector",sys,NULL);
  fill_created_class(g_primitive_class_method,"Method",sys,"vector constants name owner args error_handler");
  fill_created_class(g_primitive_class_true,"True",sys,NULL);
  fill_created_class(g_primitive_class_false,"False",sys,NULL);
  fill_created_class(g_primitive_class_nil,"Nil",sys,NULL);
  fill_created_class(g_primitive_class_exception,"Exception",sys,"kind subkind receiver method args return_value");
  fill_created_class(g_primitive_class_incarnation,"Incarnation",sys,"method receiver args");


  fill_created_class(g_top_class_class,"Class",object_class,
		     "methods super_class name slots class_slots class_values");
  fill_created_class(g_primitive_class_meta,"MetaClass",object_class,NULL);
  fill_created_class(g_primitive_class_meta_meta,"MetaMetaClass",object_class,NULL);

  CLASS(g_top_class_class)           = g_primitive_class_meta;
  CLASS(g_primitive_class_meta)      = g_primitive_class_meta_meta;
  CLASS(g_primitive_class_meta_meta) = g_primitive_class_meta_meta;


  CLASS(g_primitive_class_number)      = g_primitive_class_meta;
  CLASS(g_primitive_class_symbol)      = g_primitive_class_meta;
  CLASS(g_primitive_class_bytevector)  = g_primitive_class_meta;
  CLASS(g_primitive_class_string)      = g_primitive_class_meta;
  CLASS(g_primitive_class_vector)      = g_primitive_class_meta;
  CLASS(g_primitive_class_method)      = g_primitive_class_meta;
  CLASS(g_primitive_class_true)        = g_primitive_class_meta;
  CLASS(g_primitive_class_false)       = g_primitive_class_meta;
  CLASS(g_primitive_class_nil)         = g_primitive_class_meta;
  CLASS(g_primitive_class_exception)   = g_primitive_class_meta;
  CLASS(g_primitive_class_incarnation) = g_primitive_class_meta;

  CLASS(object_class) = g_top_class_class;
  CLASS(sys)          = g_primitive_class_meta;

  CLASS_SUPER(g_top_class_class)      = object_class;

  CLASS_SUPER(g_primitive_class_meta)      = g_top_class_class;
  CLASS_SUPER(g_primitive_class_meta_meta) = g_top_class_class;


  classObject = install_class_class(object_class,"ClassObject");
  CLASS_SUPER(classObject) = g_top_class_class;

  install_class_class(sys,"ClassSys");

  install_class_class(g_primitive_class_number,"ClassInteger");
  install_class_class(g_primitive_class_symbol,"ClassSymbol");
  install_class_class(g_primitive_class_bytevector,"ClassBytevector");
  install_class_class(g_primitive_class_string,"ClassString");
  install_class_class(g_primitive_class_vector,"ClassFixedVector");
  install_class_class(g_primitive_class_method,"ClassMethod");
  install_class_class(g_primitive_class_false,"ClassFalse");
  install_class_class(g_primitive_class_true,"ClassTrue");
  install_class_class(g_primitive_class_nil,"ClassNil");
  install_class_class(g_primitive_class_exception,"ClassException");
  install_class_class(g_primitive_class_incarnation,"ClassIncarnation");

  return object_class;
}


T_OBJECT class_add_method(T_OBJECT class, T_OBJECT method)
{
  T_OBJECT liste = CLASS_METHODS(class);
  int i;
  int max = NUMBER(VECTOR_FILL(liste));

  for (i=1; i <= max; ++i) {
    if (O_METHOD_NAME(VECTOR_ENTRY(liste,i)) == O_METHOD_NAME(method)) {
      VECTOR_ENTRY(liste,i) = method;
      return method;
    }
  }
  ++max;

  VECTOR_ENTRY(liste,max) = method;
  VECTOR_FILL(liste) = make_number(max);

  return method;
}

T_OBJECT make_method_object()
{
  T_OBJECT result;

  result = make_vector(6);
  CLASS(result) = g_primitive_class_method;

  return result;
}

T_OBJECT make_exception_object()
{
  T_OBJECT result;

  result = make_vector(7);
  CLASS(result) = g_primitive_class_exception;

  return result;
}

T_OBJECT make_exception(T_OBJECT kind,T_OBJECT subkind, T_OBJECT method, t_stackpointer stackpointer, int args)
{
  T_OBJECT result = make_exception_object();
  T_OBJECT vector;
  int i;

  vector = make_vector(args);
  for (i=1; i<=args; ++i)
    SET_SLOT_N(vector,i-1,*(stackpointer+i));

  EXCEPTION_KIND(result)     = kind;
  EXCEPTION_SUBKIND(result)  = subkind;
  EXCEPTION_RECEIVER(result) = *stackpointer;
  EXCEPTION_METHOD(result)   = method;
  EXCEPTION_ARGS(result)     = vector;

  return result;
}

T_OBJECT make_incarnation_object()
{
  T_OBJECT result;

  result = make_vector(3);
  CLASS(result) = g_primitive_class_incarnation;

  return result;
}


T_OBJECT make_method(char* classname, char* name, T_OBJECT* constants,/*)
*/		     int c_length, t_codevector vector, int length, int args,char* errorhandler)
{
  T_OBJECT result = make_method_object();
  T_OBJECT v_constants = make_vector(c_length);
  int i;

  /*printf("Install Method %s.%s\n",classname,name);*/
  for (i=0; i<c_length; ++i)
    SET_SLOT_N(v_constants,i,constants[i]);

  O_METHOD_CONSTANTS(result)     = v_constants;
  O_METHOD_VECTOR(result)        = make_bytevector(length,vector);
  O_METHOD_NAME(result)          = make_symbol(name);
  O_METHOD_OWNER(result)         = class_for(classname);
  O_METHOD_N_ARGS(result)        = make_number(args);
  O_METHOD_ERROR_HANDLER(result) = (errorhandler == NULL)?VALUE_NIL:make_symbol(errorhandler);
  /* printf("######################### %d in %s.%s\n",args,classname,name); */

  if (O_METHOD_OWNER(result) == NULL) {
    fprintf(stderr,"Kann Klasse %s nicht finden\n",classname);
    assert(O_METHOD_OWNER(result) != NULL);
  }
  class_add_method(O_METHOD_OWNER(result),result);
  
  /*printf("New Pointer: %p\n",result);*/

  return result;
}

/* ----------------------------------------------------------------------
   Help-Function 
   ---------------------------------------------------------------------- */

T_OBJECT make_method_for_bytevector(int length, t_byte* bytecode)
{
  T_OBJECT method;
  T_OBJECT codevector;

  codevector = make_bytevector(length,bytecode);
  method = make_vector(4);
  SET_SLOT_N(method,0,codevector);
  SET_SLOT_N(method,1,make_vector(0));

  return method;
}


/*eof*/
