/* ----------------------------------------------------------------------------- 
 * type.c
 *
 *     This file defines a new python type that contains information from
 *     the WAD stack trace.
 *
 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
 *
 * Copyright (C) 2000.  The University of Chicago. 
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * See the file COPYING for a complete copy of the LGPL.
 * ----------------------------------------------------------------------------- */


#include "wad.h"
#include "Python.h"

static char cvs[] = "$Id: type.c 10563 2008-06-23 21:23:54Z olly $";

typedef struct {
  PyObject_HEAD
  WadFrame   *frame;       /* Wad Stack frame object   */
  int         count;       /* Number of frames         */
} wadobject;

staticforward PyTypeObject WadObjectType;


PyObject *
new_wadobject(WadFrame *f, int count) {
  wadobject   *self;
  self = PyObject_NEW(wadobject, &WadObjectType);
  if (self == NULL) return NULL;
  
  self->frame = f;
  if (count > 0) {
    self->count = count;
  } else {
    self->count = 0;
    while (f) {
      self->count++;
      f = f->next;
    }
  }
  return (PyObject *) self;
}

/* release a wad object */
static void
wadobject_dealloc(wadobject *self) {
  PyObject_Del(self);
}

static char message[65536];
static PyObject *
wadobject_repr(wadobject *self) {
  char *srcstr = 0;
  WadFrame *fp = 0;
  int    n;
  WadFrame *f = self->frame;

  message[0] = 0;
  /* Find the last exception frame */
  n = self->count;
  while (f && n) {
    fp = f;
    f= f->next;
    n--;
  }

  if (fp) {
  /* Now work backwards */
    f = fp;
    while (f) {
      strcat(message, f->debug_str);
      if (f->debug_srcstr) srcstr = f->debug_srcstr;
      if (f == self->frame) break;
      f = f->prev;
    }
    if (srcstr) {
      strcat(message,"\n");
      strcat(message, srcstr);
      strcat(message,"\n");
    }
  }
  return PyString_FromString(message);
}

static PyObject *
wadobject_str(wadobject *self) {
  char *srcstr = 0;
  int   n;

  WadFrame *f = self->frame;
  n = self->count;
  strcpy(message,"[ C stack trace ]\n\n");
  /* Find the last exception frame */
  while (!f->last && n) {
    f= f->next;
    n--;
  }
  /* Now work backwards */
  if (n <= 0) {
    f = f->prev;
  }
  while (f) {
    strcat(message, f->debug_str);
    if (f->debug_srcstr) srcstr = f->debug_srcstr;
    if (self->frame == f) break;
    f = f->prev;
  }
  if (srcstr) {
    strcat(message,"\n");
    strcat(message, srcstr);
    strcat(message,"\n");
  }
  return PyString_FromString(message);
}

static int
wadobject_len(wadobject *self) {
  int n = 0;
  WadFrame *f = self->frame;
  while (f) {
    n++;
    f = f->next;
  }
  return n;
}

static PyObject *
wadobject_getitem(wadobject *self, int n) {
  int i;
  WadFrame *f;
  if (n < 0) {
    n = self->count + n;
  }
  if ((n < 0) || (n >= self->count)) {
    PyErr_SetString(PyExc_IndexError,"Stack frame out of range");
    return NULL;
  }
  f = self->frame;
  for (i = 0; i <n; i++) {
    f = f->next;
  }
  return new_wadobject(f,1);
}

static PyObject *
wadobject_getslice(wadobject *self, int start, int end) {
  int i;
  WadFrame *f;

  f = self->frame;
  for (i = 0; i < start; i++) {
    f = f->next;
  }
  return new_wadobject(f,(end-start));
}

static PyObject *
wadobject_getattr(wadobject *self, char *name) {
  if (strcmp(name,"__NAME__") == 0) {
    return Py_BuildValue("z", self->frame->sym_name);
  } else if (strcmp(name,"__EXE__") == 0) {
    return Py_BuildValue("z", self->frame->object->path);
  } else if (strcmp(name,"__FILE__") == 0) {
    return Py_BuildValue("z", self->frame->loc_srcfile);
  } else if (strcmp(name,"__OBJECT__") == 0) {
    return Py_BuildValue("z", self->frame->loc_objfile);
  } else if (strcmp(name,"__LINE__") == 0) {
    return Py_BuildValue("i", self->frame->loc_line);
  } else if (strcmp(name,"__SOURCE__") == 0) {
    return Py_BuildValue("z",self->frame->debug_srcstr);
  } else if (strcmp(name,"__PC__") == 0) {
    return PyLong_FromUnsignedLong(self->frame->pc);
  } else if (strcmp(name,"__SP__") == 0) {
    return PyLong_FromUnsignedLong(self->frame->sp);
  } else if (strcmp(name,"__FP__") == 0) {
    return PyLong_FromUnsignedLong(self->frame->fp);
  } else if (strcmp(name,"__STACK__") == 0) {
    return PyString_FromStringAndSize(self->frame->stack, self->frame->stack_size);
  } else if (strcmp(name,"__NARGS__") == 0) {
    return PyInt_FromLong(self->frame->debug_nargs);
  } else if (strcmp(name,"__LAST__") == 0) {
    return PyInt_FromLong(self->frame->last);
  } else if (strcmp(name,"__WHERE__") == 0) {
    return Py_BuildValue("z",self->frame->debug_str);
  } else if (strcmp(name,"__WAD__") == 0) {
    return PyInt_FromLong(1);
  }

  
  /* Put a check for local variables */
  {
    int i;
    for (i = 0; i < 2; i++) {
      WadLocal *loc;
      if (i == 0) loc = self->frame->debug_locals;
      else loc = self->frame->debug_args;
      while (loc) {
	if (strcmp(name,loc->name) == 0) {
	  switch(loc->type) {
	  case WAD_TYPE_INT32:
	  case WAD_TYPE_INT16:
	  case WAD_TYPE_INT8:
	    return PyLong_FromLong(wad_local_as_long(loc));
	    break;
	  case WAD_TYPE_UINT8:
	  case WAD_TYPE_UINT16:
	  case WAD_TYPE_UINT32:
	    return PyLong_FromUnsignedLong((unsigned long) wad_local_as_long(loc));
	    break;
	  case WAD_TYPE_CHAR:
	    return Py_BuildValue("c", (char) (PyLong_FromLong(wad_local_as_long(loc))));
	    break;
	  case WAD_TYPE_FLOAT:
	  case WAD_TYPE_DOUBLE:
	    return PyFloat_FromDouble(wad_local_as_double(loc));
	    break;
	  default:
	    return PyLong_FromUnsignedLong((unsigned long) wad_local_as_long(loc));
	  }
	}
	loc = loc->next;
      }
    }
  }
  
  PyErr_SetString(PyExc_NameError,"Unknown attribute.");
  return NULL;
}
static PySequenceMethods wadobject_as_sequence = {
  (inquiry)   wadobject_len,
  0,
  0,
  (intargfunc)  wadobject_getitem,  /* get item */
  (intintargfunc)  wadobject_getslice,  /* get slice */
  0,
  0
};

static PyTypeObject WadObjectType = {
  PyObject_HEAD_INIT(&PyType_Type)
  0,
  "WadObject",
  sizeof(wadobject),
  0,
  (destructor) wadobject_dealloc,
  0,             /* printfunc */
  (getattrfunc) wadobject_getattr,
  (setattrfunc) 0,
  (cmpfunc) 0,
  (reprfunc) wadobject_repr,
  
  0,     /* number */
  &wadobject_as_sequence,     /* sequence */
  0,     /* mapping */
  0,     /* hash */
  0,     /* call */
  (reprfunc) wadobject_str,   /* str */
};

