/*
 * This file is part of the UCB release of Plan 9. It is subject to the license
 * terms in the LICENSE file found in the top-level directory of this
 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
 * part of the UCB release of Plan 9, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms contained
 * in the LICENSE file.
 */

#include <u.h>
#include <lib9.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <control.h>
#include "group.h"

typedef struct Tab Tab;

struct Tab {
	Control;
	int		border;
	int		selected;
	int		separation;
	char		*_format;
	CImage	*bordercolor;
	CImage	*image;
	Control	*tabrow;
	Control	*tabstack;
	Control	*tabcolumn;
	int		ntabs;
	int		nbuttons;
	Control	**buttons;
};

enum{
	EAdd,
	EBorder,
	EBordercolor,
	EButton,
	EFocus,
	EFormat,
	EHide,
	EImage,
	ERect,
	EReveal,
	ESeparation,
	ESeparatorcolor,
	EShow,
	ESize,
	EValue,
};

static char *cmds[] = {
	[EAdd] =			"add",
	[EBorder] =		"border",
	[EBordercolor] =	"bordercolor",
	[EButton] =		"button",
	[EFocus] = 		"focus",
	[EFormat] = 		"format",
	[EHide] =			"hide",
	[EImage] =		"image",
	[ERect] =			"rect",
	[EReveal] =		"reveal",
	[ESeparation] =		"separation",
	[ESeparatorcolor] =	"separatorcolor",
	[EShow] =			"show",
	[ESize] =			"size",
	[EValue] =			"value",
};

static void
tabshow(Tab *t)
{
	int i;
	Rectangle r;
	Group *g;

	if (t->hidden)
		return;
	for(i=0; i<t->nbuttons; i++){
		_ctlprint(t->buttons[i], "value %d", (t->selected==i));
	}
	_ctlprint(t->tabstack, "reveal %d", t->selected);
	_ctlprint(t->tabcolumn, "show");
	if (t->selected < 0)
		return;
	g = (Group*)t->tabcolumn;
	if (g->nseparators == 0){
		return;
	}
	r = g->separators[0];
	r.min.x = t->buttons[t->selected]->rect.min.x;
	r.max.x = t->buttons[t->selected]->rect.max.x;
	draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
	flushimage(display, 1);
}

static void
tabsize(Control *c)
{
	Tab *t = (Tab*)c;
	if (t->tabcolumn && t->tabcolumn->setsize)
		t->tabcolumn->setsize((Control*)t->tabcolumn);
}

static void
tabctl(Control *c, CParse *cp)
{
	int cmd, i;
	Control *cbut, *cwin;
	Tab *t;
	Rectangle r;

	t = (Tab*)c;
	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
	switch(cmd){
	case EAdd:
		if ((cp->nargs & 1) == 0)
			ctlerror("%q: arg count: %s", t->name, cp->args[1]);
		for (i = 1; i < cp->nargs; i += 2){
			cbut = controlcalled(cp->args[i]);
			if (cbut == nil)
				ctlerror("%q: no such control: %s", t->name, cp->args[i]);
			cwin = controlcalled(cp->args[i+1]);
			if (cwin == nil)
				ctlerror("%q: no such control: %s", t->name, cp->args[i+1]);
			_ctladdgroup(t->tabrow, cbut);
			_ctlprint(t->tabstack, "add %q", cp->args[i+1]);
			_ctlprint(cbut, "format '%%s: %q button %%d'", t->name);
			controlwire(cbut, "event", t->controlset->ctl);
			t->buttons = ctlrealloc(t->buttons, (t->nbuttons+1)*sizeof(Control*));
			t->buttons[t->nbuttons] = cbut;
			t->nbuttons++;
			t->selected = -1;
		}
		_ctlprint(t->tabcolumn, "size");
		t->size = t->tabcolumn->size;
		break;
	case EBorder:
		_ctlargcount(t, cp, 2);
		if(cp->iargs[1] < 0)
			ctlerror("%q: bad border: %c", t->name, cp->str);
		t->border = cp->iargs[1];
		break;
	case EBordercolor:
		_ctlargcount(t, cp, 2);
		_setctlimage(t, &t->bordercolor, cp->args[1]);
		break;
	case EButton:
		_ctlargcount(t, cp, 2);
		if (cp->sender == nil)
			ctlerror("%q: senderless buttonevent: %q", t->name, cp->str);
		cbut = controlcalled(cp->sender);
		for(i=0; i<t->nbuttons; i++)
			if (cbut == t->buttons[i])
				break;
		if (i == t->nbuttons)
			ctlerror("%q: not my event: %q", t->name, cp->str);
		if(cp->iargs[1] == 0){
			/* button was turned off; turn it back on */
			_ctlprint(cbut, "value 1");
		}else{
			t->selected = i;
			if (t->_format)
				chanprint(t->event, t->_format, t->name, i);
			tabshow(t);
		}
		break;
	case EFocus:
		/* ignore focus change */
		break;
	case EFormat:
		_ctlargcount(t, cp, 2);
		t->_format = ctlstrdup(cp->args[1]);
		break;
	case EImage:
		_ctlargcount(t, cp, 2);
		_setctlimage(t, &t->image, cp->args[1]);
		break;
	case ESeparation:
		t->tabrow->ctl(t->tabrow, cp);
		t->tabcolumn->ctl(t->tabcolumn, cp);
		break;
	case ERect:
		_ctlargcount(t, cp, 5);
		r.min.x = cp->iargs[1];
		r.min.y = cp->iargs[2];
		r.max.x = cp->iargs[3];
		r.max.y = cp->iargs[4];
		if(Dx(r)<=0 || Dy(r)<=0)
			ctlerror("%q: bad rectangle: %s", t->name, cp->str);
		t->rect = r;
		r = insetrect(r, t->border);
		_ctlprint(t->tabcolumn, "rect %R", r);
		break;
	case EReveal:
		_ctlargcount(t, cp, 1);
	case EHide:
	case ESize:
		t->tabcolumn->ctl(t->tabcolumn, cp);
		break;
	case EShow:
		tabshow(t);
		break;
	case EValue:
		_ctlargcount(t, cp, 2);
		if (cp->iargs[1] < 0 || cp->iargs[1] >= t->nbuttons)
			ctlerror("%q: illegal value '%s'", t->name, cp->str);
		t->selected = cp->iargs[1];
		tabshow(t);
		break;
	default:
		ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
		break;
	}
}

static void
tabfree(Control*c)
{
	Tab *t = (Tab*)c;
	t->nbuttons = 0;
	free(t->buttons);
	t->buttons = 0;
}

static void
activatetab(Control *c, int act)
{
	Tab *t;

	t = (Tab*)c;
	if (act)
		activate(t->tabcolumn);
	else
		deactivate(t->tabcolumn);
}
	
Control *
createtab(Controlset *cs, char *name)
{
	char s[128];

	Tab *t;
	t = (Tab*)_createctl(cs, "tab", sizeof(Tab), name);
	t->selected = -1;
	t->nbuttons = 0;
	t->ctl = tabctl;
	t->mouse = nil;
	t->exit = tabfree;
	snprint(s, sizeof s, "_%s-row", name);
	t->tabrow = createrow(cs, s);
	snprint(s, sizeof s, "_%s-stack", name);
	t->tabstack = createstack(cs, s);
	snprint(s, sizeof s, "_%s-column", name);
	t->tabcolumn = createcolumn(cs, s);
	ctlprint(t->tabcolumn, "add %q %q", t->tabrow->name, t->tabstack->name);
	t->bordercolor = _getctlimage("black");
	t->image = _getctlimage("white");
	t->setsize = tabsize;
	t->activate = activatetab;
	return (Control*)t;
}
