// spy.cc -- engine performance monitor
//
// Author: Ian.Piumarta@inria.fr
//
// last edited: 2000-10-22 20:53:30 by piumarta on emilia.rd.wdi.disney.com

#if (defined(macintosh) || defined(WIN32))
# undef HAVE_LIBX11
  int withSpy= 0;
#else
# define HAVE_LIBX11 1	// sqUnixConfig.h
#endif


#ifdef HAVE_LIBX11

#include <stdio.h>
#include <string.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "Object.h"
#include "debug.h"

extern "C" {
  int lengthOf(oop object);
};

#undef	USE_SQUEAK_WINDOW
#define	NO_SPY_TITLE

#define MAJOR_UPDATES_PER_SEC	1
#define MINOR_UPDATES_PER_SEC	20

/* name of spy window in Xrm and the WM */
#define ClassName	"SqueakCompilerSpy"
#define WindowName	"SqueakCompiler spy"

extern Display	*stDisplay;	// Squeak's Display
extern Window	 stWindow;	// Squeak's Window
extern Visual	*stVisual;	// Squeak's default visual
extern GC	 stGC;		// Squeak's graphics context
extern XColor	 stColorBlack;	// Squeak's idea of a black pixel
extern XColor	 stColorWhite;	// Squeak's idea of a white pixel
extern Colormap	 stColormap;	// Squeak's colour map

// pixel values as defined in the Squeak colour map for 8-bit (PseudoColor).
// deeper displays (with TrueColor visuals) might not grok
#if 0
# define SqueakWhite	0
# define SqueakBlack	1
#else
# define SqueakWhite	stColorWhite.pixel
# define SqueakBlack	stColorBlack.pixel
#endif

static Window	 window;	// our window

static bool	 enabled= false;
static XFontStruct *fontStruct;	// for snarfing character properties
static Font	 font;

#define	BORDER	  4	// pixel indent
#define	NCOLS   100
#define	NROWS	 10
#define	LINESEP	  2	// pixels: raster bottom to raster top on next line
#define BARSIZE   9	// height of graphics bars

#define BARLINE	  9
#define COMPLINE 10

static unsigned	 charWidth;	// raster left to raster right
static unsigned	 charHeight;	// raster top to raster bottom
static unsigned	 charBaseline;	// raster top to baseline
static unsigned	 baselineSkip;	// distance between baselines with lineskip

static char	*transPrefix= "compiling:  ";
static int	 transPrefixLen;

extern oop memory, freeBlock, endOfMemory;

static oop  lastYoung= 0;
static oop  minYoung= 0;


// called from jitter initialisation

void spyInitialise(void)
{
  int char_X, char_p;

  transPrefixLen= strlen(transPrefix);

  fontStruct= XLoadQueryFont(stDisplay, "fixed");
  font= fontStruct->fid;
  // font dimensions
  char_X=	'X' - fontStruct->min_char_or_byte2;
  char_p=	'p' - fontStruct->min_char_or_byte2;
  charWidth=	fontStruct->per_char[char_X].width;
  charBaseline=	fontStruct->per_char[char_X].ascent;
  charHeight=	charBaseline + fontStruct->per_char[char_p].descent;
  baselineSkip=	charHeight + LINESEP;

  XRectangle windowBounds= {
    0, 0,
    (charWidth * NCOLS) + (BORDER * 2),
    (baselineSkip * NROWS) + (BORDER * 2) + BARSIZE
  };

#ifdef USE_SQUEAK_WINDOW

  window= stWindow;

#else // !USE_SQUEAK_WINDOW

  window= XCreateSimpleWindow
    (stDisplay, DefaultRootWindow(stDisplay),
     windowBounds.x, windowBounds.y,
     windowBounds.width, windowBounds.height,
     1, SqueakBlack, SqueakWhite);
  {
    XClassHint *classHints= XAllocClassHint();
    classHints->res_name= classHints->res_class= ClassName;
    XSetClassHint(stDisplay, window, classHints);
    XStoreName(stDisplay, window, WindowName);
    XFree(classHints);
  }
  {
    XWMHints *wmHints= XAllocWMHints();
    wmHints->input= True;
    wmHints->initial_state= NormalState;
    wmHints->flags= InputHint|StateHint;
    XSetWMHints(stDisplay, window, wmHints);
    XFree((void *)wmHints);
  }
  // backing store useful for event-free drawing
  {
    XSetWindowAttributes attributes;
    attributes.backing_store= Always;
    XChangeWindowAttributes(stDisplay, window, CWBackingStore, &attributes);
  }
  if (stVisual->c_class == PseudoColor) {
    XSetWindowColormap(stDisplay, window, stColormap); // humour preservation
  }
#ifdef NO_SPY_TITLE
  XSetTransientForHint(stDisplay, window, DefaultRootWindow(stDisplay));
#endif

  XMapWindow(stDisplay, window);

#endif // !USE_SQUEAK_WINDOW

  enabled= true;
  minYoung= youngStart;
}


// called from jitter release

void spyRelease(void)
{
  if (!enabled) return;
  enabled= false;
  XDestroyWindow(stDisplay, window);
  XUnloadFont(stDisplay, font);
}


// called from translateMethodFor()

void spyTranslation(Class *cls, oop selector)
{
  if (!enabled) return;
  assert(cls->okayFields());
  assert(selector->okayFields());
  char message[128];
  strcpy(message, transPrefix);
  char *classSuffix;

  if (cls->_sizeBits() == 28) {
    Metaclass *meta= cls->asMetaclass();
    cls= meta->thisClass;
    classSuffix= " class";
  } else {
    classSuffix= "";
  }

  Symbol *name= cls->name;
  assert(name->okayFields());

  strncat(message, (char *)&name->_bytes(0), stSizeOf(name));
  strcat(message, classSuffix);
  strcat(message, ">>");
  strncat(message, (char *)&selector->_bytes(0), stSizeOf(selector));
  XClearArea(stDisplay, window,
	     BORDER, BORDER + (COMPLINE * baselineSkip),
	     (BORDER * 2) + charWidth * NCOLS,
	     baselineSkip,
	     False);
  XDrawString(stDisplay, window, stGC,
	      BORDER,
	      BORDER + (COMPLINE * baselineSkip) + charBaseline,
	      message, strlen(message));

#if 1
    // this makes for smoother display, but slows down the VM
    XFlush(stDisplay);
#endif
}

void spyTranslated(void)
{
  if (!enabled) return;
  XClearArea(stDisplay, window,
	     BORDER, BORDER + (COMPLINE * baselineSkip),
	     BORDER + charWidth * (transPrefixLen - 1),
	     baselineSkip,
	     False);
#if 1
    // this makes for smoother display, but slows down the VM
    XFlush(stDisplay);
#endif
}


static unsigned widths[NROWS];

static void displayLine(int lineNo, char *line)
{
  int len= strlen(line);
  XClearArea(stDisplay, window,
	     BORDER,
	     BORDER + (lineNo * baselineSkip),
	     charWidth * widths[lineNo],
	     baselineSkip,
	     False);
  XDrawString(stDisplay, window, stGC,
	      BORDER,
	      BORDER + (lineNo * baselineSkip) + charBaseline,
	      line, len);
  widths[lineNo]= len;
}


static int lastUpdate;
static int majorClock= 0;
static int minorClock= 0;

extern "C" {
  int ioLowResMSecs(void);
  int ioMicroMSecs(void);
  int ioMSecs(void);
};


// called from: process change, post GC, interrupt check

void spyStatistics(void)
{
  if (!enabled) return;

  int thisUpdate= ioLowResMSecs();
  int updateDelta= thisUpdate - lastUpdate;
  lastUpdate= thisUpdate;
  majorClock-= updateDelta;
  minorClock-= updateDelta;

  if (minorClock < 0) {
    unsigned stat_jitterEntries= 0;
    minorClock= 1000 / MINOR_UPDATES_PER_SEC;
    int tl= BORDER;
    int ty= BORDER + BARLINE * baselineSkip;
    int tr= BORDER + charWidth * (transPrefixLen - 1);
    int tw= tr - tl;
    int roots= (rootTableCount * tw) / RootTableSize;
#   if 0
    printf("%d\n", rootTableCount);
#   endif

    XClearArea(stDisplay, window,
	       tl, ty, tr, BARSIZE,
	       False);

    XDrawRectangle(stDisplay, window, stGC,
		   tl, ty, tw, BARSIZE - 1);

    XFillRectangle(stDisplay, window, stGC,
		   tl, ty, roots, BARSIZE - 1);

    int ml= BORDER + charWidth * transPrefixLen;
    int mr= BORDER + charWidth * (NCOLS);
    int mw= mr - ml;

    XClearArea(stDisplay, window,
	       ml, ty, mr - ml, BARSIZE,
	       False);

    XDrawRectangle(stDisplay, window, stGC,
		   ml, ty, mr - ml, BARSIZE - 1);

    if (youngStart < lastYoung) minYoung= youngStart;
    lastYoung= youngStart;

    int memSize= endOfMemory - memory;
    int old= (minYoung - memory) * mw / memSize;
    int tenured= (youngStart - memory) * mw / memSize;
    int young= (freeBlock - memory) * mw / memSize;

    XFillRectangle(stDisplay, window, stGC,
		   ml, ty, old - 1, BARSIZE - 1);

    XFillRectangle(stDisplay, window, stGC,
		   ml + tenured, ty, young - tenured, BARSIZE - 1);
  }

  if (majorClock < 0) {
    majorClock= (int)(1000 / MAJOR_UPDATES_PER_SEC);
    char buf[NROWS][NCOLS];
    
    extern void printStatsOn(char [][NCOLS]);
    printStatsOn(buf);
    displayLine(0, buf[0]);
    displayLine(1, buf[1]);
    displayLine(2, buf[2]);
    displayLine(3, buf[3]);
    displayLine(4, buf[4]);
    displayLine(5, buf[5]);
    displayLine(6, buf[6]);
    displayLine(7, buf[7]);
    displayLine(8, buf[8]);
#if 1
    // this makes for smoother display, but slows down the VM
    XFlush(stDisplay);
#endif
  }
}


#else // !HAVE_LIBX11

#include "Object.h"

void spyInitialise(void)
{
}

void spyRelease(void)
{
}

void spyTranslation(Class *cls, oop selector)
{
}

void spyTranslated(void)
{
}

static void displayLine(int lineNo, char *line)
{
}

static int lastUpdate;
static int majorClock= 0;
static int minorClock= 0;

void spyStatistics(void)
{
}



#endif // HAVE_LIBX11
