/**********************************************************************
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) 
***********************************************************************/

#include "memory/array.hh"
#include "lisp/li_types.hh"
#include "file/file.hh"
#include "lisp/lisp.hh"
#include "loaders/dir_save.hh"
#include "loaders/dir_load.hh"
#include 


li_string::li_string(char *name)
  : li_object(LI_STRING)
{ 
  int l=strlen(name)+1; 
  _name=(char *)i4_malloc(l,"");
  memcpy(_name, name, l);
}

li_string::li_string(int len)
  : li_object(LI_STRING)
{
  _name=(char *)i4_malloc(len,"");
}

li_string::li_string(const i4_const_str &str)
  : li_object(LI_STRING)
{
  int len=str.length()+1;
  _name=(char *)i4_malloc(len,"");
  i4_os_string(str, _name, len);
}



void li_save_type(i4_file_class *fp, li_type_number type)
{
  fp->write_16(type);
}

li_type_number  li_load_type(i4_file_class *fp, li_type_number *type_remap)
{
  I4_ASSERT(type_remap, "call li_load_type_info before li_load_type");

  return type_remap[fp->read_16()];
}


void li_save_object(i4_saver_class *fp, li_object *o, li_environment *env)
{
  if (!o)
    fp->write_16(0);
  else
  {
    li_save_type(fp, o->type());

    int h;
    if (o->type()>LI_TYPE)
      h=fp->mark_size();

    li_get_type(o->type())->save_object(fp, o, env);

    if (o->type()>LI_TYPE)
      fp->end_mark_size(h);
  }
}


li_object *li_load_object(i4_loader_class *fp, li_type_number *type_remap, li_environment *env)
{
  li_type_number old_type=fp->read_16();  
  li_type_number type=type_remap[old_type];
  
  if (old_type==0)
    return 0;

  w32 skip=0;
  if (old_type>LI_TYPE)
    skip=fp->read_32();
  else if (type==0)
    i4_error("huh?");   // shouldn't happen (please, please)

  if (type)
    return li_get_type(type)->load_object(fp, type_remap, env);
  else if (type>0 && type<=LI_TYPE)
  {
    li_error(env, "type not found, but should be");
    return 0;
  }
  else
  {
    fp->seek(fp->tell() + skip);
    return 0;
  }
}

class li_invalid_type_function : public li_type_function_table
{
  virtual void mark(li_object   *o, int set) { i4_error("marking invalid object"); }
  virtual void free(li_object   *o) { i4_error("freeing invalid object"); }
  virtual int equal(li_object  *o1, li_object *o2)  
  {  
    i4_error("comparing  invalid object"); 
    return 0;
  }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { i4_error("printing invalid object"); }
  virtual char *name() { i4_error("getting name for invalid object"); return 0;}

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env) 
  { li_error(env, "saving invalid object"); }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap, li_environment *env)
  { 
    li_error(env, "loading invalid object"); 
    return 0;
  }

};


void li_symbol::free()
{
  i4_free(data);
}



class li_symbol_type_function : public li_type_function_table
{
  virtual void mark(li_object *o, int set)
  { 
    li_symbol *s=(li_symbol *)o;
    s->mark(set);

    if (s->value())
    {
      if (set!=s->value()->is_marked())
        li_get_type(s->value()->unmarked_type())->mark(s->value(), set);    
    }

    li_object *fun=s->fun();
    if (fun)
    {
      if (set!=fun->is_marked())
        li_get_type(fun->unmarked_type())->mark(fun, set);    
    }

    li_object *name=s->name();
    if (set!=name->is_marked())
      li_get_type(name->unmarked_type())->mark(name, set);    
  }

  virtual void free(li_object   *o)
  {
    li_symbol::get(o,0)->free();
  }
 
  virtual void print(li_object  *o, i4_file_class *stream)   
  {  
    li_symbol *s=li_symbol::get(o,0);
    char *name=s->name()->value();    
    stream->write(name, strlen(name));
  }
  virtual char *name() { return "symbol"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    li_symbol *s=li_symbol::get(o,env);
    char *name=s->name()->value();
    int name_len=strlen(name)+1;

    fp->write_16(name_len);
    fp->write(name, name_len);
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap,
                                 li_environment *env)
  { 
    char buf[200];
    int len=fp->read_16();
    if (len>200)
      li_error(env, "symbol name too long");
    fp->read(buf, len);
    return li_get_symbol(buf);
  }
};



char *li_get_type_name(li_type_number type)
{
  return li_get_type(type)->name();
}

li_string::li_string(i4_file_class *fp) : li_object(LI_STRING)
{
  int l=fp->read_32();
  _name=(char *)i4_malloc(l,"");
  fp->read(_name, l);
}

class li_string_type_function : public li_type_function_table
{ 
  virtual void free(li_object   *o) 
  { 
    i4_free(li_string::get(o,0)->value());
  }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    stream->printf("\"%s\"", li_string::get(o,0)->value());
  }

  virtual int equal(li_object  *o1, li_object *o2)  
  {   
    return (strcmp(li_string::get(o1,0)->value(), li_string::get(o2,0)->value())==0);
  }

  virtual char *name() { return "string"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    char *s=li_string::get(o,env)->value();
    int l=strlen(s)+1;
    fp->write_32(l);
    fp->write(s,l);
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap, li_environment *env)
  {
    return new li_string(fp);
  }

};



class li_int_type_function : public li_type_function_table
{
  virtual int equal(li_object  *o1, li_object *o2)
  { 
    return li_int::get(o1,0)->value()==li_int::get(o2, 0)->value(); 
  }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    stream->printf("%d", li_int::get(o,0)->value());
  }

  virtual char *name() { return "int"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    fp->write_32(li_int::get(o,0)->value());
  }

  virtual li_object *load_object(i4_loader_class *fp,  li_type_number *type_remap,
                                 li_environment *env)
  {
    return new li_int(fp->read_32());
  }
    

};


class li_type_type_function : public li_type_function_table
{
  virtual int equal(li_object  *o1, li_object *o2)  
  { 
    return li_int::get(o1,0)->value()==li_int::get(o2,0)->value(); 
  }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    stream->printf("type-%s", li_get_type(li_type::get(o,0)->value())->name());
  }

  virtual char *name() { return "type"; }

  virtual void save_object(i4_saver_class *fp, li_object *o,
                           li_environment *env)
  {
    li_save_type(fp, li_type::get(o,env)->value());
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap,
                                 li_environment *env)
  {
    int new_type=li_load_type(fp, type_remap);
    if (new_type)
      return new li_type(new_type);
    else
      return 0;
  }

};



class li_float_type_function : public li_type_function_table
{
  virtual int equal(li_object  *o1, li_object *o2)  
  { return li_float::get(o1,0)->value()==li_float::get(o2,0)->value(); }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    char buf[200], dec=0;
    sprintf(buf, "%f", li_float::get(o,0)->value());
    
    for (char *c=buf; *c; c++)
      if (*c=='.') dec=1;
    
    if (dec)
    {
      while (buf[strlen(buf)-1]=='0')
        buf[strlen(buf)-1]=0;
    
      if (buf[strlen(buf)-1]=='.')
        buf[strlen(buf)-1]=0;
    }


    stream->write(buf,strlen(buf));
  }

  virtual char *name() { return "float"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    fp->write_float(li_float::get(o,env)->value());
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap,
                                 li_environment *env)
  {
    return new li_float(fp->read_float());
  }

};


class li_character_type_function : public li_type_function_table
{
  virtual int equal(li_object  *o1, li_object *o2)  
  { 
    return li_character::get(o1,0)->value()==li_character::get(o2,0)->value(); 
  }

  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    stream->printf("#%c",li_character::get(o,0)->value());
  }

  virtual char *name() { return "character"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    fp->write_16(li_character::get(o,env)->value());
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap,
                                 li_environment *env)
  {
    return new li_character(fp->read_16());
  }

};



class li_list_type_function : public li_type_function_table
{
  virtual void mark(li_object   *o, int set) 
  { 
    if (o->is_marked() && set)
      return ;

    li_list *l=(li_list *)o;
    if (l->data())
    {
      for (li_list *p=l; p;)
      {
        p->mark(set);
        if (p->data())
        {
          if (set!=p->data()->is_marked())
            li_get_type(p->data()->unmarked_type())->mark(p->data(), set);

          if (p->next() && (set!=p->next()->is_marked()))
          {
            if (p->next()->unmarked_type()==LI_LIST)
              p=(li_list *)p->next();
            else
            {
              li_get_type(p->next()->unmarked_type())->mark(p->next(), set);
              p=0;
            }
          } else p=0;
        }
        else p=0;

      }
    }
  }
  
  virtual void free(li_object   *o) 
  { 
    li_list *l=(li_list *)o;
    l->cleanup();

  }

  virtual int equal(li_object  *o1, li_object *o2)  
  { 
    if (o1==o2) return 1; 
    li_list *p1=li_list::get(o1,0), *p2=li_list::get(o2,0);

    for (;p1;)
    {
      if (!o2) return 0;

      if (p1->data()->type() != p2->data()->type()) return 0;

      if (li_get_type(p1->data()->type())->equal(p1->data(), p2->data())==0)  return 0;
       
      if (p1->next()->type()==LI_LIST)
      {
        if (p2->next()->type()!=LI_LIST)   return 0;
        p1=(li_list *)p1->next();
        p2=(li_list *)p2->next();
      }
      else if (p1->next()->type()!=p2->next()->type()) return 0;
      else return li_get_type(p1->next()->type())->equal(p1->next(), p2->next());
    }

    if (!p2) return 1;
    else return 0;
  }


  virtual void print(li_object  *o, i4_file_class *stream)   
  { 
    stream->write_8('(');
    li_list *p=li_list::get(o,0);
    o->mark(1);          // mark to prevent recursive prints

    for (; p; )
    {  
      li_get_type(p->data()->type())->print(p->data(), stream);

      if (p->next())
      {
        if (p->next()->type()!=LI_LIST)
        {
          stream->write(" . ",3);
          li_get_type(p->next()->type())->print(p->next(), stream);
          p=0;
        }
        else
        {
          p=(li_list *)p->next();
          stream->write_8(' ');
        }
      }
      else p=0;
    }

    o->mark(0);

    stream->write_8(')');
  }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    int t=0;
    int last_is_cons=0;
    li_list *l;
    for (l=li_list::get(o,env); l;)
    {
      t++;
      if (t>2000000)
        li_error(env, "list is really big : trying to save a circular structure doesn't work");

      li_object *next=l->next();
      if (next)
      {
        if (next->type()!=LI_LIST)
        {
          l=0;
          last_is_cons=0;
        }
        else l=(li_list *)next;
      }
      else l=0;
    }


    fp->write_32(t);

    if (last_is_cons)
      fp->write_8(1);
    else
      fp->write_8(0);

    for (l=li_list::get(o, env); l;)
    {
      li_object *data=l->data();

      li_save_object(fp, data, env);

      li_object *next=l->next();
      if (next)
      {
        if (next->type()==LI_LIST)
          l=(li_list *)next;
        else
        {
          li_save_object(fp, next, env);
          l=0;
        }
      } else l=0;
    }
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap,
                                 li_environment *env)
  {
    int t=fp->read_32();
    int last_is_cons=fp->read_8();
    li_list *last=0, *first=0;

    for (int i=0; iset_next(l);
      last=l;
    }

    if (last_is_cons)
      last->set_next(li_load_object(fp,type_remap,env));

    return first;
  }


  virtual char *name() { return "list"; }
};





class li_function_type_function : public li_type_function_table
{
  virtual void print(li_object  *o, i4_file_class *stream)
  { 
    stream->printf("#(compiled function @ 0x%x)", (long)(li_function::get(o,0)->value()));
  }

  virtual char *name() { return "function"; }

  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env)
  {
    fp->write_16(li_type::get(o,env)->value());
  }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap, li_environment *env)
  {
    int t=type_remap[fp->read_16()];
    if (t)
      return new li_type(t);
    else
      return 0;
  }

};


li_symbol *&li_environment::current_function()
{
  return data->current_function;
}

li_object *&li_environment::current_arguments()
{
  return data->current_args;
}

void li_environment::print_call_stack(i4_file_class *fp)
{
  li_symbol *s=current_function();
  li_object *o=current_arguments();

  if (s && o)
    li_printf(fp, "%O %O", s,o);
  else if (s)
    li_printf(fp, "%O %O", s);

  if (data->next)
    data->next->print_call_stack(fp);
}


li_object *li_environment::value(li_symbol *s)
{
  for (value_data *p=data->value_list; p; p=p->next)
    if (p->symbol==s)
      return p->value;

  if (data->next)
    return data->next->value(s);

  return s->value();
}


li_object *li_environment::fun(li_symbol *s)
{
  for (fun_data *p=data->fun_list; p; p=p->next)
    if (p->symbol==s)
      return p->fun;

  if (data->next)
    return data->next->value(s);

  return s->fun();
}

void li_environment::set_value(li_symbol *s, li_object *value)
{
  if (data->local_namespace)
  {
    for (value_data *p=data->value_list; p; p=p->next)
      if (p->symbol==s)
        p->value=value;
  
    value_data *v=new value_data;
    v->symbol=s;
    v->value=value;
    v->next=data->value_list;
    data->value_list=v;
  }
  else if (data->next)
    data->next->set_value(s,value);
  else
    s->set_value(value);
}


void li_environment::set_fun(li_symbol *s, li_object *fun)
{
  if (data->local_namespace)
  {
    for (fun_data *p=data->fun_list; p; p=p->next)
      if (p->symbol==s)
        p->fun=fun;
  
    fun_data *f=new fun_data;
    f->symbol=s;
    f->fun=fun;
    f->next=data->fun_list;
    data->fun_list=f;
  }
  else if (data->next)
    data->next->set_fun(s, fun);
  else 
    s->set_fun(fun);
}


void li_environment::mark(int set)
{
  li_object::mark(set);

  for (value_data *v=data->value_list; v; v=v->next)
    if (set!=v->value->is_marked())
      li_get_type(v->value->unmarked_type())->mark(v->value,set);

  for (fun_data *f=data->fun_list; f; f=f->next)
    if (set!=f->fun->is_marked())
      li_get_type(f->fun->unmarked_type())->mark(f->fun,set);

  if (data->next && data->next->is_marked()!=set)
    li_get_type(LI_ENVIROMENT)->mark(data->next, set);
}

void li_environment::free()
{
  for (value_data *v=data->value_list; v; )
  {   
    value_data *last=v;
    v=v->next;
    delete last;
  }

  for (fun_data *f=data->fun_list; f; )
  {   
    fun_data *last=f;
    f=f->next;
    delete last;
  }

  delete data;
}

void li_environment::print(i4_file_class *s)
{
  s->printf("#env-(syms=");

  for (value_data *v=data->value_list; v; v=v->next)
  {
    s->write_8('(');
    li_get_type(v->symbol->type())->print(v->symbol, s);
    s->write_8(' ');
    li_get_type(v->value->type())->print(v->value,  s);    
    s->write_8(')');
  }

  s->printf("funs=");
  for (fun_data *f=data->fun_list; f; f=f->next)
  {
    s->write_8('(');
    li_get_type(f->symbol->type())->print(f->symbol, s);
    s->write_8(' ');
    li_get_type(f->fun->type())->print(f->fun,  s);    
    s->write_8(')');
  }
  s->write_8(')');

}



class li_environment_type_function : public li_type_function_table
{
public:
  virtual void mark(li_object   *o, int set)   { ((li_environment *)o)->mark(set); }
  virtual void free(li_object   *o) {  li_environment::get(o,0)->free();  }
  virtual void print(li_object  *o, i4_file_class *s) { li_environment::get(o,0)->print(s); }
  virtual char *name() { return "environment"; }

  
  virtual void save_object(i4_saver_class *fp, li_object *o, li_environment *env) 
  { li_error(env, "cannot be saved"); }

  virtual li_object *load_object(i4_loader_class *fp, li_type_number *type_remap, li_environment *env) 
  { 
    li_error(env, "cannot be loaded"); 
    return 0;
  }

};



class li_type_manager_class : public i4_init_class
{

public: 
  i4_array table;

  int add(li_type_function_table *type_functions,
          li_environment *env=0,
          int anon=0)

  {
    li_type_number old_type=0, new_type=table.size();    

    if (!anon)
    {
      li_symbol *sym=li_get_symbol(type_functions->name());
      if (sym->value() && sym->value()->type()==LI_TYPE)  
      {
        old_type=li_type::get(sym->value(), env)->value();
        i4_warning("attempt to reassign type %s ignored", type_functions->name());
        delete type_functions;
        return old_type;
      }
      
      li_set_value(sym, new li_type(new_type), env);
    }

    table.add(type_functions);

    
    return new_type;
  }

  li_type_manager_class() : table(0,32) {}

  void remove(int type_num)
  {
    delete table[type_num];
    table[type_num]=0;
  }

  li_type_function_table *get(int num)
  {
    return table[num];
  }

  int init_type() { return I4_INIT_TYPE_LISP_BASE_TYPES; }
  void init()
  {
    li_invalid_type_function *invalid=new li_invalid_type_function;  
    for (int i=0; iname(), name)==0)
        return i;

    return 0;
  }

};

static li_type_manager_class li_type_man;

int li_add_type(li_type_function_table *type_functions,   // return type number for type
                li_environment *env,
                int anon)

{
  return li_type_man.add(type_functions, env, anon);
}

void li_remove_type(int type_num)
{
  li_type_man.remove(type_num);
}

void li_cleanup_types()
{
  li_type_man.table.uninit();
}

li_type_function_table *li_get_type(li_type_number type_num)
{
  return li_type_man.get(type_num);
}



li_type_number li_find_type(char *name, li_environment *env)
{
  li_symbol *s=li_find_symbol(name);
  if (s)
    return li_type::get(li_get_value(s, env),env)->value();
  else
    return 0;
}

li_type_number li_find_type(char *name, li_environment *env, li_type_number &cache_to)
{
  if (cache_to)
    return cache_to;
  else
  {
    cache_to=li_type::get(li_get_value(li_get_symbol(name), env), env)->value();
    return cache_to;
  }
}



i4_bool li_valid_type(li_type_number type_number)
{
  return type_number>=0 && type_number