/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#ifndef LI_CLASS_HH
#define LI_CLASS_HH


#include "lisp/lisp.hh"
#include "memory/fixed_array.hh"


class li_class_type;
struct sym_var;


struct li_class_member
{
  char *name;
  int offset;
  li_type_number class_type;
  li_symbol *sym;

  li_class_member(char *name) : name(name), sym(0), class_type(0) {}
};

struct li_string_class_member : public li_class_member
{
  li_string_class_member(char *name) : li_class_member(name) {}
  char *operator()();
};

struct li_int_class_member : public li_class_member
{
  li_int_class_member(char *name) : li_class_member(name) {}
  int &operator()();
};


struct li_float_class_member : public li_class_member
{
  li_float_class_member(char *name) : li_class_member(name) {}
  float &operator()();
};


struct li_symbol_class_member : public li_class_member
{
  li_symbol_class_member(char *name) : li_class_member(name) {}
  li_symbol *&operator()();
};


struct li_object_class_member : public li_class_member
{
  li_object_class_member(char *name) : li_class_member(name) {}
  li_object *&operator()();
};


class li_class : public li_object
{
  void **values;
  li_class_type *get_type() const { return (li_class_type *)li_get_type(type()); }
public:
  void mark(int set); 

  void save(i4_saver_class *fp, li_environment *env);
  void load(i4_loader_class *fp, li_type_number *type_remap, li_environment *env);
  void print(i4_file_class *fp);

  li_class(li_type_number class_type, li_object *params=0, li_environment *env=0);

  void free();

  int member_offset(char *sym) const;    // does not type check
  int member_offset(li_symbol *sym) const;    // does not type check

  int get_offset(li_class_member &m) const;
  int get_offset(li_class_member &m, li_type_number _type) const;

  int member_offset(li_class_member &m) const   // inline version checks to see to see if cached
  {
    if (m.class_type!=type())
      get_offset(m);
    return m.offset;
  }

  int member_offset(li_class_member &m, li_type_number _type) const
  {
    if (m.class_type!=type())
      get_offset(m, _type);
    return m.offset;
  }



  char *string_value(int member) const { return ((li_string *)values[member])->value(); }
  int &int_value(int member) { return *(((int *) (values+member))); }
  float &float_value(int member) { return *(((float *) (values+member))); }
  li_symbol *&symbol_value(int member)  { return *((li_symbol **)(values+member)); }
  li_object *&object_value(int member)  { return *((li_object **)(values+member)); }

  char *get(li_string_class_member &c) const { return string_value(member_offset(c, LI_STRING)); }
  int &get(li_int_class_member &c)  {  return int_value(member_offset(c, LI_INT)); }
  float &get(li_float_class_member &c) { return float_value(member_offset(c, LI_FLOAT)); }
  li_symbol *&get(li_symbol_class_member &c) { return symbol_value(member_offset(c, LI_SYMBOL)); }
  li_object *&get(li_class_member &c) { return object_value(member_offset(c)); }


  li_object *value(char *member_name);
  li_object *value(int member);
  void set_value(int member, li_object *value);
  void set(li_class_member &c, li_object *value) { set_value(member_offset(c), value); }
  li_object *set(char *member_name, li_object *value); // slow, but easy way to access data

#ifdef LI_TYPE_CHECK
  static li_class *get(li_object *o, li_environment *env);    
#else
  static li_class *get(li_object *o, li_environment *env)  { return ((li_class *)o); }
#endif


};

li_object *li_def_class(li_object *fields, li_environment *env);

int li_class_total_members(li_type_number type);
li_symbol *li_class_get_symbol(li_type_number type, int member_number);
li_object *li_class_get_default(li_type_number type, li_symbol *sym);
li_object *li_class_get_property_list(li_type_number type, li_symbol *sym);
void li_set_class_editor(li_type_edit_class *editor);

extern li_class *li_this;

inline char *li_string_class_member::operator()() { return li_this->get(*this); }
inline int &li_int_class_member::operator()() { return li_this->get(*this); }
inline float &li_float_class_member::operator()() { return li_this->get(*this); }
inline li_symbol *&li_symbol_class_member::operator()() { return li_this->get(*this); }
inline li_object *&li_object_class_member::operator()() { return li_this->get(*this); }

class li_class_context
{
  li_class *old_context;
public:
  li_class_context(li_class *current_context) 
  { 
    old_context=li_this;
    li_this=current_context; 
  }

  ~li_class_context() { li_this=old_context; }
};

#endif