#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <yafl_usr.h>
#include <yafl_rnt.h>
#include "dbx_rnt.h"
#include "dbx_modu.h"
#include "dbx_type.h"
#include "dbx_clas.h"
#include "dbx_meth.h"
#include "dbx_call.h"
#include "dbx_matc.h"

#define MAX_CALLS 512
static dbx_call call_table[MAX_CALLS+1];
/* caution : there is a dummy element in position 0 of the array or else */
/* the program will crash at the end when accessing the inexistant method */
/* to restore yafl_step_mode and at the beginning too when writing back */
/* dbx_line_nr ??? */
/* this element contains a pointer to a dummy method */

static unsigned used_calls = 0;                               
int dbx_line_nr ;				 			         
extern int yafl_step_mode; 
int dbx_mode_run = 0;


#ifdef XDEBUG

static void dump_calls YPARAMS0
{
  int i;
  dbx_call *p;
  printf("dbx_call\tline\n\n");
  for (i = 1;i <= used_calls;i++)
  {
    p = &call_table[i];
    printf("%s\t%s\n",p->method->name,p->line);
  }
}

#endif

/* it useful to know the depth of the call stack for the variants */
/* of the trace command */
unsigned call_depth YPARAMS0
{
  return (used_calls);
}


dbx_call *first_call YPARAMS0
{
  if (used_calls > 0)
    return (&call_table[1]);
  else
    return NULL;
}

dbx_call *last_call YPARAMS0
{
  if (used_calls > 0)
    return (&call_table[used_calls]);
  else
    return NULL;
}

dbx_call *next_call YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  if (pc == &call_table[used_calls])
    return NULL;
  else
    return (pc + 1);
}

dbx_call *prev_call YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  if (pc == &call_table[1])
    return NULL;
  else
    return (pc - 1);
}

dbx_method *call_method YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  return (pc->method);
}


char *call_name YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  return (method_name(call_method(pc)));
}

dbx_class *call_class YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  return (method_class(call_method(pc)));
}

dbx_module *call_module YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  return (class_module(call_class(pc)));
}

unsigned call_line YPARAMS2(dbx_call *,  pc)
{
  assertp(pc);
  return (pc->line);
}

/* search in the call list on method name */
dbx_call *find_next_call YPARAMS4(dbx_call *,  pc,
                            char *,      pattern)
{
  dbx_call *pcal = NULL;
  while (pc)
  {
    if (dbx_match(method_name(call_method(pc)),pattern))
    {
      pcal = pc;
      pc = NULL;
    }
    else
    {
      pc = next_call(pc); 
    }
  }
  return pcal;
}
dbx_call *find_prev_call YPARAMS4(dbx_call *,  pc,
                            char *,      pattern)
{
  dbx_call *pcal = NULL;
  while (pc)
  {
    if (dbx_match(method_name(call_method(pc)),pattern))
    {
      pcal = pc;
      pc = NULL;
    }
    else
    {
      pc = prev_call(pc); 
    }
  }
  return pcal;
}

/* search in the call list on method */
dbx_call *search_call YPARAMS4(dbx_call *,  pc,
                              dbx_method *,      pm)
{
  dbx_call *pcal = NULL;
  while (pc)
  {
    if (call_method(pc) == pm)
    {
      pcal = pc;
      pc = NULL;
    }
    else
    {
/* going backward */    
      pc = prev_call(pc); 
    }
  }
  return pcal;
}


void dbx_push_call YPARAMS6(dbx_method *,  pm,
                            VOID *,        context,
		            obj_ptr *,       this)
{
  dbx_call *p;
  int i;
  if (pm)
  {
/* the case where the pointer to the context is NULL is now acceptable */
/* in the case of a method that contains INLINE statements */     
/*  assert(context); */
    assertp(this);
    if(used_calls >= MAX_CALLS)
      {
        printf ("Call stack overflow...%d\n", used_calls);
        exit(997);
      }
    call_table[used_calls].line = dbx_line_nr;
    used_calls++;
    p = &call_table[used_calls];
    dbx_line_nr = method_beginline(pm);
    p->method = pm;
    p->context = context;
    p->this = this;
    p->used_v_stack = used_v_stack();
    p->used_d_stack = used_d_stack();
/* if in running mode yafl_step_mode should be updated at each call of */
/* a debugged method to force the program to enter the monitor if one or */
/* more breakpoints are present in the method (the monitor will then do */
/* additionnal checks to ensure that a breakpoint has really been hit) */
/* if in other modes yafl_step_mode should be left unchanged at the state */
/* it was set,ie 1 for trace modes and 0 for end mode */
    if (dbx_mode_run)
      yafl_step_mode = bp_count(pm); 
  }
  else
  {
    writeline("WARNING: NULL method pointer");
/*
    *((int*)pm) = 0;
*/    
  }
}

void dbx_pop_call YPARAMS0
{
  dbx_method *pm;
  if (used_calls > 0)
    {
      used_calls--;
/* it is necessary to restore dbx_line_nr when leaving a method because */  
/* it is possible that another method will be called immediatly after */
/* without having executed a SET_LINE,if the first method is in the */
/* parameters of the second */
      dbx_line_nr = call_table[used_calls].line;
/* when leaving a method yafl_step_mode should be updated in the same */
/* way as when entering to reflect the status of the new current method */  
      if (dbx_mode_run)
      {
        pm = call_method(&call_table[used_calls]);
        if (pm)
          yafl_step_mode = bp_count(pm);
      }
    }
}


/* a dummy method is allocated and assigned to the pointer in the element */
/* zero of the call table to prevent crashing at the end of the program */
/* when this element is accessed to restore checkstep */
void init_dbx_call YPARAMS0
{
  call_table[0].method = new_method();
}

void term_dbx_call YPARAMS0
{
}
