/*
 *   MediaMVP Server Widget Library
 *
 *   (C) 2004 Dave Kelly
 *
 *   $Id: window.c,v 1.14 2004/08/30 11:21:05 ravelox Exp $
 *   $Date: 2004/08/30 11:21:05 $
 *
 *   Window handling
 *
 */

#ifdef TESTING
#warning "TESTING MACRO ENABLED"
#endif

#include "mvpwindow.h"


typedef struct {
    int x,y,z;
} pos_t;

typedef struct {
    int width, height;
    pos_t offset;
} dimension_t;

/* Definitions for event callbacks */
typedef struct {
	int               event;
    void             *cb_ptr;
    event_cb_t        event_callback;
} event_callback_t;

struct _window {
    char *name;
    int type; // If we need it
	int flags;
    pos_t pos; // Position within parent
    dimension_t dimension; // Size of window
    surface_t *draw_surface; // Surface to draw on
    surface_t *contents; // Surface to draw on
    window_t *parent; 
    window_t *first_child;
	window_t *child_focus;
    window_t *next_sibling;
    window_t *prev_sibling;
    void (*draw_func)(window_t *window); // User-defined function to draw window
	void (*delete_func)(window_t *window); // User defined data deletion
	int  (*event_func)(window_t *window, int keycode, void *param); // Widget event handler
	int num_callbacks;
	event_callback_t *callbacks;
    void *data; // Window-specific data
	void *appdata; // Application-specific data;
	attrib_fns_t *attrib_fns;
};

static int      window_get_callback_index(window_t *window, event_t event);
static void     delete_window_internal(window_t *window);
static void     window_draw_internal(window_t *window, int *dirty);
static void     window_internal_dump_window(window_t *window, int depth);
static void     window_blit(window_t *window);
static void		window_bring_forward(window_t *window);
static void		window_send_backward(window_t *window);

static window_t *window_get_focus_child(window_t *window)
{
	window_t *current = NULL;
	int window_count = 0;

	if(! window) {
		return NULL;
	}

	/* If there are no children */
	if(! window->first_child) {
		return NULL;
	}

	window_count++;
	/* Go to the end of the list */
	current = window->first_child;
	while(current && current->next_sibling) {
		window_count++;
		current = current->next_sibling;
	}

	/* Work backwards until we find a visible window */
	while(! window_is_visible(current) && current->prev_sibling) {
		window_count--;
		current = current->prev_sibling;
	}
	
	/* If there are no visible windows then */
	/* nothing has focus                    */
	return (window_count > 0 ? current : NULL);
}

/** \brief Set the fill colour of a window
 * 
 *  \param window Window handle to affect
 *  \param fill_col Colour to use
 */
void window_set_fill_col(window_t *window, uint32_t fill_col)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_fill_col) {
		window->attrib_fns->set_fill_col(window, fill_col);
	}
}

/** \brief Set the border colour of a window
 * 
 *  \param window Window handle to affect
 *  \param border_col Colour to use
 */
void window_set_border_col(window_t *window, uint32_t border_col)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_border_col) {
		window->attrib_fns->set_border_col(window, border_col);
	}
}

/** \brief Set the border size of a window
 * 
 *  \param window Window handle to affect
 *  \param border_size Size in pixels of the border
 */
void window_set_border_size(window_t *window, int border_size)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_border_size) {
		window->attrib_fns->set_border_size(window, border_size);
	}
}

/** \brief Set the font of a window
 * 
 *  \param window Window handle to affect
 *  \param font Font handle to use
 */
void window_set_font(window_t *window, font_t *font)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_font) {
		window->attrib_fns->set_font(window, font);
	}
}

/** \brief Set the foreground colour of a window
 * 
 *  \param window Window handle to affect
 *  \param fgcol Colour to use
 */
void window_set_fgcol(window_t *window, uint32_t fgcol)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_fgcol) {
		window->attrib_fns->set_fgcol(window, fgcol);
	}
}

/** \brief Set the background colour of a window
 * 
 *  \param window Window handle to affect
 *  \param bgcol Colour to use
 */
void window_set_bgcol(window_t *window, uint32_t bgcol)
{
	if(! window) {
		return;
	}

	if(window->attrib_fns && window->attrib_fns->set_bgcol) {
		window->attrib_fns->set_bgcol(window, bgcol);
	}
}

/** \brief Get the fill colour of a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current fill colour
 */
uint32_t window_get_fill_col(window_t *window)
{
	if(! window) {
		return 0;
	}

	if(window->attrib_fns && window->attrib_fns->get_fill_col) {
		return window->attrib_fns->get_fill_col(window);
	}

	return 0;
}

/** \brief Get the border colour of a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current border colour
 */
uint32_t window_get_border_col(window_t *window)
{
	if(! window) {
		return 0;
	}

	if(window->attrib_fns && window->attrib_fns->get_border_col) {
		return window->attrib_fns->get_border_col(window);
	}

	return 0;
}

/** \brief Get the border size of a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current border colour
 */
int window_get_border_size(window_t *window)
{
	if(! window) {
		return 0;
	}

	if(window->attrib_fns && window->attrib_fns->get_border_size) {
		return window->attrib_fns->get_border_size(window);
	}

	return 0;
}

/** \brief Get the current font for a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current font handle
 */
font_t *window_get_font(window_t *window)
{
	if(! window) {
		return NULL;
	}

	if(window->attrib_fns && window->attrib_fns->get_font) {
		return window->attrib_fns->get_font(window);
	}

	return NULL;
}

/** \brief Get the foreground colour of a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current foreground colour
 */
uint32_t window_get_fgcol(window_t *window)
{
	if(! window) {
		return 0;
	}

	if(window->attrib_fns && window->attrib_fns->get_fgcol) {
		return window->attrib_fns->get_fgcol(window);
	}

	return 0;
}

/** \brief Get the background colour of a window
 * 
 *  \param window Window handle to query
 *   
 *  \return Current background colour
 */
uint32_t window_get_bgcol(window_t *window)
{
	if(! window) {
		return 0;
	}

	if(window->attrib_fns && window->attrib_fns->get_bgcol) {
		return window->attrib_fns->get_bgcol(window);
	}

	return 0;
}

/** \brief Set common attribute functions for a window
 *
 *  \param window Window handle
 *  \param attrib_fns  Functions that control this window
 */
void window_set_attrib_fns(window_t *window, attrib_fns_t *attrib_fns)
{
	if(! window || ! attrib_fns) {
		return;
	}

	window->attrib_fns = attrib_fns;
}

/** \brief Get offset into event list for this window
 *
 *  \param window Window to query
 *  \param event Event to find callback for
 *
 *  \retval Offset into callback list
 *  \retval -1 No callback registered for this event
 */
static int window_get_callback_index(window_t *window, event_t event)
{
	int i = 0;

	if(! window) {
		return -1;
	}

	while ( i < window->num_callbacks ) {
		if ( window->callbacks[i].event == event ) {
			return i;
		}
		i++;
	}
	return -1;
}

/** \brief Register a callback for an event
 *
 *  \param window Window to register for
 *  \param event Event type to register for
 *  \param event_callback Callback to register for
 *  \param cb_ptr Generic pointer to callback with
 */
void window_register_callback(window_t *window, event_t event, event_cb_t event_callback, void *cb_ptr)
{
	int index = 0;

	if ( !window ) {
		return;
	}

	index = window_get_callback_index(window, event);

	if ( index == -1 ) {
		window->callbacks = (event_callback_t *)realloc(window->callbacks, (window->num_callbacks + 1) * sizeof(event_callback_t));
		index = window->num_callbacks++;
	}

	window->callbacks[index].event = event;
	window->callbacks[index].cb_ptr = cb_ptr;
	window->callbacks[index].event_callback = event_callback;
}

/** \internal
 *  \brief Create a new window type
 *
 *  \param parent Parent window
 *  \param type Type of the window
 *  \param x Placement on screen
 *  \param y Placement on screen
 *  \param width Width of the window
 *  \param height Height of the window
 *
 *  Attrib_fns should already be registered prior to calling this function
 */
window_t *new_window(window_t *parent, int type, int x, int y, int width, int height)
{
    window_t *window = (window_t *)malloc(sizeof(window_t));

    window->name = NULL;
    window->type = type;
	window->flags = FLAG_VISIBLE;
    window->pos.x = x;
    window->pos.y = y;
    window->pos.z = 0;
    window->dimension.width = width;
    window->dimension.height = height;
    window->dimension.offset.x = 0;
    window->dimension.offset.y = 0;
    window->draw_surface = NULL;
    window->contents = surface_alloc(width, height, 24);
    window->parent = parent;
    window->first_child = NULL;
	window->child_focus = NULL;
    window->next_sibling = NULL;
    window->prev_sibling = NULL;
    window->draw_func = NULL;
	window->delete_func = NULL;
	window->event_func = NULL;
    window->data = NULL;
	window->appdata = NULL;
	window->num_callbacks = 0;
	window->callbacks = NULL;
	window->attrib_fns = NULL;

    if(parent) {
		window_add_child(parent, window);
    }

    return window;
}

/** \brief Set the name of a window
 *
 *  \param window Window handle
 *  \param name Name of the window
 *
 */
void window_set_name(window_t *window, char *name)
{
    if(! window) {
		return;
    }
    
    if ( window->name ) {
        free(window->name);
    }

    window->name = (char *)strdup(name);
}

/** \brief Get the name of a window
 *
 *  \param window Window handle
 *  \return Name of the window
 *
 */
char *window_get_name(window_t *window)
{
    if(! window) {
		return NULL;
    }

    return window->name;
}

/** \brief Set the widget specific data value
 *
 *  \param window Window handle
 *  \param data Void pointer to store
 *
 *  This should only be used by widget implementations - applications
 *  should uses window_set_appdata()
 */
void window_set_data(window_t *window, void *data)
{
    if(! window) {
		return;
    }

    window->data = data;
	window_mark_dirty(window);
}

/** \brief Get the widget specific data value
 *
 *  \param window Window handle 
 *  \return Widget specific data value
 *
 *  This should only be used by widget implementations - applications
 *  should uses window_set_appdata()
 */
void *window_get_data(window_t *window)
{
    if(! window) {
		return NULL;
    }

    return window->data;
}

/** \brief Set the application specific data value
 *
 *  \param window Window handle
 *  \param data Void pointer to store
 */
void window_set_appdata(window_t *window, void *appdata)
{
	if(! window) {
		return;
	}

	window->appdata = appdata;
	window_mark_dirty(window);
}

/** \brief Get the application specific data value
 *
 *  \param window Window handle
 *  \return Application specific data value
 */
void *window_get_appdata(window_t *window)
{
	if(! window) {
		return NULL;
	}

	return window->appdata;
}

/** \brief Set the widget draw function
 *
 *  \param window Window handle
 *  \param draw_func Drawing function
 *
 *  This function should only be used by widgets,  window_register_callback()
 *  can be used for application specific drawing functionality
 */
void window_set_draw_func(window_t *window, void (*draw_func)(window_t *this))
{
    if(! window) {
		return;
    }

    window->draw_func = draw_func;
	window_mark_dirty(window);
}

/** \brief Set the widget delete function
 *
 *  \param window Window handle
 *  \param delete_func Delete function
 *
 *  This function should only be used by widgets,  window_register_callback()
 *  can be used for application specific delete functionality
 */
void window_set_delete_func(window_t *window, void (*delete_func)(window_t *this))
{
	if(! window) {
		return;
	}

	window->delete_func = delete_func;
}

/** \brief Set the widget event function
 *
 *  \param window Window handle
 *  \param delete_func Delete function
 *
 *  This function should only be used by widgets,  window_register_callback()
 *  can be used for application event handling
 */
void window_set_event_func(window_t *window, int (*event_func)(window_t *this, int keycode, void *param))
{
	if(! window) {
		return;
	}

	window->event_func = event_func;
}

/** \brief Get the window drawing surface
 *
 *  \param window Window handle
 *  \return Window draw surface
 */
surface_t *window_get_draw_surface(window_t *window)
{
    if(! window) {
		return NULL;
    }

    return window->draw_surface;
}

/** \brief Set the window drawing surface
 *
 *  \param window Window handle
 *  \param draw_surface Window draw surface
 */
void window_set_draw_surface(window_t *window, surface_t *draw_surface)
{
    if(! window) {
		return;
    }

    window->draw_surface = draw_surface;
}

/** \brief Set the window type
 *
 *  \param window Window handle
 *  \param type Window type
 */
void window_set_type(window_t *window, int type)
{
    if(! window) {
		return;
    }

    window->type = type;
}

/** \brief Get the window type
 *
 *  \param window Window handle
 *  \return Window type
 */
int window_get_type(window_t *window)
{
    if(! window) {
		return WINDOW_ROOT;
    }

    return window->type;
}

/** \brief Get the top left coordinate of a window
 *
 *  \param window Window handle
 *  \return Top left X coordinate
 */
int window_get_xpos(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->pos.x;
}

/** \brief Set the top left coordinate of a window
 *
 *  \param window Window handle
 *  \param x  Top left X coordinate
 */
void window_set_xpos(window_t *window, int x)
{
    if(! window) {
		return;
    }

    window->pos.x = x;
	window_mark_dirty(window);
	window_mark_dirty(window->parent);

    window_raise_event(window,event_position,NULL);
}

/** \brief Get the top left coordinate of a window
 *
 *  \param window Window handle
 *  \return Top left Y coordinate
 */
int window_get_ypos(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->pos.y;
}

/** \brief Set the top left coordinate of a window
 *
 *  \param window Window handle
 *  \param y Top left Y coordinate
 */
void window_set_ypos(window_t *window, int y)
{
    if(! window) {
		return;
    }

    window->pos.y = y;

	window_mark_dirty(window);
	window_mark_dirty(window->parent);

    window_raise_event(window,event_position,NULL);

}

/** \brief Set the top left coordinate of a window
 *
 *  \param window Window handle
 *  \param x Top left X coordinate
 *  \param y Top left Y coordinate
 */
void window_set_pos(window_t *window, int x, int y)
{
    if(! window) {
		return;
    }

    window->pos.x = x;
    window->pos.y = y;

	window_mark_dirty(window);
	window_mark_dirty(window->parent);

    window_raise_event(window,event_position,NULL);
}

/** \brief Get the height of a window
 *
 *  \param window Window handle
 *  \return Window height
 */
int window_get_height(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->dimension.height;
}

/** \brief Set the height of a window
 *
 *  \param window Window handle
 *  \param height Window height
 */
void window_set_height(window_t *window, int height)
{
    if(! window) {
		return;
    }

    window->dimension.height = height;
	if(window->contents) {
		surface_dealloc(window->contents);
	}
	window->contents = surface_alloc(window->dimension.width, window->dimension.height, 24);

	window_mark_dirty(window);
	window_mark_dirty(window->parent);

    window_raise_event(window,event_size,NULL);
}

/** \brief Get the width of a window
 *
 *  \param window Window handle
 *  \return Window width
 */
int window_get_width(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->dimension.width;
}

/** \brief Set the width of a window
 *
 *  \param window Window handle
 *  \param width Window width
 */
void window_set_width(window_t *window, int width)
{
    if(! window) {
		return;
    }

    window->dimension.width = width;

	if(window->contents) {
		surface_dealloc(window->contents);
	}
	window->contents = surface_alloc(window->dimension.width, window->dimension.height, 24);

	window_mark_dirty(window);
	window_mark_dirty(window->parent);
    window_raise_event(window,event_size,NULL);
}

/** \brief Set the size of a window
 *
 *  \param window Window handle
 *  \param width Window width
 *  \param height Window height
 */
void window_set_dimension(window_t *window, int width, int height)
{
    if(! window) {
		return;
    }

    window->dimension.width = width;
    window->dimension.height = height;

	if(window->contents) {
		surface_dealloc(window->contents);
	}
	window->contents = surface_alloc(window->dimension.width, window->dimension.height, 24);
	window_mark_dirty(window);
	window_mark_dirty(window->parent);
    window_raise_event(window,event_size,NULL);
}

/** \brief Get the offset of a subwindow
 *
 *  \param window Window handle
 *  \return X offset
 */
int window_get_xoffset(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->dimension.offset.x;
}

/** \brief Set the offset of a subwindow
 *
 *  \param window Window handle
 *  \param xoffset X offset
 */
void window_set_xoffset(window_t *window, int xoffset)
{
    if(! window) {
		return;
    }

    window->dimension.offset.x = xoffset;
	window_mark_dirty(window);
	window_mark_dirty(window->parent);
}

/** \brief Get the offset of a subwindow
 *
 *  \param window Window handle
 *  \return Y offset
 */
int window_get_yoffset(window_t *window)
{
    if(! window) {
		return 0;
    }

    return window->dimension.offset.y;
}

/** \brief Set the offset of a subwindow
 *
 *  \param window Window handle
 *  \param yoffset Y offset
 */
void window_set_yoffset(window_t *window, int yoffset)
{
    if(! window) {
		return;
    }

    window->dimension.offset.y = yoffset;
	window_mark_dirty(window);
	window_mark_dirty(window->parent);
}

/** \brief Set the offset of a subwindow
 *
 *  \param window Window handle
 *  \param xoffset X offset
 *  \param yoffset Y offset
 */
void window_set_offset(window_t *window, int xoffset, int yoffset)
{
    if(! window) {
		return;
    }

    window->dimension.offset.x = xoffset;
    window->dimension.offset.y = yoffset;

	window_mark_dirty(window);
	window_mark_dirty(window->parent);
}

/** \brief Bring a window to the front of the zorder
 *
 *  \param window Window handle
 *
 *  This will only work for children of the root window
 */
static void window_bring_forward(window_t *window)
{
	window_t *current_window;

	if( !window ) {
		return;
	}

	/* If this window isn't the child of root then drop out */
	if(window_get_type(window->parent) != WINDOW_ROOT) {
		return;
	}

	fprintf(stderr, "window_bring_forward(%s [%p])\n", window->name, window);

	/* Remove this window from the list */
	if(window->prev_sibling) {
		window->prev_sibling->next_sibling = window->next_sibling;
	} else {
		window->parent->first_child = window->next_sibling;
	}

	if(window->next_sibling) {
		window->next_sibling->prev_sibling = window->prev_sibling;
	}

	window->prev_sibling = NULL;
	window->next_sibling = NULL;

	/* Move the window to the end of the list */
	if(! window->parent->first_child) {
		window->parent->first_child = window;
	} else {
		if( window->parent->first_child != window ) {
			current_window = window->parent->first_child;
			while(current_window->next_sibling) {
				current_window = current_window->next_sibling;
			}
			current_window->next_sibling = window;
			window->prev_sibling = current_window;
		}
	}
}

/** \brief Move a window backwards in z order
 *
 *  \param window Window handle
 *
 *  This will only work for children of the root window
 */
static void window_send_backward(window_t *window)
{
	if(! window) {
		return;
	}

	if(window_get_type(window->parent) != WINDOW_ROOT) {
		return;
	}

	fprintf(stderr, "window_send_backward(%s [%p])\n", window->name, window);

	/* Remove this window from the list */
	if(window->prev_sibling) {
		window->prev_sibling->next_sibling = window->next_sibling;
	} else {
		window->parent->first_child = window->next_sibling;
	}

	if(window->next_sibling) {
		window->next_sibling->prev_sibling = window->prev_sibling;
	}

	window->prev_sibling = NULL;
	window->next_sibling = NULL;

	if(! window->parent->first_child) {
		window->parent->first_child = window;
	} else {
		if( window->parent->first_child != window) {
			window->next_sibling = window->parent->first_child;
			window->parent->first_child->prev_sibling = window;
			window->parent->first_child = window;
		}
	}
}

/** \brief Add a child window to a window
 *
 *  \param window Window handle
 *  \param child Child window handle to add
 */
void window_add_child(window_t *parent, window_t *child)
{
    window_t *current_window;

    if( ! child ) {
		return;
    }

    child->parent = parent;

	if(! parent) {
		return;
	}

    /* If there are no children */
    if(! parent->first_child ) {
		parent->first_child = child;
		window_mark_dirty(parent);
		goto end_add_child;
    } 

	current_window = parent->first_child;

	/* If the first child is higher in the zorder */
	if(current_window->pos.z >= child->pos.z) {
		parent->first_child = child;
		current_window->prev_sibling = child;
		child->next_sibling = current_window;
		child->prev_sibling = NULL;
		goto end_add_child;
	}

	while(current_window && current_window->next_sibling) {
		current_window = current_window->next_sibling;
		if(current_window->pos.z >= child->pos.z) {
			child->next_sibling = current_window;
			child->prev_sibling = current_window->prev_sibling;
			if(current_window->prev_sibling) {
				current_window->prev_sibling->next_sibling = child;
			}
			current_window->prev_sibling = child;
			goto end_add_child;
		}
	}

end_add_child:
	window_mark_dirty(parent);
}


/** \brief Delete a window 
 *
 *  \param window Window handle
 *
 *  All siblings are deleted
 */
static void delete_window_internal(window_t *window)
{
    if(! window) {
		return;
    }

    printf("[Internal] Deleting %p [%s] [type=%d]\n",window,window->name, window->type);

	window_dump_window(window);

    /* Mark the parent as dirty */
	window_mark_dirty(window->parent);

    /* Recursively delete all children */
    if(window->first_child) {
		fprintf(stderr, "Deleting children\n");
		delete_window_internal(window->first_child);
		window->first_child = NULL;
    }

    /* Recursively delete all siblings */
    if(window->next_sibling) {
		fprintf(stderr, "Deleting siblings\n");
		delete_window_internal(window->next_sibling);
		window->next_sibling = NULL;
    }

   

    /* Delete the surface */
    if(window->draw_surface) {
		surface_dealloc(window->draw_surface);
		window->draw_surface = NULL;
    }

    /* Delete the contents */
    if(window->contents) {
		surface_dealloc(window->contents);
		window->contents = NULL;
    }
  
    /* Delete any window-specific data */
    if ( window->data ) {
		if(window->delete_func) {
			window->delete_func(window);
			window->delete_func = NULL;
		} else {
			free(window->data);
			window->data = NULL;
		}
    }

    /* Send an event to the application */
    window_raise_event(window,event_delete,NULL);

    printf("[Internal] Finished - Deleting %p [%s] [type=%d]\n",window,window->name, window->type);

    if(window->name) {
		free(window->name);
		window->name = NULL;
    }

    /* Delete the window memory */
	memset(window, 0, sizeof(window_t));
    free(window);
}


/** \brief Delete a window 
 *
 *  \param window Window handle
 *
 *  Does not delete siblings.
 */
void delete_window(window_t *window)
{
    printf("Deleting %p [%s] [type=%d]\n",window,window->name, window->type);
	window_dump_window(window);

    printf("Prev sibling is %p next %p\n",window->prev_sibling,window->next_sibling);

    /* Mark the parent as dirty */
	window_mark_dirty(window->parent);

    if(window->first_child) {
		fprintf(stderr, "Deleting children\n");
		delete_window_internal(window->first_child);
		window->first_child = NULL;
    }

    /* Remove this window from the sibling list */
    if(! window->prev_sibling) {
		if(window->parent) {
			window->parent->first_child = window->next_sibling;
		}
	} else {
		window->prev_sibling->next_sibling = window->next_sibling;
        if ( window->prev_sibling->prev_sibling == window ) {
            window->prev_sibling->prev_sibling = NULL;
        }
	}

    if ( window->next_sibling && window->next_sibling->prev_sibling == window ) {
        window->next_sibling->prev_sibling = NULL;
    }

    /* Delete the surface */
    if(window->draw_surface) {
		surface_dealloc(window->draw_surface);
		window->draw_surface = NULL;
    }

    /* Delete the contents surface */
    if(window->contents) {
		surface_dealloc(window->contents);
		window->contents = NULL;
    }

    if ( window->data ) {
        if(window->delete_func) {
			window->delete_func(window);
			window->delete_func = NULL;
		} else {
			free(window->data);
			window->data = NULL;
		}
    }

    /* Send an event to the application */
    window_raise_event(window,event_delete,NULL);

    printf("Finished - Deleting %p [%s] [type=%d]\n",window,window->name, window->type);

    if(window->name) {
		free(window->name);
		window->name = NULL;
    }

    /* Delete the window memory */
	memset(window, 0, sizeof(window_t));
    free(window);
}


/** \brief Draw a window 
 *
 *  \param window Window handle
 *
 *  All siblings are drawn
 */
static void window_draw_internal(window_t *window, int *dirty)
{
    if(! window) {
		return;
    }

    /* Call the draw function for this window */
    if(window_is_dirty(window) && window->draw_func) {
		window->draw_func(window);
        window_raise_event(window,event_draw,NULL);
		window_mark_clean(window);
		*dirty +=1 ;
    } 

	if(window_is_visible(window))  {
		window_blit(window);

		/* Draw any children */
		if(window->first_child) {
			window_draw_internal(window->first_child, dirty);
		}
	}

    /* Draw the next sibling */
    if(window->next_sibling) {
		window_draw_internal(window->next_sibling, dirty);
    }

}


/** \brief Draw a window 
 *
 *  \param window Window handle
 *
 *  All siblings are not drawn
 */
int window_draw(window_t *window)
{
	int dirty = 0;

    if(! window) {
		return 0;
    }

    /* Call the draw function for this window */
    if(window_is_dirty(window) && window->draw_func) {
		window->draw_func(window);
        window_raise_event(window,event_draw,NULL);
		window_mark_clean(window);
		dirty++;
    }

	if(window_is_visible(window)) {
		window_blit(window);

		/* Draw any children */
		if(window->first_child) {
			window_draw_internal(window->first_child, &dirty);
		}
	}

	return dirty;
}

/** \brief Create a new root window
 *
 *  \param width Width of the root window
 *  \param height Height of the root window
 *  \param bpp Colour depth of the root window
 *
 *  All applications should call this initialliy and place any new
 *  windows inside. If bpp is 8 then a palettised colour scheme is
 *  used. If 24 then a full 24 bits of colour is available, though
 *  it may be slower.
 */
window_t *new_root(int width, int height, int bpp)
{
    window_t *root = new_window(NULL, WINDOW_ROOT ,0,0, width, height);

	window_set_name(root, "%internal% root");
    root->draw_surface = surface_alloc(width, height, bpp);

    return root;
}

#ifdef TESTING
static void window_internal_dump_window(window_t *window, int depth)
{
    int i;
    if(! window) {
	return;
    }

    fprintf(stderr, "|_%s (ptr=%p, type=%d, zorder=%d)\n", window->name, window, window->type, window->pos.z);

    /* Dump children */
    if(window->first_child) {
		for(i = 0 ; i < depth; i++)
		{
			fprintf(stderr, "|\t");
		}

		window_internal_dump_window(window->first_child, depth+1);
    }

    /* Dump siblings */
    if(window->next_sibling) {
		for(i = 0 ; i < depth-1; i++)
		{
			fprintf(stderr, "|\t");
		}

		window_internal_dump_window(window->next_sibling, depth);
    }
}

void window_dump_window(window_t *window)
{
    if(! window) {
		return;
    }

    fprintf(stderr, "--- WINDOW DUMP ---\n");
    fprintf(stderr, "%s (ptr=%p, type=%d, zorder=%d)\n", window->name, window, window->type, window->pos.z);

    /* Dump children */
    if(window->first_child) {
		window_internal_dump_window(window->first_child, 1);
    }
    fprintf(stderr, "-------------------\n");
}
#endif

/** \brief Blit a window
 *
 *  \param window Window to blit
 *
 *  This function will blit an individual surface for a window onto
 *  its parents surface - this is usually the root window
 *
 *  \sa new_root()
 */
static void window_blit(window_t *window)
{
    surface_t *surface;
    window_t *current;
    int delta_x = 0, delta_y = 0;
    int max_x = 0, max_y = 0;

    if(! window) {
        return;
    }

	Dprintf(DEBUG, "window_blit(%s)\n", window_get_name(window));

    /* Determine which surface to use */
    current = window;
    max_x = window->dimension.width;
    max_y = window->dimension.height;
    while( current->draw_surface == NULL && current->parent) {
        delta_x += current->pos.x;
        delta_y += current->pos.y;

		/*
        if ( current != window ) {
            if(max_x + delta_x > current->dimension.width ) {
                max_x = current->dimension.width - delta_x;
            }
            
            if(max_y + delta_y > current->dimension.height) {
                max_y = current->dimension.height - delta_y;
            }
        }
		*/

        current = current->parent;

    }

    surface = current->draw_surface;

    if(! surface) {
	    return;
    }
    surface_blit(surface,window->contents,delta_x,delta_y,max_x,max_y);
}

/** \brief Return the windows content surface
 * 
 *  \param window Window handle
 *  \return Content surface
 */
surface_t *window_get_contents(window_t *window)
{
	if(! window) {
		return NULL;
	}

	return window->contents;
}

/** \brief Draw a pixel on the windows content surface
 * 
 *  \param window Window handle
 *  \param x X coordinate of the pixel
 *  \param y Y coordinate of the pixel
 *  \param col Colour of the pixel
 */
void window_set_pixel(window_t *window, int x, int y, uint32_t col)
{
	if(! window) {
		return;
	}

	if(! window->contents) {
		return;
	}

	surface_set_pixel(window->contents, x, y, col);
}

/** \brief Clear the windows content surface
 *
 *  \param window Window handle
 *  \param col Colour to clear the display to
 */
void window_clear(window_t *window, uint32_t col)
{
	if(! window) {
		return;
	}

	if(! window->contents) {
		return;
	}

	surface_clear(window->contents, col);
}

/** \brief Mark a window dirty
 *
 *  \param window Window handle
 */
void window_mark_dirty(window_t *window)
{
	if(! window) {
		return;
	}

	window->flags |= FLAG_DIRTY;
}

/** \brief Mark a window clean
 *
 *  \param window Window handle
 */
void window_mark_clean(window_t *window)
{
	if(! window) {
		return;
	}

	window->flags &= ~FLAG_DIRTY;
}

/** \brief Return window dirty status
 *
 *  \param window Window handle
 *
 *  \retval 0 Not dirty
 *  \retval 1 Dirty
 */
int window_is_dirty(window_t *window)
{
	if(! window) {
		return 0;
	}

	return ( ( window->flags & FLAG_DIRTY ) == FLAG_DIRTY );
}

/** \brief Return window focus status
 *
 *  \param window Window handle
 *
 *  \retval 0 Not focussed
 *  \retval 1 Focussed
 */
int window_has_focus(window_t *window)
{
	if(! window) {
		return 0;
	}

	return ( (window->flags & FLAG_FOCUS ) == FLAG_FOCUS);
}

/** \brief Cause window to gain focus
 *
 *  \param window Window handle
 *
 *  This function will cause an event_focus events to be raised
 *	and will raise an event_blur event for the previous focus
 *	window
 */
void window_set_focus(window_t *window)
{
	if(! window) {
		return;
	}

	fprintf(stderr, "window_set_focus([%s] type=%d)\n", window->name, window->type);
	if(window->parent) {
		/* Raise blur event for the previous focus window */
		if(window->parent->child_focus) {
			window_send_backward(window->parent->child_focus);
			window->parent->child_focus->flags &= ~FLAG_FOCUS;
			window_mark_dirty(window->parent->child_focus);
			window_raise_event(window->parent->child_focus, event_blur, NULL);
		}
		window->parent->child_focus = window;
	}

	window_bring_forward(window);
	window->flags |= FLAG_FOCUS;
	window_mark_dirty(window);

	window_raise_event(window, event_focus, NULL);

	window_dump_window(window->parent);
}

/** \brief Set a windows modal state
 *
 *  \param window Window handle
 *  \param modal Modal state of a window (0/1)
 */
void window_set_modal(window_t *window, int modal)
{
	if(! window) {
		return;
	}

	if(! modal) {
		window->flags &= ~ FLAG_MODAL;
	} else {
		window->flags |= FLAG_MODAL;
	}
}

/** \brief Return window modal status
 *
 *  \param window Window handle
 *
 *  \retval 0 Not modal
 *  \retval 1 Modal
 */
int window_is_modal(window_t *window)
{
	if(! window) {
		return 0;
	}

	return ( (window->flags & FLAG_MODAL ) == FLAG_MODAL);
}

/** \brief Return windows root window
 *
 *  \param window Window handle
 *
 *  \return Window root
 */
window_t *window_get_root(window_t *window)
{
	if(! window) {
		return NULL;
	}

	while(window->parent) {
		window = window->parent;
	}

	return window;
}

/** \brief Return windows visible status
 *
 *  \param window Window handle
 *
 *  \retval 0 Not visible
 *  \retval 1 Visible
 */
int window_is_visible(window_t *window)
{
	if(! window) {
		return 0;
	}

	return ( (window->flags & FLAG_VISIBLE ) == FLAG_VISIBLE);
}

/** \brief Set a windows visible status
 *
 *  \param window Window handle
 *  \param modal Visible state of a window (0/1)
 */
void window_set_visible(window_t *window, int visible)
{
	if(! window) {
		return;
	}

	if(! visible) {
		window->flags &= ~ FLAG_VISIBLE;
	} else {
		window->flags |= FLAG_VISIBLE;
	}

	window_mark_dirty(window);
	window_mark_dirty(window->parent);
}

/** \brief Handle key presses passed on by the application
 *
 *  \param window Window handle
 *  \param keycode Key code to pass down
 *
 *  window is normally the root handle
 */
void window_dispatch_event(window_t *window, int keycode)
{
	int handled = 0;
	window_t *child_focus;

	fprintf(stderr, "window_dispatch_event\n");

	if(! window->child_focus) {
		child_focus = window_get_focus_child(window);
		window->child_focus = child_focus;
	}

	/* Call event handler for window */
	if(window->child_focus && window->child_focus->event_func != NULL ) {
		handled = window->child_focus->event_func(window->child_focus, keycode, window->child_focus->appdata);
	}

	fprintf(stderr, "Event Handled - %s\n", (handled == 0 ? "No" : "Yes"));

	/* Handled any global keycode */
	if(! handled) {
		switch(keycode) {
			case keyBlank:
				window_send_backward(window->child_focus);
				break;
			default:
				window_raise_event(window->child_focus, event_keypress, &keycode);
				break;
		}
	}
}

/** \brief Raise an event on a window
 *
 *  \param window Window handle
 *  \param event Event to raise
 *  \param param Callback parameter
 * 
 *  This function should be called by widgets when something interesting 
 *  happens
 */
void window_raise_event(window_t *window, event_t event, void *param)
{
	static int level = 0;
	int i;
	int index = 0;

	if(! window) {
		printf("Event [%d] raised on non-existent window\n", event);
		return;
	}

	for(i = 0 ; i < level ; i++) {
		printf("\t");
	}

    printf("%p [%s] Raising event [%d]\n",window,window->name,event);

	if( (index = window_get_callback_index(window, event)) == -1) {
		goto no_callback;
	}


	level++;
	window->callbacks[index].event_callback(window, event, window->callbacks[index].cb_ptr, param);
	level--;

no_callback:
	for(i = 0 ; i < level ; i++) {
		printf("\t");
	}
//    printf("%p [%s] Returned from event [%d]\n",window, window->name, event);
}
