/***************************************************************************
    begin                : Tue Oct 10 2006
    copyright            : (C) 2006 - 2009 by Alper Akcan
    email                : alper.akcan@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2.1 of the  *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include "../lib/xynth_.h"
#include "widget.h"

int w_listbox_item_image_init (w_listbox_item_image_t **item_image, unsigned int style, unsigned int rotation, unsigned int nimgs, char **imgs)
{
	unsigned int i;
	w_listbox_item_image_t *ii;
	ii = (w_listbox_item_image_t *) s_malloc(sizeof(w_listbox_item_image_t));
	ii->style = style;
	ii->rotation = rotation;
	ii->nimages = nimgs;
	ii->images = (char **) s_malloc(sizeof(char *) * nimgs);
	for (i = 0; i < nimgs; i++) {
		ii->images[i] = strdup(imgs[i]);
	}
	*item_image = ii;
	return 0;
}

void w_listbox_item_image_uninit (w_listbox_item_image_t *item_image)
{
	unsigned int i;
	for (i = 0; i < item_image->nimages; i++) {
		s_free(item_image->images[i]);
	}
	s_free(item_image->images);
	s_free(item_image);
}

void w_listbox_item_data_set (w_listbox_item_t *listbox_item, void *data)
{
	listbox_item->data = data;
}

void w_listbox_item_name_set (w_object_t *listbox, w_listbox_item_t *listbox_item, char *name)
{
	s_free(listbox_item->name);
	listbox_item->name = strdup(name);
}

int w_listbox_item_init (w_object_t *listbox, w_listbox_item_t **listbox_item)
{
	int pos;
	w_listbox_t *lb;
	w_listbox_item_t *li;
	w_listbox_item_image_t *ii;
	lb = listbox->data[OBJECT_LISTBOX];
	li = (w_listbox_item_t *) s_malloc(sizeof(w_listbox_item_t));
	memset(li, 0, sizeof(w_listbox_item_t));
	w_textbox_init(listbox->window, &(li->textbox), listbox);
	w_textbox_set_size(li->textbox->object, lb->itemheight - 8);
	for (pos = 0; !s_list_eol(lb->item_images, pos); pos++) {
		ii = (w_listbox_item_image_t *) s_list_get(lb->item_images, pos);
		w_textbox_set_image(li->textbox->object, ii->style, ii->rotation, ii->nimages, ii->images);
	}
	w_object_show(li->textbox->object);
	*listbox_item = li;
	return 0;
}

void w_listbox_item_uninit (w_listbox_item_t *listbox_item)
{
	s_free(listbox_item->name);
	w_textbox_uninit(listbox_item->textbox->object);
	s_free(listbox_item);
}

int w_listbox_item_add (w_object_t *object, w_listbox_item_t *item)
{
	w_listbox_t *lb;
	w_listbox_item_t *active;
	lb = object->data[OBJECT_LISTBOX];
	active = s_list_get(lb->items, lb->active);
	s_list_add(lb->items, item, -1);
	if (active == NULL) {
		lb->active = 0;
	} else {
		lb->active = s_list_get_pos(lb->items, active);
	}
	lb->dirty = 1;
	w_object_draw(object);
	return 0;
}

int w_listbox_item_del (w_object_t *object, w_listbox_item_t *item)
{
	int p;
	w_listbox_t *lb;
	w_listbox_item_t *active;
	if (item == NULL) {
		return 0;
	}
	lb = object->data[OBJECT_LISTBOX];
	active = s_list_get(lb->items, lb->active);
	p = s_list_get_pos(lb->items, item);
	s_list_remove(lb->items, p);
	w_listbox_item_uninit(item);
	if (lb->active == p) {
		active = s_list_get(lb->items, p);
		if (active == NULL) {
			active = s_list_get(lb->items, p - 1);
		} else {
			lb->pactive = -1;
		}
	}
	if (active == NULL) {
		lb->active = 0;
	} else {
		lb->active = s_list_get_pos(lb->items, active);
	}
	if (lb->active < 0) {
		lb->active = 0;
	}
	lb->dirty = 1;
	w_object_draw(object);
	return 0;
}

int w_listbox_clear (w_object_t *object)
{
	w_listbox_t *lb;
	w_listbox_item_t *li;
	lb = object->data[OBJECT_LISTBOX];
	while (!s_list_eol(lb->items, 0)) {
		li = s_list_get(lb->items, 0);
		s_list_remove(lb->items, 0);
		w_listbox_item_uninit(li);
	}
	lb->dirty = 1;
	lb->height = 0;
	lb->yoffset = 0;
	lb->active = 0;
	lb->pactive = -1;
	w_object_draw(object);
	return 0;
}

w_listbox_item_t * w_listbox_item_active_get (w_object_t *object)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	return s_list_get(lb->items, lb->active);
}

int w_listbox_item_active_set (w_object_t *object, w_listbox_item_t *listbox_item)
{
	int active;
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	active = s_list_get_pos(lb->items, listbox_item);
	if (active < 0) {
		return -1;
	}
	lb->active = active;
	lb->dirty = 1;
	w_object_draw(object);
	return 0;
}

void w_listbox_slide (w_object_t *object, int vertical, int horizontal, int *ytotal, int *yoffset)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	lb->yoffset -= vertical;
	if (vertical > 0) {
		if (lb->active * lb->itemheight + lb->yoffset < 0) {
			lb->active += 1;
		}
	} else {
		if ((lb->active + 1) * lb->itemheight + lb->yoffset > lb->object->content->h) {
			lb->active -= 1;
		}
	}
	w_object_draw(object);
	(*ytotal) = lb->height;
	(*yoffset) = lb->yoffset;
}

void w_listbox_draw (w_object_t *object)
{
	int x;
	int y;
	int h;
	int w;
	int pos;
	w_listbox_t *lb;
	w_listbox_item_t *li;
	lb = object->data[OBJECT_LISTBOX];
	x = object->content->x;
	w = object->content->w;
	h = lb->itemheight;
	lb->height = lb->items->nb_elt * h;
	lb->yoffset = MAX(lb->yoffset, object->content->h - lb->height);
	lb->yoffset = MIN(0, lb->yoffset);
	y = object->content->y + lb->yoffset;
	if (object->surface->vbuf == NULL) {
		goto end;
	}
	if (lb->scrollbuffer) {
		if (((lb->active + 1) * h) + lb->yoffset > lb->object->content->h) {
			w_scrollbuffer_slide(lb->scrollbuffer, (((lb->active + 1) * h) + lb->yoffset) - lb->object->content->h, 0);
			return;
		} else if ((lb->active * h) + lb->yoffset < 0) {
			w_scrollbuffer_slide(lb->scrollbuffer, ((lb->active * h) + lb->yoffset), 0);
			return;
		} else if (lb->dirty) {
			lb->dirty = 0;
			w_scrollbuffer_slide(lb->scrollbuffer, 0, 0);
			return;
		}
	}
	w_frame_draw(object);
	for (pos = 0; !s_list_eol(lb->items, pos); pos++, y += h) {
		li = (w_listbox_item_t *) s_list_get(lb->items, pos);
		if (!(pos == lb->active || pos == lb->pactive) &&
		    (li->textbox->object->surface->buf->x == x &&
		     li->textbox->object->surface->buf->y == y &&
		     li->textbox->object->surface->buf->w == w &&
		     li->textbox->object->surface->buf->h == h)) {
#if 0
			printf("pos %d, active: %d, pactive: %d\n"
			       "%d %d %d %d, %d %d %d %d\n",
			       pos, lb->active, lb->pactive, x, y, w, h,
			       li->textbox->object->surface->buf->x, li->textbox->object->surface->buf->y,
			       li->textbox->object->surface->buf->w, li->textbox->object->surface->buf->h);
#endif
			continue;
		}
		if (pos == lb->active) {
			w_textbox_set_rgb(li->textbox->object, 0, 0, 0);
			w_textbox_set_style(li->textbox->object, lb->activeshape, lb->activeshadow);
		} else {
			w_textbox_set_rgb(li->textbox->object, 255, 255, 255);
			w_textbox_set_style(li->textbox->object, lb->inactiveshape, lb->inactiveshadow);
		}
		w_textbox_set_str(li->textbox->object, li->name);
		w_object_move_silent(li->textbox->object, x, y, w, h);
	}
	if (lb->pactive != lb->active) {
		if (lb->changed != NULL) {
			lb->changed(object, lb->active);
		}
		lb->pactive = lb->active;
	}
	/*
	 * TODO - calling object update causes infinite loop while WIDGET_OPTIMIZE_MEMORY == 3 is selected
	 */
	w_object_update(lb->object, lb->object->surface->win);
end:	return;
}

void w_listbox_geometry (w_object_t *object)
{
	w_frame_geometry(object);
	w_listbox_draw(object);
}

void w_listbox_event (w_object_t *object, s_event_t *event)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	if (event->type & EVENT_TYPE_KEYBOARD) {
		if (event->type & EVENT_TYPE_KEYBOARD_RELEASED) {
			if (event->keybd->keycode == KEYBOARD_BUTTON_UP) {
				lb->active = MAX(lb->active - 1, 0);
				w_listbox_draw(object);
			} else if (event->keybd->keycode == KEYBOARD_BUTTON_DOWN) {
				lb->active = MIN(lb->items->nb_elt - 1, lb->active + 1);
				lb->active = MAX(lb->active, 0);
				w_listbox_draw(object);
			}
		}
	}
}

void w_listbox_scrollbuffer_set (w_object_t *object, w_object_t *scrollbuffer)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	lb->scrollbuffer = scrollbuffer;
	w_scrollbuffer_set_slide(scrollbuffer, w_listbox_slide);
}

int w_listbox_set_changed (w_object_t *object, void (*changed) (w_object_t *object, int active))
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	lb->changed = changed;
	return 0;
}

int w_listbox_set_style (w_object_t *object, FRAME_SHAPE shape, FRAME_SHADOW shadow)
{
	return w_frame_set_style(object, shape, shadow);
}

int w_listbox_set_itemimage (w_object_t *object, unsigned int style, unsigned int rotation, unsigned int nimgs, char **imgs)
{
	w_listbox_t *lb;
	w_listbox_item_image_t *ii;
	lb = object->data[OBJECT_LISTBOX];
	w_listbox_item_image_init(&ii, style, rotation, nimgs, imgs);
	s_list_add(lb->item_images, ii, -1);
	return 0;
}

int w_listbox_set_active_style (w_object_t *object, unsigned int shape, unsigned int shadow)
{
	w_listbox_t *lb;
	w_listbox_item_t *li;
	lb = object->data[OBJECT_LISTBOX];
	lb->activeshape = shape;
	lb->activeshadow = shadow;
	li = w_listbox_item_active_get(object);
	if (li) {
		w_textbox_set_style(li->textbox->object, lb->activeshape, lb->activeshadow);
		w_object_draw(li->textbox->object);
	}
	return 0;
}

int w_listbox_set_inactive_style (w_object_t *object, unsigned int shape, unsigned int shadow)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	lb->inactiveshape = shape;
	lb->inactiveshadow = shadow;
	w_object_draw(object);
	return 0;
}

int w_listbox_set_item_height (w_object_t *object, int size)
{
	w_listbox_t *lb;
	lb = object->data[OBJECT_LISTBOX];
	lb->itemheight = size;
	w_object_draw(object);
	return 0;
}

int w_listbox_init (w_window_t *window, w_listbox_t **listbox, w_object_t *parent)
{
	w_listbox_t *lb;
	lb = (w_listbox_t *) s_malloc(sizeof(w_listbox_t));
	w_frame_init(window, &(lb->frame), FRAME_PANEL | FRAME_SUNKEN, parent);
	s_list_init(&(lb->items));
	s_list_init(&(lb->item_images));
	lb->dirty = 1;
	lb->height = 0;
	lb->yoffset = 0;
	lb->active = 0;
	lb->pactive = -1;
	lb->itemheight = 24;
	lb->changed = NULL;
	lb->activeshape = FRAME_PANEL;
	lb->activeshadow = FRAME_SUNKEN;
	lb->inactiveshape = FRAME_NOFRAME;
	lb->inactiveshadow = FRAME_RAISED;
	lb->scrollbuffer = NULL;
	lb->object = lb->frame->object;
	lb->object->type = OBJECT_LISTBOX;
	lb->object->draw = w_listbox_draw;
	lb->object->event = w_listbox_event;
	lb->object->geometry = w_listbox_geometry;
	lb->object->destroy = w_listbox_uninit;
	lb->object->data[OBJECT_LISTBOX] = lb;
	*listbox = lb;
	return 0;
}

void w_listbox_uninit (w_object_t *object)
{
	w_listbox_t *lb;
	w_listbox_item_t *li;
	w_listbox_item_image_t *ii;
	lb = object->data[OBJECT_LISTBOX];
	while (!s_list_eol(lb->items, 0)) {
		li = (w_listbox_item_t *) s_list_get(lb->items, 0);
		s_list_remove(lb->items, 0);
		w_listbox_item_uninit(li);
	}
	while (!s_list_eol(lb->item_images, 0)) {
		ii = (w_listbox_item_image_t *) s_list_get(lb->item_images, 0);
		s_list_remove(lb->item_images, 0);
		w_listbox_item_image_uninit(ii);
	}
	s_list_uninit(lb->item_images);
	s_list_uninit(lb->items);
	w_frame_uninit(object);
	s_free(lb);
}
