/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   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

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define ROTR(x,bits) (((x << (16 - bits)) | (x >> bits)) & 0xffff)

#define HASH_SIZE 4096

#pragma rundown show_profile 40
#pragma startup init_profile 200
extern char **_argv;

unsigned _elapsed_time(void);

typedef struct _profile {
	struct _profile *hashlink;
	struct _profile *link;
	char *name;
   unsigned int time;
   unsigned int starttime;
	unsigned count;
} PROFILE;
	
#define PROFILESTR "$$$PROFILER"
#define PROFILESTR1 "$$$PROFILER1"

static PROFILE *proclink;
static int profiling = 0;
static int profileincomplete = 0;
static int profnames = 0;
static PROFILE *hashtab[HASH_SIZE];
static unsigned fnoverhead;

void _profile_in(char *name);
void _profile_out(char *name);
static PROFILE *HashIt(char *str);
static void validate(void);

static void init_profile(void)
{
	// won't work quite right in a multithreaded app
	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL + THREAD_PRIORITY_HIGHEST);
	_elapsed_time();
	profiling = 1;
	validate();
}

static int comparison(const void *a, const void *b)
{
	PROFILE *x=*((PROFILE **)a),*y=*((PROFILE **)b);
	unsigned r,s;
	r = x->time;
	s = y->time;
	if (r & 0xf0000000)
		r = 0;
	if (s & 0xf0000000)
		s = 0;
	if (r < s)
		return 1;
	if (r == s)
		return 0;
	return -1;
}
static void validate(void)
{
	unsigned v1,v2,v3;
	PROFILE *t,*t1;
	_profile_in(PROFILESTR);
	_profile_in(PROFILESTR1);
	_profile_out(PROFILESTR1);
	_profile_out(PROFILESTR);
	t = HashIt(PROFILESTR);
	fnoverhead = t->time;
}
static void show_profile(void)
{
	FILE *out;
	PROFILE **p;
#ifdef XXXXX
	LARGE_INTEGER x;
#endif
	int i,pos=0;
	char name[256],*s;
	unsigned totaltime = 0,totalcount=0;
	profiling = 0;

#ifdef XXXXX
	QueryPerformanceFrequency(&x);
#endif
	if (profileincomplete) {
		printf(stderr,"Not enough memory to perform profiling");
		return;
	}
	p = malloc(profnames * sizeof(PROFILE *));
	if (!p) {
		printf(stderr,"Can't allocate profile sort list");
		return;
	}
   strcpy(name,_argv[0]);
	s = strrchr(name,'.');
	if (!s)
		s = name + strlen(name);
	strcpy(s,".prf");
	out = fopen(name,"w");
	if (!out) {
		printf(stderr,"Can't open profile output file");
		return;
	}
	for (i=0; i < HASH_SIZE; i++) {
		PROFILE *q = hashtab[i];
		while (q) {
			totaltime += q->time;
			totalcount +=q->count;
			p[pos++] = q;
			q = q->hashlink;
		}
	}
	qsort(p,profnames,sizeof(PROFILE *),comparison);
	fprintf(out,"Profile info: (time: %10d, calls: %10d)\n\n",totaltime,totalcount);
	for (i=0; i < profnames; i++) {
		unsigned v = p[i]->time ;
		
		fprintf(out,"%32s: %02u%% (time:%10u) (count:%10d) (avg:%10d)\n",p[i]->name,v/(totaltime/100), v,p[i]->count,v/p[i]->count);
	}
	fclose(out);
	
}
static unsigned int ComputeHash(char *string)
{
  int rv = 0;
  while (*string)
	  rv = (rv << 7) + (rv << 2) + rv + *string++;
  return rv % HASH_SIZE;
}
static PROFILE *HashIt(char *str)
{
	int h = ComputeHash(str);
	PROFILE *p = hashtab[h];
	while (p) {
		if (!strcmp(p->name,str))
			return p;
		p = p->hashlink;
	}
	p = malloc(sizeof(PROFILE));
	if (!p)
		return p;
	profnames++;
	p->name = str;
	p->time = 0;
	p->count = 0;
	p->hashlink = hashtab[h];
	p->link = 0;
	hashtab[h] = p;
	return p;
}
void _profile_in(char *name)
{
	int t;
	PROFILE *p;
	if (!profiling)
		return;
	if (proclink) {
		t = _elapsed_time() - proclink->starttime;
		if (t < 0) t = - t;
		proclink->time += t - fnoverhead;
	}

	profiling = 0;
	p = HashIt(name);
	if (!p) {
		profileincomplete = 1;
		return;
	}
	profiling = 1;
	p->link = proclink;
	proclink = p;
	p->count++;
	p->starttime = _elapsed_time();
	
}
void _profile_out(char *name)
{
	int t;
	if (!profiling || !proclink)
		return;
	t = _elapsed_time() - proclink->starttime;
	if (t < 0) t = - t;
	proclink->time += t - fnoverhead;
	proclink = proclink->link;
	if (proclink)
		proclink->starttime = _elapsed_time();
}