#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <yafl_rnt.h>
#include "dbx_rnt.h"
#include "dbx_modu.h"
#include "dbx_type.h"
#include "dbx_clas.h"
#include "dbx_obar.h"
#include "dbx_meth.h"
#include "dbx_ctrl.h"
#include "dbx_file.h"
#include "dbx_type.h"
#include "dbx_vari.h"
#include "dbx_fiel.h"
#include "dbx_call.h"
#include "dbx_loca.h"
#include "dbx_list.h"
#include "dbx_fram.h"
#include "dbx_ctxt.h"
#include "dbx_curr.h"
#include "dbx_brea.h"
#include "dbx_watc.h"
#include "dbx_out.h"
#include "dbx_lssw.h"
#include "dbx_stri.h"

static char buffer[100];             

/* maximum number of characters when printing values */
#define MAX_CHARS (3*39)
/* character used to replace non-printable characters in printed strings */
#define WILDCARD '&'

static char *result;
static int full;

/* this function is machine-dependant */
static int is_printable YPARAMS2(char, c)
{
  return (c >= 32);
}

/* append the given string to the result string but ensuring that the */
/* result is not longer that the maximum acceptable length */
static void append YPARAMS2(char *,  s)
{
  char *r,*q;
  unsigned l1,l2;
  if (!full)
  {
    l1 = strlen(result);  
    l2 = strlen(s);
    if (l1+l2 > MAX_CHARS)
    {
      full = 1;
      q = strleft(s,MAX_CHARS-l1);
      r = strjoin(result,q);
      free(q);
      free(result);
      result = r;
    }
    else
    {
      r = strjoin(result,s);
      free(result);
      result = r;
    }
  }
}

/* a YAFL string is enclosed by double quotes */
static void putystring YPARAMS2(char *,  s)
{
  if (s)
  {
    append("\"");
    append(s);
    append("\"");
  }
  else
  {
    append("VOID");
  }
}


static void putyarray YPARAMS2(obj_ptr,  p)
{
  unsigned i;
  if (p)
  {
    append("{");
    if (array_size(p) > 0)
    {
/* recursive call to the printing function */    
      putyvalue(array_type(p),array_level(p)-1,array_element(p,0));
      if (array_size(p) > 1)
      {
        i = 1;
        while ((i < array_size(p))&&(!full))
        {
          append(",");
/* recursive call to the printing function */          
          putyvalue(array_type(p),array_level(p)-1,array_element(p,i));
          i++;
        }
      }
    }      
    append("}");
  }
  else
  {
    append("VOID");
  }
} 

static void putyref YPARAMS2(obj_ptr,  p)
{
  if (p)
  {
    append("--->");
  }
  else
  {
    append("VOID");
  }
}

static void putyreal YPARAMS2(double,  d)                         
{
  sprintf(buffer,"%.30f",d);
  append(buffer);
}

static void putyint YPARAMS2(yint,  i)
{
  sprintf(buffer,"%d",i);
  append(buffer);
}

/* a YAFL character is enclosed by single quotes */
static void putychar YPARAMS2(char,  c)
{
  if (is_printable(c))
  {
    append("'");
    sprintf(buffer,"%c",c);
    append(buffer);
    append("'");
  }
  else
  {
    append("chr");
    append("(");
    sprintf(buffer,"%d",c);
    append(buffer);
    append(")");
  }
}

static void putybool YPARAMS2(int,  b)
{
  if (b)
    append("TRUE");
  else
    append("FALSE");
}

static void putyvalue YPARAMS6(dbx_type *,  pt,
                         unsigned,    level,
                  			 VOID *,      adress)
{
  assertp(adress);
  if (pt)
  {
    if (level > 0)
    {
/* strings in YAFL are arrays of characters */
      if (is_char(pt)&&(level == 1))
      {
        putystring(*(char **)adress);
      }
      else
      {
        putyarray(*(obj_ptr *)adress);
      }
    }
    else
    {
      if (is_class(pt))
        putyref(*(obj_ptr *)adress);
      else
      {
        if (is_real(pt))
          putyreal(*(double *)adress);
        else if (is_int(pt))
          putyint(*(YINT *)adress);
        else if (is_char(pt))
          putychar(*(char *)adress);
        else if (is_bool(pt))
          putybool(*(int *)adress);
        else if (is_virtual(pt))
          putyref(*(obj_ptr *)adress);
        else
          assert(0);
      }  
    }
  }
  else
  {
/*    this should not happen anymore */
/*    putyref(*(obj_ptr *)adress); */
    assert(0);
  }
}

/* replace the non-printable characters in the result by a given character */
void edit_result YPARAMS2(char *, s)
{
  int n;
  int i;
  n = strlen(s);
  for (i = 0;i < n;i++)
  {
    if (!is_printable(s[i]))
      s[i] = WILDCARD;
  }
}  
  
/* write the textual representation of a YAFL value */
void writeyvalue YPARAMS6(dbx_type *,  pt,
                         unsigned,    level,
                         VOID *,  adress)
{
  result = strnew();
  full = 0;
  putyvalue(pt,level,adress);
  edit_result(result);
  writestring(result);
  free(result);
}
/*************************************************************/
void show_begin_current YPARAMS2(int, current)
{
  if (current)
    writestring(">");
  else
    writestring(" ");
}
/*************************************************************/
void show_end_current YPARAMS2(int, current)
{
  if (current)
    writestring("<");
  else
    writestring(" ");
}
/*************************************************************/                         			 
void show_field YPARAMS4(dbx_field *,  pf,
                        obj_ptr,      po)
{
  VOID *data;
  assertp(pf);
  writestring(field_name(pf));
  if (dbx_field_type(pf))
  {
    writestring(type_name(dbx_field_type(pf)));
  }
  else
  {
/* this should not happen anymore */
/*    writestring("-"); */
    assert(0);
  }
  writeint(field_level(pf));
  writeyn(once_field(pf));
  if (po)
  {
    data = get_fdata(pf,po);
    writeyvalue(dbx_field_type(pf),field_level(pf),data);
  }
  else
  {
    writestring("-");
  }
}
/************************************************/
void list_fields YPARAMS8(dbx_class *,  cur_class,
                         dbx_field *,  cur_field,
                         obj_ptr,  cur_object,
				         char *,  pattern)
{
  dbx_field *pf;
  int current;
  assertp(cur_class);
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(32);
  set_column(0); /* hidden columns */
  set_column(0);
  set_column(0);
  set_column(38);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("type");
  writestring("level");
  writestring("once");
  writestring("value");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pf = find_next_field(first_field(cur_class),pattern);
  while (pf)
  {
    current = (pf == cur_field);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_field(pf,cur_object);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pf = find_next_field(next_field(pf),pattern);
  }         
  close_table();
}
/***********************************************/
void show_elem YPARAMS4(unsigned,  i,
                       obj_ptr,   pa)
{
  VOID *data;
  assertp(pa);
  assert(i<array_size(pa));
  writeint(i);
  data = array_element(pa,i);
  writeyvalue(array_type(pa),array_level(pa)-1,data);
}
/***********************************************/
void list_elems YPARAMS8(obj_ptr,  cur_array,
                        int,  first_subscript,
                        int,  last_subscript,
                        int,      cur_subscript)
{
  int i;
  int current;
  assertp(cur_array);
  open_table();
  set_column(1);
  set_column(19);
  set_column(56);
  set_column(1);
  compute_table();
  writestring("");
  writestring("subscript");
  writestring("value");
  writestring("");
  writeln();     
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  for (i = first_subscript;i <= last_subscript;i++) 
  {
    current = (i == cur_subscript);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_elem(i,cur_array);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
  }  
  close_table();
}
/***********************************************/
void show_local YPARAMS4(dbx_local *,  pl,
                        dbx_call *,    pc) /* because we need both
         			                          this and context */
{
  VOID *data;
  assertp(pl);
  writestring(local_name(pl));
  if (local_type(pl))
  {
    writestring(type_name(local_type(pl)));
  }
  else
  {
/* this should not happen anymore */  
/*    writestring("-"); */
    assert(0);
  }
  writeint(local_level(pl));
  writeyn(once_local(pl));
  if (pc)
  {
    data = get_ldata(pl,pc);
    writeyvalue(local_type(pl),local_level(pl),data);
  }
  else
  {
    writestring("-");
  }
}
/************************************************/
void list_locals YPARAMS8(dbx_method *,  cur_method,
                         dbx_local *,   cur_local,
                         dbx_call *,  cur_call, /*because we need
				                                  both this and context */
                         char *,  pattern)
{
  dbx_local *pl;
  int current;
  assertp(cur_method);
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(37);
  set_column(0); /* hidden columns */
  set_column(0);
  set_column(0);
  set_column(38);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("type");
  writestring("level");
  writestring("once");
  writestring("value");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pl = find_next_local(first_local(cur_method),pattern);
  while (pl)
  {
    current = (pl == cur_local);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_local(pl,cur_call);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pl = find_next_local(next_local(pl),pattern);
  }
  close_table();
}
/************************************************/
void show_method YPARAMS2(dbx_method *,  pm)
{
  assertp(pm);
  writestring(method_name(pm));
}
/************************************************/
void list_methods YPARAMS6(dbx_class *,  cur_class,
                          dbx_method *, cur_method,
                          char *,  pattern)
{
  dbx_method *pm;
  int current;
  assertp(cur_class);
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(75);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pm = find_next_method(first_method(cur_class),pattern);
  while (pm)
  {
    current = (pm == cur_method);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_method(pm);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pm = find_next_method(next_method(pm),pattern);
  }
  close_table();
}
/***********************************************/
void show_class YPARAMS2(dbx_class *,  pc)
{
  assertp(pc);
  writestring(class_name(pc));
  writeyn(once_class(pc));
}
/************************************************/
void list_classes YPARAMS6(dbx_module *,  cur_module,
                          dbx_class *,   cur_class,
                          char *,  pattern)
{
  dbx_class *pc;
  int current;
  assertp(cur_module);
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(37);
  set_column(38);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("once");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pc = find_next_class(first_class(cur_module),pattern);
  while (pc)
  {
    current = (pc == cur_class);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_class(pc);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pc = find_next_class(next_class(pc),pattern);
  }
  close_table();
}
/************************************************/
void show_module YPARAMS2(dbx_module *,  pm)
{
  assertp(pm);
  writestring(module_name(pm));
  if (pm->line_numbers)
    writestring(" ");
  else
    writestring("?");
  if (file_name(pm))
    writestring(file_name(pm));
  else
    writestring("{not found}");
}

/************************************************/
void list_modules YPARAMS4(dbx_module *,  cur_module,
                          char *,   pattern)
{
  dbx_module *pm;
  int current;
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(37);
  set_column(1);
  set_column(37);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("");
  writestring("file");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pm = find_next_module(first_module(),pattern);
  while (pm)
  {
    current = (pm == cur_module);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_module(pm);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pm = find_next_module(next_module(pm),pattern);
  }
  close_table();
}
/************************************************/
void show_call YPARAMS2(dbx_call *,  pc)
{
  char a[20];
  
  assertp(pc);
  stop_tab();
  sprintf (a, "[%d.%d] ", pc->used_v_stack, pc->used_d_stack);
  writestring (a);
  writestring(module_name(call_module(pc)));
  writestring(".");
  writestring(class_name(call_class(pc)));
  writestring(".");
  writestring(method_name(call_method(pc)));
  writestring(":");
  writeint(call_line(pc));
  restart_tab();
}
/************************************************/
void list_calls YPARAMS4(dbx_call *,  cur_call,
                        char *,  pattern)
{
  dbx_call *pc;
  int current;
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(75);
  set_column(1);
  compute_table();
  writestring("");
  writestring("statement");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pc = find_prev_call(last_call(),pattern);
  while (pc)
  {
    current = (pc == cur_call);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_call(pc);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pc = find_prev_call(prev_call(pc),pattern);
  }
  close_table();
}
/************************************************/
void show_line YPARAMS4(dbx_module *,  pm,
                       unsigned,  line_number)
{
  dbx_method *pme;
  int ok;
  assertp(pm);
  ok = is_statement(pm, line_number);
  pme = line_in_method(pm, line_number);
  writeint(line_number);
  if (search_breakpoint(pm,line_number))
    writestring("*");
  else if (pme)
  {
    if (ok || line_number == method_endline(pme))
      writestring(".");
    else
      writestring(" ");
  }
  else
    writestring(" ");
  if (file_name(pm))
    writestring(get_source_line(file_name(pm),line_number));
  else
    writestring("{not available}");
}
/************************************************/
void list_lines YPARAMS8(dbx_module *,  pm,
                        unsigned,  first_line,
			unsigned,  last_line,
			unsigned,  current_line)
{
  unsigned i;	
  int current;
/* les tabulations perturbent le calcul de longueur des lignes */
/* utiliser affichage libre ??? */
/*  
  open_table();
  set_column(1);
  set_column(3);
  set_column(1);
  set_column(71);
  set_column(1);
  compute_table();
*/
  for (i = first_line;i <= last_line;i++)
  {
    current = (i == current_line);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_line(pm,i);
    show_end_current(current);
    writeln();
    if (i == current_line)
      highlight_off();
  }
/*
  close_table();
*/
}
/************************************************/
void show_breakpoint YPARAMS2(dbx_breakpoint *,  pb)
{
  assertp(pb);
  stop_tab();  
  writestring(module_name(breakpoint_module(pb)));
  writestring(".");
  writestring(class_name(breakpoint_class(pb)));
  writestring(".");
  writestring(method_name(breakpoint_method(pb)));
  writestring(":");
  writeint(breakpoint_line(pb));
  restart_tab();  
  writeint(breakpoint_counter(pb));
  writeint(breakpoint_action(pb));
  writestring(breakpoint_string(pb));
}
/************************************************/
extern dbx_list *break_list; /* made visible by the module */
void list_breakpoints YPARAMS4(dbx_breakpoint *,  cur_breakpoint,
                              char *, pattern)
{
  dbx_elem *pe;
  dbx_breakpoint *pb;
  int i;
  int current;
  open_table();
  set_column(1);
  set_column(9);
  set_column(28);
  set_column(10);
  set_column(10);
  set_column(18);
  set_column(1);
  compute_table();
  writestring("");
  writestring("number");
  writestring("statement");
  writestring("counter");
  writestring("action");
  writestring("string");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pe = first_elem(break_list);
  i = 1;
  while (pe)
  {
    pb = elem_info(pe);
    current = (pb == cur_breakpoint);
    if (current)
      highlight_on();
    show_begin_current(current);
    writeint(i);
    show_breakpoint(pb);    
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pe = next_elem(pe);
    i++;
  }
  close_table();
}
/************************************************/
static void list_par YPARAMS2(dbx_class *,  pc)
{
  if (pc)
  {
    list_par(parent_class(pc));
    writestring(class_name(pc));
    writeln();
  }
}
/***************************************************/
void list_parentclasses YPARAMS2(dbx_class *,  pc)
{
  assertp(pc);
  writestring("name");
  writeln();
  writeln();
  list_par(pc);
}
/************************************************/
static void list_enc YPARAMS2(dbx_method *,  pm)
{
  if (pm)
  {
    list_enc(enclosing_method(pm));
    writestring(method_name(pm));
    writeln();
  }
}
/***************************************************/
void list_enclosingmethods YPARAMS2(dbx_method *,  pm)
{
  assertp(pm);
  writestring("name");
  writeln();
  writeln();
  list_enc(pm);
}
/***************************************************/
void show_watch YPARAMS2(dbx_watch *,  pw)
{
  dbx_variable *pv;
  assertp(pw);
  writestring(watch_expression(pw));
  pv = watch_value(pw);
  if (pv)
  {
    writeyvalue(variable_type(pv),variable_level(pv),variable_address(pv));
    dispose_variable(pv);
  }
  else
  {
    writestring("    -    ");
  }
}
/************************************************/
extern dbx_list *watch_list; /* made visible by the module */
void list_watches YPARAMS4(dbx_watch *,  cur_watch,
                          char *, pattern)
{
  dbx_elem *pe;
  dbx_watch *pw;
  int i;
  int current;
  open_table();
  set_column(1);
  set_column(9);
  set_column(30);
  set_column(36);
  set_column(1);
  compute_table();
  writestring("");
  writestring("number");
  writestring("expression");
  writestring("value");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pe = first_elem(watch_list);
  i = 1;
  while (pe)
  {
    pw = elem_info(pe);
    current = (pw == cur_watch);
    if (current)
      highlight_on();
    show_begin_current(current);
    writeint(i);
    show_watch(pw);    
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pe = next_elem(pe);
    i++;
  }
  close_table();
}

/******************************************/
void show_once YPARAMS2(dbx_class *,  pc)
{
  assertp(pc);
  writestring(class_name(pc));
}

/*******************************************************/
void list_onces YPARAMS4(dbx_class *,  cur_once,
                          char *,   pattern)
{
  dbx_class *pc;
  int current;
  assertp(pattern);
  open_table();
  set_column(1);
  set_column(75);
  set_column(1);
  compute_table();
  writestring("");
  writestring("name");
  writestring("");
  writeln();
  writestring("");
  writestring("");
  writestring("");
  writeln();
  pc = find_next_once(first_once(),pattern);
  while (pc)
  {
    current = (pc == cur_once);
    if (current)
      highlight_on();
    show_begin_current(current);
    show_once(pc);
    show_end_current(current);
    writeln();
    if (current)
      highlight_off();
    pc = find_next_once(next_once(pc),pattern);
  }
  close_table();
}

/****************************************************/
/* machine-dependant character codes */

#ifdef unix

#define VBARCHAR '|'
#define HBARCHAR '-'
#define RTEECHAR '+'
#define URCOINCHAR '+'

#else

#define VBARCHAR 179
#define HBARCHAR 196
#define RTEECHAR 195
#define URCOINCHAR 192

#endif

#define TAB_WIDTH 8

/*****************************************************************/

/* to store the nodes of the branch currently explored in the method tree */
#define MAX_LEVEL_METHODS 20
static dbx_method *method_table[MAX_LEVEL_METHODS];
static unsigned used_methods;

static void lmh YPARAMS2(dbx_method *,  pm)
{
  int i,j;
  dbx_method *pm2;
  if (used_methods > 0)
  {
    for (i = 0;i < used_methods-1;i++)
    {
      if (last_innermethod(method_table[i]) == method_table[i+1])
      {
        for (j = 1;j <= TAB_WIDTH;j++)
          writechar(' ');
      }
      else
      {
        writechar(VBARCHAR);
        for (j = 1;j < TAB_WIDTH;j++)
          writechar(' ');
      }
    }
    if (pm == last_innermethod(method_table[used_methods-1]))
      writechar(URCOINCHAR);
    else
      writechar(RTEECHAR);
    for (j = 1;j < TAB_WIDTH;j++)
      writechar(HBARCHAR);
  }
  assert(used_methods < MAX_LEVEL_METHODS);
  method_table[used_methods] = pm;
  used_methods++;
  writestring(method_name(pm));
  writeln();
  pm2 = first_innermethod(pm);
  while (pm2)
  {
    lmh(pm2);
    pm2 = next_innermethod(pm2);
  }      
  used_methods--;
}

void list_method_hierarchy YPARAMS2(dbx_method *,  pm)
{                            
  used_methods = 0;
  lmh(pm);
}

/*************************************************************/

/* to store the nodes of the branch currently explored in the class tree */
#define MAX_LEVEL_CLASSES 20
static dbx_class *class_table[MAX_LEVEL_CLASSES];
static unsigned used_classes;

static void lch YPARAMS2(dbx_class *,  pc)
{
  int i,j;
  dbx_class *pc2;
  if (used_classes > 0)
  {
    for (i = 0;i < used_classes-1;i++)
    {
      if (last_subclass(class_table[i]) == class_table[i+1])
      {
        for (j = 1;j <= TAB_WIDTH;j++)
          writechar(' ');
      }
      else
      {
        writechar(VBARCHAR);
        for (j = 1;j < TAB_WIDTH;j++)
          writechar(' ');
      }
    }
    if (pc == last_subclass(class_table[used_classes-1]))
      writechar(URCOINCHAR);
    else
      writechar(RTEECHAR);
    for (j = 1;j < TAB_WIDTH;j++)
      writechar(HBARCHAR);
  }
  assert(used_classes < MAX_LEVEL_CLASSES);
  class_table[used_classes] = pc;
  used_classes++;
  writestring(class_name(pc));
  writeln();
  pc2 = first_subclass(pc);
  while (pc2)
  {
    lch(pc2);
    pc2 = next_subclass(pc2);
  }      
  used_classes--;
}

void list_class_hierarchy YPARAMS2(dbx_class *,  pc)
{                            
  used_classes = 0;
  lch(pc);
}

