#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <panel.h>
#include "pldefs.h"
typedef struct List List;
struct List{
	void (*hit)(Panel *, int, int);	/* call user back on hit */
	char *(*gen)(Panel *, int);	/* return text given index or 0 if out of range */
	int lo;				/* indices of first, last items displayed */
	int sel;			/* index of hilited item */
	int len;			/* # of items in list */
	Rectangle listr;
	Point minsize;
	int buttons;
};
#define	MAXHGT	12
void pl_listsel(Panel *p, int sel, int on){
	List *lp;
	int hi;
	Rectangle r;
	lp=p->data;
	hi=lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height;
	if(lp->lo>=0 && lp->lo<=sel && sel<hi && sel<lp->len){
		r=lp->listr;
		r.min.y+=(sel-lp->lo)*font->height;
		r.max.y=r.min.y+font->height;
		if(on)
			pl_highlight(p->b, r);
		else{
			pl_fill(p->b, r);
			pl_drawicon(p->b, r, PLACEW, 0, lp->gen(p, sel));
		}
	}
}
void pl_liststrings(Panel *p, int lo, int hi, Rectangle r){
	Panel *sb;
	List *lp;
	char *s;
	int i;
	lp=p->data;
	for(i=lo;i!=hi && (s=lp->gen(p, i));i++){
		r.max.y=r.min.y+font->height;
		pl_drawicon(p->b, r, PLACEW, 0, s);
		r.min.y+=font->height;
	}
	if(lo<=lp->sel && lp->sel<hi) pl_listsel(p, lp->sel, 1);
	sb=p->yscroller;
	if(sb && sb->setscrollbar)
		sb->setscrollbar(sb, lp->lo,
			lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, lp->len);
}
void pl_drawlist(Panel *p){
	List *lp;
	lp=p->data;
	lp->listr=pl_box(p->b, p->r, UP);
	pl_liststrings(p, lp->lo, lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height,
		lp->listr);
}
int pl_hitlist(Panel *p, Mouse *m){
	int oldsel, hitme;
	Point ul, size;
	List *lp;
	lp=p->data;
	hitme=0;
	ul=p->r.min;
	size=subpt(p->r.max, p->r.min);
	pl_interior(p->state, &ul, &size);
	oldsel=lp->sel;
	if(m->buttons&OUT){
		p->state=UP;
		if(m->buttons&~OUT) lp->sel=-1;
	}
	else if(p->state==DOWN || m->buttons&7){
		lp->sel=(m->xy.y-ul.y)/font->height+lp->lo;
		if(m->buttons&7){
			lp->buttons=m->buttons;
			p->state=DOWN;
		}
		else{
			hitme=1;
			p->state=UP;
		}
	}
	if(oldsel!=lp->sel){
		pl_listsel(p, oldsel, 0);
		pl_listsel(p, lp->sel, 1);
	}
	if(hitme && 0<=lp->sel && lp->sel<lp->len && lp->hit)
		lp->hit(p, lp->buttons, lp->sel);
	return 0;
}
void pl_scrolllist(Panel *p, int dir, int buttons, int val, int len){
	Point ul, size;
	int nlist, oldlo, hi, nline, y;
	List *lp;
	Rectangle r;
	lp=p->data;
	ul=p->r.min;
	size=subpt(p->r.max, p->r.min);
	pl_interior(p->state, &ul, &size);
	nlist=size.y/font->height;
	oldlo=lp->lo;
	if(dir==VERT) switch(buttons){
	case 1: lp->lo-=nlist*val/len; break;
	case 2: lp->lo=lp->len*val/len; break;
	case 4:	lp->lo+=nlist*val/len; break;
	}
	if(lp->lo<0) lp->lo=0;
	if(lp->lo>=lp->len) lp->lo=lp->len-1;
	if(lp->lo==oldlo) return;
	p->scr.pos.y=lp->lo;
	r=lp->listr;
	nline=(r.max.y-r.min.y)/font->height;
	hi=lp->lo+nline;
	if(hi<=oldlo || lp->lo>=oldlo+nline){
		pl_box(p->b, r, PASSIVE);
		pl_liststrings(p, lp->lo, hi, r);
	}
	else if(lp->lo<oldlo){
		y=r.min.y+(oldlo-lp->lo)*font->height;
		pl_cpy(p->b, Pt(r.min.x, y), 
			Rect(r.min.x, r.min.y, r.max.x, r.min.y+(hi-oldlo)*font->height));
		r.max.y=y;
		pl_box(p->b, r, PASSIVE);
		pl_liststrings(p, lp->lo, oldlo, r);
	}
	else{
		pl_cpy(p->b, r.min, Rect(r.min.x, r.min.y+(lp->lo-oldlo)*font->height,
			r.max.x, r.max.y));
		r.min.y=r.min.y+(oldlo+nline-lp->lo)*font->height;
		pl_box(p->b, r, PASSIVE);
		pl_liststrings(p, oldlo+nline, hi, r);
	}
}
void pl_typelist(Panel *g, Rune c){
	USED(g, c);
}
Point pl_getsizelist(Panel *p, Point children){
	USED(children);
	return pl_boxsize(((List *)p->data)->minsize, p->state);
}
void pl_childspacelist(Panel *g, Point *ul, Point *size){
	USED(g, ul, size);
}
void plinitlist(Panel *v, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
	List *lp;
	int wid, max;
	char *str;
	lp=v->data;
	v->flags=flags|LEAF;
	v->state=UP;
	v->draw=pl_drawlist;
	v->hit=pl_hitlist;
	v->type=pl_typelist;
	v->getsize=pl_getsizelist;
	v->childspace=pl_childspacelist;
	lp->gen=gen;
	lp->hit=hit;
	max=0;
	for(lp->len=0;str=gen(v, lp->len);lp->len++){
		wid=stringwidth(font, str);
		if(wid>max) max=wid;
	}
	if(flags&(FILLX|EXPAND)){
		for(lp->len=0;gen(v, lp->len);lp->len++);
		lp->minsize=Pt(0, nlist*font->height);
	}
	else{
		max=0;
		for(lp->len=0;str=gen(v, lp->len);lp->len++){
			wid=stringwidth(font, str);
			if(wid>max) max=wid;
		}
		lp->minsize=Pt(max, nlist*font->height);
	}
	lp->sel=-1;
	lp->lo=0;
	v->scroll=pl_scrolllist;
	v->scr.pos=Pt(0,0);
	v->scr.size=Pt(0,lp->len);
	v->kind="list";
}
Panel *pllist(Panel *parent, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){
	Panel *v;
	v=pl_newpanel(parent, sizeof(List));
	plinitlist(v, flags, gen, nlist, hit);
	return v;
}
