lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include "efuns.h"

#include "array.h"
#include "call_out.h"
#include "dynamic_buffer.h"
#include "main.h"
#include "simulate.h"

call_out **pending_calls=NULL; /* pointer to first busy pointer */
int num_pending_calls; /* no of busy pointers in buffer */
static call_out **call_buffer=NULL; /* pointer to buffer */
static int call_buffer_size; /* no of pointers in buffer */

extern int current_time;

static void verify_call_outs()
{
#ifdef DEBUG
  struct vector *v;
  int e;
  if(call_buffer) return;

  if(num_pending_calls<0 || num_pending_calls>call_buffer_size)
    fatal("Error in call out tables.\n");

  if(pending_calls+num_pending_calls!=call_buffer+call_buffer_size)
    fatal("Error in call out tables.\n");

  for(e=0;e<num_pending_calls;e++)
  {
    if(e)
    {
      if(pending_calls[e-1]>pending_calls[e])
	fatal("Error in call out order.\n");
    }
    
    if(!(v=pending_calls[e]->args))
      fatal("No arguments to call\n");

    if(v->ref!=1)
      fatal("Array should exactly have one reference.\n");

    if(v->malloced_size<v->size)
      fatal("Impossible array.\n");
  }
#endif
}



/* start a new call out, return 1 for success */
static int new_call_out(int num_arg,struct svalue *argp)
{
  int e,c;
  call_out *new,**p,**pos;
  struct vector *v;

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(!call_buffer)
  {
    call_buffer_size=20;
    call_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size);
    if(!call_buffer) return 0;
    pending_calls=call_buffer+call_buffer_size;
    num_pending_calls=0;
  }

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(num_pending_calls==call_buffer_size)
  {
    /* here we need to allocate space for more pointers */
    call_out **new_buffer;

    new_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size*2);
    if(!new_buffer)
      return 0;

    MEMCPY((char *)(new_buffer+call_buffer_size),
	   (char *)call_buffer,
	   sizeof(call_out *)*call_buffer_size);
    free((char *)call_buffer);
    call_buffer=new_buffer;
    pending_calls=call_buffer+call_buffer_size;
    call_buffer_size*=2;
  }

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

  /* time to allocate a new call_out struct */
  v=allocate_array(num_arg-1);
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(!v) return 0;
  new=(call_out *)xalloc(sizeof(call_out));
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(!new)
  {
    free_vector(v);
    return 0;
  }
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  new->time=current_time+argp[1].u.number;
  if(new->time<=current_time) new->time=current_time+1;
  new->command_giver=command_giver;
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(command_giver) add_ref(command_giver, "call_out (command_giver)");
  new->args=v;

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  move_svalue(v->item,argp);
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  for(e=1;e<num_arg-1;e++)  move_svalue(v->item+e,argp+e+1);

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  /* time to link it into the buffer using binsearch */
  pos=pending_calls;
  if(new->time>current_time+1) /* do we need to search where ?*/
  {
    e=num_pending_calls;
    while(e>0)
    {
      c=e/2;
      if(new->time>pos[c]->time)
      {
	pos+=c+1;
	e-=c+1;
      }else{
	e=c;
      }
    }
  }
  pos--;
  pending_calls--;
  for(p=pending_calls;p<pos;p++) p[0]=p[1];
  *pos=new;
  num_pending_calls++;
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  return 1;
}

void f_call_out(int args,struct svalue *argp)
{
  verify_call_outs();
  if (!(current_object->flags & O_DESTRUCTED))
  {
    if(argp[0].u.ob!=current_object)
    {
      error("Call out only permitted to this_object()\n");
    }
    new_call_out(args,argp);
  }
  verify_call_outs();
  pop_n_elems(args);
}

void do_call_outs()
{
  call_out *c;
  int e,args;
  verify_call_outs();
  while(num_pending_calls && pending_calls[0]->time<=current_time)
  {
    /* unlink call out */
    c=pending_calls[0];
    pending_calls++;
    num_pending_calls--;
    
    if((command_giver=c->command_giver))
      free_object(command_giver, "call_out (command_giver)");

    args=c->args->size;
    for(e=0;e<args;e++) push_svalue(c->args->item+e);
    free_vector(c->args);
    free((char *)c);
    f_call_function(args,sp-args+1);
    pop_stack();
    verify_call_outs();
  }
  command_giver=0;
}

static int find_call_out(struct svalue *fun)
{
  int e;
  for(e=0;e<num_pending_calls;e++)
  {
    if(pending_calls[e]->args->item[0].u.ob==fun->u.ob &&
       pending_calls[e]->args->item[0].type==fun->type &&
       pending_calls[e]->args->item[0].subtype==fun->subtype)
      return e;
  }
  return -1;
}

void f_find_call_out(int argc,struct svalue *argp)
{
  int e;
  verify_call_outs();
  e=find_call_out(argp);
  if(e==-1)
  {
    free_svalue(sp);
    SET_TO_UNDEFINED(*sp);
  }else{
    pop_stack();
    push_number(pending_calls[e]->time-current_time);
  }
    verify_call_outs();
}

void f_remove_call_out(int args,struct svalue *argp)
{
  int e;
  verify_call_outs();
  e=find_call_out(argp);
  if(e!=-1)
  {
    pop_stack();
    push_number(pending_calls[e]->time-current_time);
    free_vector(pending_calls[e]->args);
    if(pending_calls[e]->command_giver)
      free_object(pending_calls[e]->command_giver,
		  "remove_call_out (command_giver)");
    free((char*)(pending_calls[e]));
    for(;e>0;e--)
      pending_calls[e]=pending_calls[e-1];
    pending_calls++;
    num_pending_calls--;

  }else{
    free_svalue(sp);
    SET_TO_UNDEFINED(*sp);
  }
  verify_call_outs();
}

char *print_call_out_usage(int verbose)
{
  int i,svalues,len;
  char b[100];

  verify_call_outs();
  init_buf();
  for (len=svalues=i=0; i<num_pending_calls; i++)
  {
    len+=pending_calls[i]->time-current_time;
    svalues+=pending_calls[i]->args->size;
  }

  if(num_pending_calls)
    len/=num_pending_calls;

  if (verbose)
  {
    my_strcat("\nCall out information:\n");
    my_strcat("---------------------\n");
    sprintf(b,"Number of allocated call outs: %8d, %8d bytes\n",
	    num_pending_calls,
	    (int)(num_pending_calls *
	    (sizeof(call_out) + sizeof(struct vector) - sizeof(struct svalue))+
	    svalues*sizeof(struct svalue) ));
    my_strcat(b);
    sprintf(b,"Average length: %d\n", len);
    my_strcat(b);
  } else {
    sprintf(b,"call out:\t\t\t%8d %8d (current length %d)\n",
	    num_pending_calls,
	    (int)(num_pending_calls *
	    (sizeof(call_out) + sizeof(struct vector) - sizeof(struct svalue))+
	    svalues*sizeof(struct svalue)),
	    len );
    my_strcat(b);
  }
  return free_buf();
}

/* return a vector containing info about all call outs:
 * ({  ({ delay, command_giver, function, args, ... }), ... })
 */
struct vector *get_all_call_outs()
{
  int e,d;
  struct vector *ret;

  verify_call_outs();
  ret=allocate_array(num_pending_calls);
  for(e=0;e<num_pending_calls;e++)
  {
    struct vector *v;
    v=allocate_array(pending_calls[e]->args->size+2);
    v->item[0].u.number=pending_calls[e]->time-current_time;

    if(pending_calls[e]->command_giver)
    {
      add_ref(pending_calls[e]->command_giver,"get_all_call_outs");
      v->item[1].u.ob=pending_calls[e]->command_giver;
      v->item[1].type=T_OBJECT;
    }

    for(d=0;d<pending_calls[e]->args->size;d++)
      assign_svalue(v->item+d+2,pending_calls[e]->args->item+d);
    ret->item[e].u.vec=v;
    ret->item[e].type=T_POINTER;
  }
  return ret;
}

void f_call_out_info(int num_arg,struct svalue *argp)
{
  push_vector(get_all_call_outs());
  sp->u.vec->ref--;		/* Was set to 1 at allocation */
}

void free_all_call_outs()
{
  int e;
  verify_call_outs();
  for(e=0;e<num_pending_calls;e++)
  {
    free_vector(pending_calls[e]->args);
    if(pending_calls[e]->command_giver)
      free_object(pending_calls[e]->command_giver,
		  "free_all_call_outs (command_giver)");
    free((char*)(pending_calls[e]));
  }
  if(call_buffer) free((char*)call_buffer);
  num_pending_calls=0;
  call_buffer=NULL;
  pending_calls=NULL;
}