#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <yafl_usr.h>
#include <yafl_rnt.h>
#include "dbx_ctrl.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_brea.h"
#include "dbx_comm.h"
#include "dbx_out.h"

/* this module allows the user to control the advance mode of the debugged */
/* program and executes the actions related to breakpoint triggering */


static char *stopped_table[] = {"breakpoint hit",
                                "step",
                                "move",
                                "leave",
                                "trace"};
                               
static char *failed_table[] = {"error: assert failed",
                               "error: void method",
                               "error: void object",
                               "error: bad class",
                               "error: void array",
                               "error: incorrect for",
                               "error: index out of bounds",
                               "error: void what",
                               "error: bad what",
                               "error: bad case",
                               "error: dual stack overflow",
                               "error: out of memory",
                               "error: bad clone",
                               "error: bad array",
                               "error: bad version",
                               "error: bad array size",
                               "error: precondition failed",
                               "error: postcondition failed"};
                               
/* note that the FPE signal (error on arithmetic operation) is */
/* of course not an abnormal situation */
static char *crashed_table[] = {"error: arithmetic operation",
                                "??? illegal instruction",
                                "??? bus error",
                                "??? segmentation violation",
                                "??? abort"};
                                
static char *pressed_table[] = {"!!! interrupted",
                               "!!! stopped"};

char *ctrl_message YPARAMS2(unsigned,  code)
{
  if ((FIRST_STOPPED <= code) && (code <= LAST_STOPPED))
  {
    return stopped_table[code - FIRST_STOPPED];
  }
  else if ((FIRST_FAILED <= code) && (code <= LAST_FAILED))
  {
    return failed_table[code - FIRST_FAILED];
  }
  else if ((FIRST_CRASHED <= code) && (code <= LAST_CRASHED))
  {
    return crashed_table[code - FIRST_CRASHED];
  }
  else if ((FIRST_PRESSED <= code) && (code <= LAST_PRESSED))
  {
    return pressed_table[code - FIRST_PRESSED];
  }
  else
    assert(0);
  return NULL;    
}
                                                              
/* yafl_step_mode is the global variable that is checked at each YAFL */
/* statement (in the code substituted for the SETLINE macro) to see */
/* if the program should enter the monitor function */
extern int yafl_step_mode;

/* dbx_mode_run is the variable that controls the operation of the */
/* push_call and pop_call functions; if this variable is set to one */
/* yafl_step_mode is updated each time the program enters or leaves a YAFL */
/* method to reflect the number of breakpoints in the current method */
/* if this number is greater than zero the monitor function will be entered */
/* to perform additionals checks; if dbx_mode_run is set to zero the */
/* variable yafl_step_mode will not be modified; a value of one then */
/* results in trace mode or a derivated mode (step, enter or leave) */
/* while a value of zero results in end mode */
extern int dbx_mode_run;

/* status is the variable indicating why the control was given to the user */
int dbx_status;

/* mode is the variable indicating the last execution command given */
extern int yafl_debug_mode;
static int status; /* ??? */

/* the current depth of the call stack is stored when a step,enter or leave */
/* command is issued; we can then compare this value with the new one to */
/* see if control should be given back to the user */
static int old_depth;

/* find if any breakpoint should be triggered and do the appropriate action */
int do_break YPARAMS0
{
  dbx_call *pc;
  dbx_breakpoint *pb;
  pc = last_call();
  pb = find_breakpoint(call_method(pc),call_line(pc));
  if (pb)
  {
    inc_counter(pb);
    if (breakpoint_evaluate(pb))
    {
      if (breakpoint_action(pb) == BP_BREAK)
      {
        dbx_status = XSTOPPED_BREAK;
        main_loop();
        return(1);
      }	
      else if (breakpoint_action(pb) == BP_LOG)
      {
        writeline(breakpoint_string(pb));
      }
    }
  }
  return(0);
}

/* this function is entered each time the yafl_step_mode variable is set */
/* to one;additional checks must then be performed to verify that the */
/* conditions for stopping program execution are satisfied */
void monitor YPARAMS0
{
/* if stack is empty we are currently executing class initialisation code */
/* force immediate return to prevent crashing of the debugger */ 
  if (call_depth() == 0)
    return;
/* updates the top of the call stack so that it contains the correct */
/* value for the current line number */
  last_call()->line = dbx_line_nr; /* caution */
  if (do_break())
    return;
  if (yafl_debug_mode == YAFL_DEBUG_RUN)
    return;
  else if (yafl_debug_mode == YAFL_DEBUG_STEP)
  {
    if (call_depth() == old_depth)
    {
      dbx_status = XSTOPPED_STEP;      
      main_loop();
    }
  }
  else if (yafl_debug_mode == YAFL_DEBUG_MOVE)
  {
    if (call_depth() != old_depth) /* enter or return in another method */
    {
      dbx_status = XSTOPPED_MOVE;
      main_loop();
    }
  }
  else if (yafl_debug_mode == YAFL_DEBUG_LEAVE)
  {
    if (call_depth() < old_depth)
    {
      dbx_status = XSTOPPED_LEAVE;
      main_loop();
    }
  }
  else if (yafl_debug_mode == YAFL_DEBUG_TRACE)
  {
    dbx_status = XSTOPPED_TRACE; 
    main_loop();
  }
}

/* ensure all the informations are correctly set and give the control to */
/* the user */
void crash YPARAMS2(int,  code)
{ 
  if (get_module_count() > 0)
  {
/* the debugger stack could be empty at the time of the crash */
    if (last_call())
      last_call()->line = dbx_line_nr;
    dbx_status = code;
    main_loop();
  }
}


/* run the debugged program until a breakpoint is triggered or an error */
/* is catched */
void mode_run YPARAMS0
{
  dbx_mode_run = 1;
  yafl_step_mode = bp_count(call_method(last_call()));
  yafl_debug_mode = YAFL_DEBUG_RUN;
}

/* run the debugged program until an error is catched, ignoring all */
/* breakpoints set */
void mode_end YPARAMS0
{
  dbx_mode_run = 0;
  yafl_step_mode = 0;
  yafl_debug_mode = YAFL_DEBUG_END;
}

/* run the debugged program,skipping over method invocations (ie, until */
/* we execute a statement at the same call depth than the old one) */
void mode_step YPARAMS0
{
  dbx_mode_run = 0;
  yafl_step_mode = 1;
  yafl_debug_mode = YAFL_DEBUG_STEP;
  old_depth = call_depth();
}

/* run the debugged program,stopping unconditionnaly at each instruction */
void mode_trace YPARAMS0
{
  dbx_mode_run = 0;
  yafl_step_mode = 1;
  yafl_debug_mode = YAFL_DEBUG_TRACE;
}

/* run the debugged program,stopping after each method call or return */
/* (ie,until the current call depth differs from the old one) */
void mode_move YPARAMS0
{
  dbx_mode_run = 0;
  yafl_step_mode = 1;
  yafl_debug_mode = YAFL_DEBUG_MOVE;
  old_depth = call_depth();
}

/* run the debugged program,until returning from the current method */
/* (ie,until the current call depth is less than the old one) */
void mode_leave YPARAMS0
{
  dbx_mode_run = 0;
  yafl_step_mode = 1;
  yafl_debug_mode = YAFL_DEBUG_LEAVE;
  old_depth = call_depth();
}

void init_dbx_ctrl YPARAMS0
{               
}

void term_dbx_ctrl YPARAMS0
{
}
