#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <yafl_usr.h>
#include <yafl_rnt.h>
#include "dbx_rnt.h"
#include "dbx_type.h"
#include "dbx_vari.h"
#include "dbx_modu.h"
#include "dbx_clas.h"
#include "dbx_obar.h"
#include "dbx_fiel.h"
#include "dbx_meth.h"
#include "dbx_call.h"
#include "dbx_loca.h"
#include "dbx_list.h"
#include "dbx_fram.h"
#include "dbx_ctxt.h"
#include "dbx_stri.h"

/* the base once of a global context can be undefined if no onces are */
/* present in the debugged system */
/* the base call of a local context is always defined because the runtime */ 
/* is never called with an empty call stack */ /* ??? */
/* the base local of a local context is always defined because there is */
/* at least once local (THIS) for every call */
/* the top class of a context can be undefined if no one of the classes */
/* composing the top object is present in the debugger;the top field of a */
/* context can be undefined is the top class is empty (contains no fields) */
/* the top subscript of a context can be undefined if the top array is */
/* empty (has size equal to zero) */
/* in every case this string is used in place of names in the descriptor */
/* of the context */

static char undefstr[] = "?";

/* for conversions */
static char buffer[100];

dbx_context *new_context YPARAMS0
{
  dbx_context *p;
  p = (dbx_context *)malloc(sizeof(dbx_context));
  memset(p,0,sizeof(dbx_context));
  p->the_name_ptr = strnew();
  p->parts = new_stack();
  return p;
}

/* the context must be emtpy before being destroyed */
void dispose_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  free(pc->the_name_ptr);
  dispose_stack(pc->parts);
  free(pc);
}

/* create a new variable structure corresponding to the top data item */
/* of a context so that this item can be printed or modified by the user */
/* if the context is undefined (designates no data item) a NULL value */
/* is returned */
dbx_variable *create_variable YPARAMS2(dbx_context *,  pc)
{
  dbx_variable *pv;
  dbx_type *pt;
  unsigned level;
  VOID *data;
  assertp(pc);
  if (empty_context(pc))
  { 
    if (global_context(pc))
    {
      if (context_once(pc))
      {
        dbx_class *pcl = context_once(pc);
        pt = class_type(pcl);
        level = 0;
        data = class_once_ptr(pcl);
      }
      else
      {
        return NULL;
      }
    }
    else /* local_context(pc) */
    {
      if (context_local(pc))
      {
        dbx_local *pl = context_local(pc);
        pt = local_type(pl);
        level = local_level(pl);
        data = get_ldata(pl,context_call(pc));
      }
      else
      {
        return NULL;
      }
    }
  }
  else
  {
    obj_ptr p;
    p = top_objarr(pc);
    if (is_object(p))
    {
      if (top_field(pc))
      {
        dbx_field *pf = top_field(pc);
        pt = dbx_field_type(pf);
        level = field_level(pf);
        data = get_fdata(pf,top_objarr(pc));
      }
      else
        return NULL;
    }
    else /* is_array(p) */
    {
      if (array_size(p) != 0)
      {
        pt = array_type(p);
        level = array_level(p)-1;
        data = array_element(p,top_subscript(pc));
      }
      else
        return NULL;
    }
  }
  pv = new_variable(pt,level,data);
  return(pv);
}
                 
/* expand the context by entering in a new object or array if possible */
/* if operation fails a value of one is returned */
int expand_context YPARAMS2(dbx_context *,  pc)
{
  dbx_variable *pv;
  dbx_type *pt;
  unsigned level;
  VOID *data;
  assertp(pc);
  pv = create_variable(pc);
  if (!pv)
    return 1;
  pt = variable_type(pv);
  level = variable_level(pv);
  data = variable_address(pv);
  dispose_variable(pv);
  if (pt)
  {
    if (is_class(pt)||(level>0)||(is_virtual(pt)))
    {
      obj_ptr p;
      p = (*(obj_ptr *)data);
      if (p)
      {
        push_objarr(pc,p);
        return 0;
      }
      else
        return 1;
    }
    else
      return 1;
  }
  else
  {
/* this should not happen anymore */
/*  
    obj_ptr *p;
    p = (*(obj_ptr *)data);
    if (p)
    {
      push_objarr(pc,p);
      return 0;
    }
    else
      return 1;
*/
    assert(0);  
  }
  return 0;
}
                                                   
/* retract a context by coming back to the previous state */
/* if operation fails a value of one is returned */
int retract_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  if (empty_context(pc))
    return 1;
  pop_objarr(pc);
  return 0;
}

void pop_objarr YPARAMS2(dbx_context *,  pc)
{
  obj_ptr p;
  assertp(pc);
  p = top_objarr(pc);
  if (is_object(p))
  {
    pop_object(pc);
  }
  else /* is_array(p) */
  {
    pop_array(pc);
  }
}

void push_objarr YPARAMS4(dbx_context *,  pc,
                         obj_ptr,        p)
{	
  assertp(pc);
  assertp(p);	
  if (is_object(p))
  {
    dbx_class *pcl;
    pcl = lowest_class(p);
    if (pcl)
    {
      push_object(pc,p,pcl,first_field(pcl));
    }		    
    else
    {
      push_object(pc,p,NULL,NULL);
    }
  }
  else /* is_array(p) */
  {
    push_array(pc,p,0);
  }
}

/* the name of the context is the textual description of the path followed */
/* in the graph of data items */
char *context_name YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (pc->the_name_ptr);
}

dbx_stack *context_parts YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (pc->parts);
}

int empty_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (empty_stack(context_parts(pc)));
}

int undefined_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return 0;
}

/* a global context is used to explore data starting with a once class */
int global_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return(pc->global);
}

/* a local context is used to explore data starting with a local variable */
int local_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return !(pc->global);
}

void push_object YPARAMS8(dbx_context *,  pc,
                         obj_ptr ,     po,
                         dbx_class *,  pcl,
		         dbx_field *,  pf)
{
  char *s;
  char *sc,*sf;
  int length;
  assertp(pc);
  assertp(po);
  push_frame(pc->parts,new_frame(po,pcl,pf,-1));
  if (pcl)
    sc = class_name(pcl);
  else
    sc = undefstr;
  if (pf)
    sf = field_name(pf);
  else
    sf = undefstr;
#ifndef MVS
  s = strjoin5(pc->the_name_ptr,".<",sc,">",sf);
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;
#endif
}

void pop_object YPARAMS2(dbx_context *,  pc)
{
  char *s;
  unsigned n;
  assertp(pc);
  pop_frame(pc->parts);
#ifndef MVS
  n = strrightchar(pc->the_name_ptr,'.');
  s = strleft(pc->the_name_ptr,n-1);
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;
#endif
}
						
void push_array YPARAMS6(dbx_context *,  pc,
                        obj_ptr,        pa,
		                    unsigned,       i)
{
  char *s;    
  assertp(pc);
  assertp(pa);
  push_frame(pc->parts,new_frame(pa,NULL,NULL,i));
  sprintf(buffer,"%d",i);
#ifndef MVS
  if (array_size(pa) != 0)
    s = strjoin4(pc->the_name_ptr,"[",buffer,"]");
  else
    s = strjoin4(pc->the_name_ptr,"[",undefstr,"]");
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;
#endif
}

void pop_array YPARAMS2(dbx_context *,  pc)
{
  char *s;
  unsigned n;
  assertp(pc);
  pop_frame(pc->parts);
#ifndef MVS
  n = strrightchar(pc->the_name_ptr,'[');
  s = strleft(pc->the_name_ptr,n-1);
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;
#endif
}

void reset_context YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  while (!empty_context(pc))
  {
    pop_objarr(pc);
  }
}

dbx_class *context_once YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return(pc->once);
}

dbx_call *context_call YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (pc->call);
}

dbx_method *context_method YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (call_method(context_call(pc)));
}

dbx_local *context_local YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (pc->local);
}

obj_ptr top_objarr YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (frame_objarr(top_frame(context_parts(pc))));
}

dbx_class *top_class YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return (frame_class(top_frame(context_parts(pc))));
}

dbx_field *top_field YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return(frame_field(top_frame(context_parts(pc))));
}

unsigned top_subscript YPARAMS2(dbx_context *,  pc)
{
  assertp(pc);
  return(frame_subscript(top_frame(context_parts(pc))));
}

void change_once YPARAMS4(dbx_context *,  pc,
                         dbx_class *,  pcl)
{
  char *s;
  assertp(pc);
  reset_context(pc);
  pc->global = 1;
  pc->once = pcl;
/*
  pc->call = NULL;
  pc->local = NULL;
*/
#ifndef MVS
  if (pcl)
    s = strclone(class_name(pcl));
  else
    s = strclone(undefstr);
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;
#endif
}

void change_call YPARAMS4(dbx_context *,  pc,
                         dbx_call *,  pca)
{
  char *s;
  char *sc,*sl;
  dbx_local *pl;
  int length;
  assertp(pc);
  assertp(pca);
  reset_context(pc);
  pl = first_local(call_method(pca));
  pc->global = 0;
/*
  pc->once = NULL;
*/
  pc->call = pca;
  pc->local = pl;
  sc = call_name(pca);
#ifndef MVS
  if (pl)
    sl = local_name(pl);
  else
    sl = undefstr;
  s = strjoin4("{",sc,"}",sl);
  free(pc->the_name_ptr);
  pc->the_name_ptr  = s;
#endif
}

void change_local YPARAMS4(dbx_context *,  pc,
                          dbx_local *,    pl)
{
  char *s;
  char *t;
  unsigned n;
  assertp(pc);
  assertp(pl);
  reset_context(pc);
  pc->local = pl;
#ifndef MVS
  n = strrightchar(pc->the_name_ptr,'}');
  t = strleft(pc->the_name_ptr,n);
  s = strjoin(t,local_name(pl));
  free(t);
  free(pc->the_name_ptr);
  pc->the_name_ptr = s;  
#endif
}
   
void change_top_class YPARAMS4(dbx_context *,pc,
                              dbx_class *,  pcl)
{
  obj_ptr po;
  dbx_field *pf;
  assertp(pc);
  assertp(pcl);
  po = top_objarr(pc);
  pf = first_field(pcl);
  pop_object(pc);
  push_object(pc,po,pcl,pf);
}

void change_top_field YPARAMS4(dbx_context *,pc,
                              dbx_field *,  pf)
{
  obj_ptr po;
  dbx_class *pcl;
  assertp(pc);
  po = top_objarr(pc);
  pcl = top_class(pc);
  pop_object(pc);
  push_object(pc,po,pcl,pf);
}

void change_top_subscript YPARAMS4(dbx_context *, pc,
                                  unsigned,  i)
{
  obj_ptr pa;
  assertp(pc);
  pa = top_objarr(pc);
  pop_array(pc);
  push_array(pc,pa,i);
}
