#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <bio.h>
#include <mouse.h>
#include <keyboard.h>

void keyboardthread(void *v);
void mousethread(void *v);
void resizethread(void *v);
void updateproc(void *v);

enum {
	STACK = 8196,
};

int nokill;
int textmode;
char *title;

Biobuf b;
Image *light;
Image *dark;
Image *text;
Keyboardctl *kc;
Mousectl *mc;

void
initcolor(void)
{
	text = display->black;
	light = allocimagemix(display, DPalegreen, DWhite);
	dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen);
}

Rectangle rbar;
vlong n, d;
int last;
int lastp = -1;

char backup[80];

void
drawbar(void)
{
	int i, j;
	int p;
	char buf[400], bar[200];
	static char lastbar[200];

	if(n > d || n < 0 || d <= 0)
		return;

	i = (Dx(rbar)*n)/d;
	p = (n*100LL)/d;
	if(textmode){
		if(Dx(rbar) > 150){
			rbar.min.x = 0;
			rbar.max.x = 150;
			return;
		}
		bar[0] = '|';
		for(j=0; j<i; j++)
			bar[j+1] = '#';
		for(; j<Dx(rbar); j++)
			bar[j+1] = '-';
		bar[j++] = '|';
		bar[j++] = ' ';
		sprint(bar+j, "%3d%% ", p);
		for(i=0; bar[i]==lastbar[i] && bar[i]; i++)
			;
		memset(buf, '\b', strlen(lastbar)-i);
		strcpy(buf+strlen(lastbar)-i, bar+i);
		if(buf[0])
			write(1, buf, strlen(buf));
		strcpy(lastbar, bar);
		return;
	}

	if(lastp == p && last == i)
		return;

	if(lastp != p){
		sprint(buf, "%d%%", p);
		stringbg(screen, addpt(screen->r.min, Pt(Dx(rbar)-30, 4)), text, ZP, display->defaultfont, buf, light, ZP);
		lastp = p;
	}

	if(last != i){
		draw(screen, Rect(rbar.min.x+last, rbar.min.y, rbar.min.x+i, rbar.max.y),
			dark, nil, ZP);
		last = i;
	}
	flushimage(display, 1);
}

void
resize()
{
	Point p, q;
	Rectangle r;

	r = screen->r;
	draw(screen, r, light, nil, ZP);
	p = string(screen, addpt(r.min, Pt(4,4)), text, ZP,
		display->defaultfont, title);

	p.x = r.min.x+4;
	p.y += display->defaultfont->height+4;
	
	q = subpt(r.max, Pt(4,4));
	rbar = Rpt(p, q);
	border(screen, rbar, -2, dark, ZP);
	last = 0;
	lastp = -1;

	flushimage(display, 1);
	drawbar();	
}

void
keyboardthread(void *v)
{	
	Rune r;

	while(recv(kc->c , &r) == 1){
		if ((r == 0x7F || r == 0x03 || r == 'q') && !nokill)
			threadexitsall("interrupt");
	}
}

void
mousethread(void *v)
{	
	USED(v);
	
	while(recv(mc->c, 0) == 1); /* to unblock mc->c */
}

void
resizethread(void *v)
{
	USED(v);

	while(recv(mc->resizec, 0) == 1){
		lockdisplay(display);
		if(getwindow(display, Refnone) < 0)
			sysfatal("attach to window: %r");
		resize();
		unlockdisplay(display);
	}
}

void
updateproc(void *v)
{
	char *p, *f[2];

	sleep(1000);
	while((p = Brdline(&b, '\n'))){
		p[Blinelen(&b)-1] = '\0';
		if(tokenize(p, f, 2) != 2)
			continue;
		n = strtoll(f[0], 0, 0);
		d = strtoll(f[1], 0, 0);
		if(!textmode){
			lockdisplay(display);
			drawbar();
			unlockdisplay(display);
		} else
			drawbar();
	}
	threadexitsall("success");
}

void
usage(void)
{
	fprint(2, "usage: statusbar [-kt] [-W winsize] 'title'\n");
	threadexitsall("usage");
}

void
threadmain(int argc, char **argv)
{
	char *p;
	int lfd;

	p = "300x40@100,100";
	
	ARGBEGIN{
	case 'W':
		p = ARGF();
		break;
	case 't':
		textmode = 1;
		break;
	case 'k':
		nokill = 1;
		break;
	default:
		usage();
	}ARGEND;

	if(argc != 1)
		usage();

	winsize = p;

	title = argv[0];

	lfd = dup(0, -1);
	Binit(&b, lfd, OREAD);

	rbar = Rect(0, 0, 60, 1);
	if (!textmode){
		if(initdraw(0, nil, "bar") < 0)
			sysfatal("initdraw: %r");
		initcolor();
		if((mc = initmouse(nil, screen)) == nil)
			sysfatal("initmouse: %r");
		if((kc = initkeyboard(nil)) == nil)
			sysfatal("initkeyboard: %r");
		display->locking = 1;
		threadcreate(resizethread, nil, STACK);
		threadcreate(keyboardthread, nil, STACK);
		threadcreate(mousethread, nil, STACK);
		resize();
		unlockdisplay(display);
	}
	proccreate(updateproc, nil, STACK);
}

