// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "runtime.h"
#include "type.h"

//static Lock debuglock;

static void vprintf(int8*, byte*);

void
dump(byte *p, int32 n)
{
	int32 i;

	for(i=0; i<n; i++) {
		·printpointer((byte*)(p[i]>>4));
		·printpointer((byte*)(p[i]&0xf));
		if((i&15) == 15)
			prints("\n");
		else
			prints(" ");
	}
	if(n & 15)
		prints("\n");
}

void
prints(int8 *s)
{
	write(fd, s, findnull((byte*)s));
}

#pragma textflag 7
void
printf(int8 *s, ...)
{
	byte *arg;

	arg = (byte*)(&s+1);
	vprintf(s, arg);
}

static byte*
vrnd(byte *p, int32 x)
{
	if((uint32)(uintptr)p&(x-1))
		p += x - ((uint32)(uintptr)p&(x-1));
	return p;
}

// Very simple printf.  Only for debugging prints.
// Do not add to this without checking with Rob.
static void
vprintf(int8 *s, byte *arg)
{
	int8 *p, *lp;
	byte *narg;

//	lock(&debuglock);

	lp = p = s;
	for(; *p; p++) {
		if(*p != '%')
			continue;
		if(p > lp)
			write(fd, lp, p-lp);
		p++;
		narg = nil;
		switch(*p) {
		case 't':
			narg = arg + 1;
			break;
		case 'd':	// 32-bit
		case 'x':
			arg = vrnd(arg, 4);
			narg = arg + 4;
			break;
		case 'D':	// 64-bit
		case 'U':
		case 'X':
		case 'f':
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + 8;
			break;
		case 'C':
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + 16;
			break;
		case 'p':	// pointer-sized
		case 's':
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + sizeof(uintptr);
			break;
		case 'S':	// pointer-aligned but bigger
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + sizeof(String);
			break;
		case 'a':	// pointer-aligned but bigger
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + sizeof(Slice);
			break;
		case 'i':	// pointer-aligned but bigger
		case 'e':
			arg = vrnd(arg, sizeof(uintptr));
			narg = arg + sizeof(Eface);
			break;
		}
		switch(*p) {
		case 'a':
			·printslice(*(Slice*)arg);
			break;
		case 'd':
			·printint(*(int32*)arg);
			break;
		case 'D':
			·printint(*(int64*)arg);
			break;
		case 'e':
			·printeface(*(Eface*)arg);
			break;
		case 'f':
			·printfloat(*(float64*)arg);
			break;
		case 'C':
			·printcomplex(*(Complex128*)arg);
			break;
		case 'i':
			·printiface(*(Iface*)arg);
			break;
		case 'p':
			·printpointer(*(void**)arg);
			break;
		case 's':
			prints(*(int8**)arg);
			break;
		case 'S':
			·printstring(*(String*)arg);
			break;
		case 't':
			·printbool(*(bool*)arg);
			break;
		case 'U':
			·printuint(*(uint64*)arg);
			break;
		case 'x':
			·printhex(*(uint32*)arg);
			break;
		case 'X':
			·printhex(*(uint64*)arg);
			break;
		case '!':
			panic(-1);
		}
		arg = narg;
		lp = p+1;
	}
	if(p > lp)
		write(fd, lp, p-lp);

//	unlock(&debuglock);
}

#pragma textflag 7
void
·printf(String s, ...)
{
	// Can assume s has terminating NUL because only
	// the Go compiler generates calls to ·printf, using
	// string constants, and all the string constants have NULs.
	vprintf((int8*)s.str, (byte*)(&s+1));
}

void
·printpc(void *p)
{
	prints("PC=");
	·printhex((uint64)·getcallerpc(p));
}

void
·printbool(bool v)
{
	if(v) {
		write(fd, (byte*)"true", 4);
		return;
	}
	write(fd, (byte*)"false", 5);
}

void
·printfloat(float64 v)
{
	byte buf[20];
	int32 e, s, i, n;
	float64 h;

	if(isNaN(v)) {
		write(fd, "NaN", 3);
		return;
	}
	if(isInf(v, 1)) {
		write(fd, "+Inf", 4);
		return;
	}
	if(isInf(v, -1)) {
		write(fd, "-Inf", 4);
		return;
	}


	n = 7;	// digits printed
	e = 0;	// exp
	s = 0;	// sign
	if(v != 0) {
		// sign
		if(v < 0) {
			v = -v;
			s = 1;
		}

		// normalize
		while(v >= 10) {
			e++;
			v /= 10;
		}
		while(v < 1) {
			e--;
			v *= 10;
		}

		// round
		h = 5;
		for(i=0; i<n; i++)
			h /= 10;
		v += h;
		if(v >= 10) {
			e++;
			v /= 10;
		}
	}

	// format +d.dddd+edd
	buf[0] = '+';
	if(s)
		buf[0] = '-';
	for(i=0; i<n; i++) {
		s = v;
		buf[i+2] = s+'0';
		v -= s;
		v *= 10.;
	}
	buf[1] = buf[2];
	buf[2] = '.';

	buf[n+2] = 'e';
	buf[n+3] = '+';
	if(e < 0) {
		e = -e;
		buf[n+3] = '-';
	}

	buf[n+4] = (e/100) + '0';
	buf[n+5] = (e/10)%10 + '0';
	buf[n+6] = (e%10) + '0';
	write(fd, buf, n+7);
}

void
·printcomplex(Complex128 v)
{
	write(fd, "(", 1);
	·printfloat(v.real);
	·printfloat(v.imag);
	write(fd, "i)", 2);
}

void
·printuint(uint64 v)
{
	byte buf[100];
	int32 i;

	for(i=nelem(buf)-1; i>0; i--) {
		buf[i] = v%10 + '0';
		if(v < 10)
			break;
		v = v/10;
	}
	write(fd, buf+i, nelem(buf)-i);
}

void
·printint(int64 v)
{
	if(v < 0) {
		write(fd, "-", 1);
		v = -v;
	}
	·printuint(v);
}

void
·printhex(uint64 v)
{
	static int8 *dig = "0123456789abcdef";
	byte buf[100];
	int32 i;

	i=nelem(buf);
	for(; v>0; v/=16)
		buf[--i] = dig[v%16];
	if(i == nelem(buf))
		buf[--i] = '0';
	buf[--i] = 'x';
	buf[--i] = '0';
	write(fd, buf+i, nelem(buf)-i);
}

void
·printpointer(void *p)
{
	·printhex((uint64)p);
}

void
·printstring(String v)
{
	extern int32 maxstring;

	if(v.len > maxstring) {
		write(fd, "[invalid string]", 16);
		return;
	}
	if(v.len > 0)
		write(fd, v.str, v.len);
}

void
·printsp(void)
{
	write(fd, " ", 1);
}

void
·printnl(void)
{
	write(fd, "\n", 1);
}
