/*
 * dml - Dialog Markup Language
 *
 * $Id: logic.c,v 1.4 2003/04/04 10:08:27 malekith Exp $
 * Author: Michal Moskal <malekith@pld-linux.org>
 * include COPYING-GNU
 */

#include "pi.h"
#include <unistd.h>
#include <string.h>

char *pi_title_string;

static void focus_move(struct pi_object *self, int delta)
{
	struct pi_object *p, *was;

	if (self->children == 0)
		return;

	was = self->active;

	do {
		if (delta > 0) {
			if (self->active) 
				self->active = self->active->next;
		} else {
			if (self->active == self->children) {
				for (p = self->active; p->next; p = p->next)
					/* nothing */ ;
			} else {
				for (p = self->children; p; p = p->next)
					if (p->next == self->active)
						break;
			}
			self->active = p;
		}
		if (self->active == 0)
			self->active = self->children;
	} while ((self->active->ghosted || !self->active->f->can_focus) && 
		 self->active != was);

	pi_draw(self);
}

/* ------------> pi_object */
static struct pi_funcs object_funcs = {
	0,
	(pi_handler_void)pi_object_draw,
	(pi_handler_int)pi_object_cc,
	(pi_handler_int)pi_object_key,
	(pi_handler_void)pi_object_kill,
	(pi_handler_rstring)pi_object_fetch,
};

const char *pi_object_fetch(struct pi_object *self)
{
	(void)self;
	return 0;
}

void pi_object_draw(struct pi_object *self)
{
	struct pi_object *p;

	if (self->parent == 0 && pi_title_string) {
		pi_goto(0, 0);
		pi_addstr_alt(pi_title_string, -1, 
			      pi_color_title1, pi_color_title2);
	}
		
	for (p = self->children; p; p = p->next)
		if (p != self->active)
			pi_draw(p);

	if (self->active)
		pi_draw(self->active);
}

void pi_object_cc(struct pi_object *self, int data)
{
	(void)self;
	(void)data;
}

void pi_object_kill(struct pi_object *self)
{
	(void)self;
}

void pi_object_key(struct pi_object *self, int key)
{
	if (self->active)
		switch (key) {
		case pi_key_right:
		case pi_key_down:
		case '\n':
		case '\t':
			focus_move(self, 1);
			return;
		case pi_key_left:
		case pi_key_up:
			focus_move(self, -1);
			return;
		}

	if (self->parent)
		self->parent->f->handle_key(self->parent, key);
}

struct pi_object *pi_new_object(int size)
{
	struct pi_object *r;

	if (size == 0)
		size = sizeof(struct pi_object);
	r = (struct pi_object*)xmalloc(size);
	memset(r, 0, size);
	r->f = &object_funcs;

	return r;
}

/* -------------------->  add/kill child */
void pi_add_child(struct pi_object *self, struct pi_object *o)
{
	struct pi_object *p;
	
	if (self->children == 0)
		self->children = o;
	else {
		for (p = self->children; ; p = p->next)
			if (p->next == 0) {
				p->next = o;
				break;
			}
	}

	if (self->active == 0 && o->f->can_focus && !o->ghosted)
		self->active = o;
		
	o->parent = self;
}

void pi_kill(struct pi_object *self)
{
	struct pi_object *p;
	
	while (self->children) 
		pi_kill(self->children);

	if (self == self->parent->children)
		self->parent->children = self->next;
	else {
		for (p = self->parent->children; p; p = p->next)
			if (p->next == self) { 
				p->next = self->next;
				break;
			}
	}
	
	self->f->kill(self);
	xfree(self);
}

/* ---------------> globals */
void pi_add_window(struct pi_window *p)
{
	pi_add_child(_pi_root_object, (struct pi_object*)p);
}

int pi_exec(struct pi_window *win)
{
	struct pi_object *old;
	
	if (win->parent == 0)
		pi_add_window(win);

	old = _pi_root_object->active;
	_pi_root_object->active = (struct pi_object*)win;
	
	pi_draw(_pi_root_object);
	pi_refresh();

	for (;;) {
		struct pi_object *p;
		int k;

		if (win->modal_result)
			break;

		k = pi_getch();
		if (k == 0) {
			usleep(30000);
			continue;
		}

		for (p = _pi_root_object; p->active; p = p->active)
			/* nothing */ ;
		p->f->handle_key(p, k);
		pi_refresh();

	}

	return win->modal_result;
}

void pi_loop()
{
	pi_draw(_pi_root_object);
	pi_refresh();

	for (;;) {
		struct pi_object *p;
		int k;

		k = pi_getch();
		if (k == 0) {
			usleep(30000);
			continue;
		}

		for (p = _pi_root_object; p->active; p = p->active)
			/* nothing */ ;
		p->f->handle_key(p, k);
		pi_refresh();
	}
}

void pi_repaint()
{
	pi_cls();
	pi_draw(_pi_root_object);
	pi_refresh();
}
