/********************************************************
 ** Based on:                                          **
 ** F***ing Small Panel 0.7 Copyright (c) 2000-2001 By **
 ** Peter Zelezny <zed@linuxpower.org>                 **
 ********************************************************/

#include "wm.h"
#include "plugutil.h"

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#ifdef HAVE_XPM
#include <X11/xpm.h>
#include "icon.xpm"
#endif

#include "fspanel.h"
#include <string.h>
#include <stdio.h>

// bva ... for .so's
#include <dlfcn.h>
#include "config_tokens.h"

/* you can edit these */
#define MAX_DESKTOPS   40
#define MAX_TASK_WIDTH 145
#define ICONWIDTH 16
#define ICONHEIGHT 16
#define WINHEIGHT 20
#define WINWIDTH (scr_width)
#define FONT_NAME "-*-lucida*-b*-r-*-*-12-*-*"

/* don't edit these */
#define TEXTPAD 6
//#define left_arrow_x  25
//#define right_arrow_x 50
static char	*applets_path = NULL;


typedef struct appletStruct
{
    int x;
    int x2;

    int width;

    char name[250];
    char soName[250];

    fspanel_constants_t justification;

    int (*draw_hook)(int, void*);
    int (*press_hook)(int,int,int,void*);
    int (*release_hook)(int,int,int,void*);
    int (*notify_hook)( void* );
    applet_info_t (*get_info_hook) ();
    void (*set_info_hook)( applet_info_t );

} appletStruct_t;


appletStruct_t applet_info[MAX_APPLET];

int active_applet_count_g;

typedef struct taskbar
{
	Window win;
	task *task_list[MAX_DESKTOPS];
	int num_tasks;
	int my_desktop;
	unsigned int hidden:1;
	unsigned int at_top:1;
   client_t	*client;
}
taskbar;

/* decoration group for taskbar windows */
static dgroup_t	*taskbar_dgroup	= NULL;
screen_t *golem_screen;

char applet_order[MAX_APPLET][100];
char panel_base_dir[100];

// bva
taskbar *tb_g;

Display *dd;
Window root_win;
Pixmap generic_icon;
Pixmap generic_mask;
GC fore_gc;
XFontStruct *xfs;
int scr_screen;
int scr_depth;
int scr_width;
int scr_height;
int text_y;
int pager_width;
/*int time_width;*/

int get_applet_info_index( char *name );

unsigned short cols[] =
{
	0xd75c, 0xd75c, 0xd75c,		  /* 0. light gray */
	0xbefb, 0xbaea, 0xbefb,		  /* 1. mid gray */
	0xaefb, 0xaaea, 0xaefb,		  /* 2. dark gray */
	0xefbe, 0xefbe, 0xefbe,		  /* 3. white */
	0x8617, 0x8207, 0x8617,		  /* 4. darkest gray */
	0x0000, 0x0000, 0x0000		  /* 5. black */
};

#define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0]) / 3)

unsigned long palette[PALETTE_COUNT];

//#define ATOM_COUNT (sizeof (atom_names) / sizeof (atom_names[0]))
#define ATOM_COUNT 15

char *panel_atom_names[ATOM_COUNT] =
{
	"KWM_WIN_ICON",
	"_MOTIF_WM_HINTS",
	"_WIN_WORKSPACE",
	"_WIN_HINTS",
	"_WIN_LAYER",
	"_NET_CLIENT_LIST",
	"_WIN_CLIENT_LIST",
	"_WIN_WORKSPACE_COUNT",
	"_WIN_STATE",
	"WM_STATE",
    "KWM_NUMBER_OF_DESKTOPS", // bva
    "KWM_CURRENT_DESKTOP",
    "_WIN_WORKSPACE_NAMES",
    "_WIN_APP_STATE",
    "KWM_ACTIVE_WINDOW"
};


Atom panel_atoms[ATOM_COUNT];

#define atom_KWM_WIN_ICON panel_atoms[0]
#define atom__MOTIF_WM_HINTS panel_atoms[1]
#define atom__WIN_WORKSPACE panel_atoms[2]
#define atom__WIN_HINTS panel_atoms[3]
#define atom__WIN_LAYER panel_atoms[4]
#define atom__NET_CLIENT_LIST panel_atoms[5]
#define atom__WIN_CLIENT_LIST panel_atoms[6]
#define atom__WIN_WORKSPACE_COUNT panel_atoms[7]
#define atom__WIN_STATE panel_atoms[8]
#define atom_WM_STATE panel_atoms[9]
#define atom_KWM_NUMBER_OF_DESKTOPS panel_atoms[10]
#define atom_KWM_CURRENT_DESKTOP panel_atoms[11]
#define atom__WIN_WORKSPACE_NAMES panel_atoms[12]
#define atom__WIN_APP_STATE panel_atoms[13]
#define atom_KWM_ACTIVE_WINDOW panel_atoms[14]

void * get_prop_data (Window win, Atom prop, Atom type, int *items);
int find_desktop (Window win);

Atom wm_get_winlayer_atom( void )
{
    return atom__WIN_LAYER;
}
Atom wm_get_workspace_atom( void )
{
    return atom__WIN_WORKSPACE;
}

Atom wm_get_winstate_atom( void )
{
    return atom__WIN_STATE;
}
Atom wm_get_winhints_atom( void )
{
    return atom__WIN_HINTS;
}
Atom wm_get_mwmhints_atom( void )
{
    return atom__MOTIF_WM_HINTS;
}
int wm_get_current_desktop( void )
{
    return find_desktop( root_win );
}
unsigned long panel_get_palette( int index )
{
    return palette[index];
}

int wm_get_workspace_count( void )
{
    unsigned long *data = NULL;
    int retVal = 0;

    data = get_prop_data ( root_win, atom__WIN_WORKSPACE_COUNT, XA_CARDINAL, 0 );
    if( data == 0 )
    {
        // try another technique, if we can't get the current desktop.
        data = get_prop_data (root_win, atom_KWM_NUMBER_OF_DESKTOPS, atom_KWM_NUMBER_OF_DESKTOPS, 0);
        if( data != 0 )
        {
            retVal = *data;
            XFree(data);
        }
    }
    else
    {
        retVal = *data;
        XFree(data);
    }

    return retVal;
}

XFontStruct * x_get_fontstruct( void )
{
    return xfs;
}

Display *x_get_display( void )
{
    return display;
}
Window x_get_root_window( void )
{
    return root_win;
}
Window x_get_window( void )
{
    return tb_g->win;
}
GC x_get_foregroundGC( void )
{
    return fore_gc;
}
int x_get_screen( void )
{
    return scr_screen;
}

int x_get_screen_height( void )
{
    return scr_height;
}
int x_get_screen_width( void )
{
    return scr_width;
}

int panel_get_winheight( void )
{
    return WINHEIGHT;
}
int panel_get_winwidth( void )
{
    return WINWIDTH;
}
int panel_get_taskcount( void )
{
    return tb_g->num_tasks;
}
int panel_get_y_text_origin( void )
{
    return text_y;
}



void * get_prop_data (Window win, Atom prop, Atom type, int *items)
{
	Atom type_ret;
	int format_ret;
	unsigned long items_ret;
	unsigned long after_ret;
	unsigned char *prop_data;

	prop_data = 0;

    // bva - Working on this ...
	XGetWindowProperty ( display,
                         win,
                         prop,
                         0L, // 0,
                         0x7fffffff,
                         False,
                         type,
                         &type_ret,
                         &format_ret,
                         &items_ret,
                         &after_ret,
                         &prop_data );
	if (items)
    {
		*items = items_ret;
    }

	return prop_data;
}

void
set_foreground (int index)
{
	XSetForeground (display, fore_gc, palette[index]);
}

void draw_line ( int x, int y, int a, int b)
{
	XDrawLine (display, tb_g->win, fore_gc, x, y, a, b);
}

void fill_rect ( int x, int y, int a, int b)
{
	XFillRectangle (display, tb_g->win, fore_gc, x, y, a, b);
}

void scale_icon (task *tk)
{
	int xx, yy, x, y, w, h, d, bw;
	Pixmap pix, mk = None;
	XGCValues gcv;
	GC mgc;

	XGetGeometry (display, tk->icon, &pix, &x, &y, &w, &h, &bw, &d);
	pix = XCreatePixmap (display, tk->win, ICONWIDTH, ICONHEIGHT, scr_depth);

	if (tk->mask != None)
	{
		mk = XCreatePixmap (display, tk->win, ICONWIDTH, ICONHEIGHT, 1);
		gcv.subwindow_mode = IncludeInferiors;
		gcv.graphics_exposures = False;
		mgc = XCreateGC (display, mk, GCGraphicsExposures | GCSubwindowMode, &gcv);
	}

	set_foreground (3);

	/* this is my simple & dirty scaling routine */
	for (y = ICONHEIGHT - 1; y >= 0; y--)
	{
		yy = (y * h) / ICONHEIGHT;
		for (x = ICONWIDTH - 1; x >= 0; x--)
		{
			xx = (x * w) / ICONWIDTH;
			if (d != scr_depth)
				XCopyPlane (display, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y, 1);
			else
				XCopyArea (display, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y);
			if (mk != None)
				XCopyArea (display, tk->mask, mk, mgc, xx, yy, 1, 1, x, y);
		}
	}

	if (mk != None)
	{
		XFreeGC (display, mgc);
		tk->mask = mk;
	}

	tk->icon = pix;
}

void get_task_hinticon (task *tk)
{
	XWMHints *hin;

	tk->icon = None;
	tk->mask = None;

	hin = (XWMHints *) get_prop_data (tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
	if (hin)
	{
		if ((hin->flags & IconPixmapHint))
		{
			if ((hin->flags & IconMaskHint))
			{
				tk->mask = hin->icon_mask;
			}

			tk->icon = hin->icon_pixmap;
			tk->icon_copied = 1;
			scale_icon ( tk );
		}
		XFree (hin);
	}

	if (tk->icon == None)
	{
		tk->icon = generic_icon;
		tk->mask = generic_mask;
	}
}

void get_task_kdeicon (task *tk)
{
	unsigned long *data;

	data = get_prop_data (tk->win, atom_KWM_WIN_ICON, atom_KWM_WIN_ICON, 0);
	if (data)
	{
		tk->icon = data[0];
		tk->mask = data[1];
		XFree (data);
	}
}

int find_desktop( Window win )
{
	int desk = 0;
	unsigned long *data;

	// bva
    data = get_prop_data (win, atom__WIN_WORKSPACE, XA_CARDINAL, 0);
    //data = get_prop_data (win, atom_KWM_CURRENT_DESKTOP, XA_CARDINAL, 0);

	if (data)
	{
		desk = *data;
		XFree (data);
	}
	return desk;
}

int is_shaded (Window win)
{
	unsigned long *data;
	int ret = 0;

    data = get_prop_data ( win, atom__WIN_STATE, XA_CARDINAL, 0);
    if (data)
	{
		if ((*data) & WIN_STATE_SHADED )
        {
			ret = 1;
        }

		XFree (data);
	}

	return ret;
}

int is_hidden (Window win)
{
	unsigned long *data;
	int ret = 0;

	data = get_prop_data (win, atom__WIN_HINTS, XA_CARDINAL, 0);
	if (data)
	{
		if ((*data) & WIN_HINTS_SKIP_TASKBAR)
        {
			ret = 1;
        }

		XFree (data);
	}

    data = get_prop_data ( win, atom__WIN_STATE, XA_CARDINAL, 0);
    if (data)
	{
		if ((*data) & WIN_STATE_HIDDEN )
        {
			ret = 1;
        }

		XFree (data);
	}

	return ret;
}

int is_iconified (Window win)
{
	unsigned long *data;
	int ret = 0;

	data = get_prop_data (win, atom_WM_STATE, atom_WM_STATE, 0);
	if (data)
	{
		if (data[0] == IconicState)
			ret = 1;
		XFree (data);
	}
	return ret;
}

char *wm_get_current_workspace_name()
{
    char *chardata = NULL;
    int  workspace = find_desktop( root_win );
    char *retVal;
    int i, pos;

    chardata  = get_prop_data( root_win, atom__WIN_WORKSPACE_NAMES,  XA_STRING, 0);
    if( chardata  == 0 )
    {
        XFree (chardata); //might not work
        retVal = NULL;
    }
    else
    {
        //printf("curent desktop: %d\n", workspace );

        i = 0;
        pos = 0;
        while( i <= workspace )
        {
            //printf("position: %d\n", pos );
            //printf("space: %s\n", chardata + pos );
            if( i == workspace )
            {
                retVal = (char *) malloc( strlen( chardata + pos) + 1 );
                strcpy( retVal, chardata + pos );
                //printf("we want to return: %s\n", retVal );
            }

            pos += strlen( chardata + pos) + 1;
            i++;
        }

        XFree (chardata); //might not work

    }

    return retVal;
}

void add_task (taskbar * tb, Window win, int focus)
{
	task *tk, *list;

	/* is this window on a different desktop? */
	if( tb->my_desktop != find_desktop (win) ||
        is_hidden (win) )
    {
		return;
    }

	tk = calloc (1, sizeof (task));
	tk->win = win;
	tk->focused = focus;
	tk->name = get_prop_data (win, XA_WM_NAME, XA_STRING, 0);
	tk->iconified = is_iconified ( win );

    tk->shaded = is_shaded( win ); // bva .. not currently used, since we aren't
    // updated when a window is shaded. We'll always have to call the get props
    // to know for sure ...

    tk->marked = 0;

	get_task_kdeicon (tk);
	if (tk->icon == None)
    {
		get_task_hinticon (tk);
    }

	XSelectInput (display, win, PropertyChangeMask |
                                FocusChangeMask |
                                StructureNotifyMask );

	/* now append it to our linked list */
	tb->num_tasks++;

	list = tb->task_list[0]; // tb->task_list[tb->my_desktop];
	if( !list )
	{
		// tb->task_list[tb->my_desktop] = tk;
        tb->task_list[0] = tk;

		return;
	}

	while (1)
	{
		if (!list->next)
		{
			list->next = tk;
			return;
		}
		list = list->next;
	}
}

void gui_sync (void)
{
	XSync (display, False);
}

void set_prop (Window win, Atom at, long val)
{
	XChangeProperty (display, win, at, XA_CARDINAL, 32,
						  PropModeReplace, (unsigned char *) &val, 1);
}

taskbar * gui_create_taskbar (void)
{
	taskbar *tb;
	Window win;
	MWMHints mwm;
	XSizeHints size_hints;
	XWMHints wmhints;
	XSetWindowAttributes att;
	clientflags_t flags;

	att.background_pixel = palette[0];
	att.event_mask = ButtonPressMask | ExposureMask | ButtonReleaseMask;

	win = XCreateWindow ( /* display */ display,
                          /* parent  */ root_win,
                          /* x       */ 0,
                          /* y       */ scr_height - WINHEIGHT,
                          /* width   */ WINWIDTH,
                          /* height  */ WINHEIGHT,
                          /* border  */ 0,
                          /* depth   */ CopyFromParent,
                          /* class   */ InputOutput,
                          /* visual  */ CopyFromParent,
                          /*value mask*/ CWBackPixel | CWEventMask,
                          /* attribs */ &att);
// golem
    XSelectInput(display, win, ExposureMask | ButtonReleaseMask | ButtonPressMask);
// end golem


	/* don't let any windows cover fspanel */
	set_prop (win, atom__WIN_LAYER, 10);	/* WIN_LAYER_ABOVE_DOCK */

    // bva - TODO .. prevent the window from taking any focus or becoming
    //               the active window.
	set_prop ( win,
               atom__WIN_STATE,
               WIN_STATE_STICKY | WIN_STATE_FIXED_POSITION );

	set_prop (win, atom__WIN_HINTS, WIN_HINTS_SKIP_FOCUS |
                                    WIN_HINTS_SKIP_WINLIST |
                                    WIN_HINTS_SKIP_TASKBAR |
                                    WIN_HINTS_DO_NOT_COVER );

	/* borderless motif hint */
	memset (&mwm, 0, sizeof (mwm));
	mwm.flags = MWM_HINTS_DECORATIONS;
	XChangeProperty (display, win, atom__MOTIF_WM_HINTS,
							atom__MOTIF_WM_HINTS, 32, PropModeReplace,
							(unsigned char *) &mwm, sizeof (MWMHints) / 4);

	/* make sure the WM obays our window position */
	size_hints.flags = PPosition;
	/*XSetWMNormalHints (display, win, &size_hints);*/
	XChangeProperty (display, win, XA_WM_NORMAL_HINTS,
                     XA_WM_SIZE_HINTS, 32, PropModeReplace,
                     (unsigned char *) &size_hints, sizeof (XSizeHints) / 4);

	/* make our window unfocusable */
	wmhints.flags = InputHint;
	wmhints.input = 0;
	/*XSetWMHints (display, win, &wmhints);*/
	XChangeProperty (display, win, XA_WM_HINTS,
							XA_WM_HINTS, 32, PropModeReplace,
							(unsigned char *) &wmhints, sizeof (XWMHints) / 4);

	tb = calloc (1, sizeof (taskbar));
	tb->win = win;

// golem stuff
    /* create client_t for our taskbar */
	bzero(&flags, sizeof(clientflags_t));
	flags.internal = 1;
	flags.nofocus = 1;
	flags.noresize = 1;
	flags.noiconify = 1;
	flags.nodelete = 1;
	tb->client = client_add( golem_screen, tb->win, &flags, taskbar_dgroup);
// end golem stuff

	tb->client->state = NormalState;

	XMapWindow (display, tb->win);

    // golem
	XMapWindow(display, tb->client->frame);
    // end golem


	return tb;
}

void gui_init (void)
{
	XGCValues gcv;
	XColor xcl;
	int i, j;
	char *fontname;

	i = j = 0;
	do
	{
		xcl.red = cols[i];
		i++;
		xcl.green = cols[i];
		i++;
		xcl.blue = cols[i];
		i++;
		XAllocColor (display, DefaultColormap (display, scr_screen), &xcl);
		palette[j] = xcl.pixel;
		j++;
	}
	while (j < PALETTE_COUNT);

	fontname = FONT_NAME;
	do
	{
		xfs = XLoadQueryFont (display, fontname);
		fontname = "fixed";
	}
	while (!xfs);

	/*time_width = XTextWidth (xfs, "88:88", 5); */
#define time_width (35)
	text_y = xfs->ascent + ((WINHEIGHT - xfs->ascent) / 2);

	gcv.font = xfs->fid;
	gcv.graphics_exposures = False;
	fore_gc = XCreateGC (display, root_win, GCFont | GCGraphicsExposures, &gcv);

#ifdef HAVE_XPM
	XpmCreatePixmapFromData (display, root_win, icon_xpm, &generic_icon,
									 &generic_mask, NULL);
#else
	generic_icon = 0;
#endif
}

void gui_draw_string( char *str, int len, int x, int y )
{
    if( str != NULL && len < strlen( str ) + 1 )
    {

        XDrawString ( x_get_display(),
                      x_get_window(),
                      x_get_foregroundGC(),
                      x,
                      y,
                      str,
                      len );
    }
}


void gui_draw_vline( int x )
{
	set_foreground (4);
	draw_line ( x, 0, x, WINHEIGHT);
	set_foreground (3);
	draw_line ( x + 1, 0, x + 1, WINHEIGHT);
}

void draw_dot (Window win, int x, int y)
{
	set_foreground (4);
	XDrawPoint (display, win, fore_gc, x, y);
	set_foreground (3);
	XDrawPoint (display, win, fore_gc, x + 1, y + 1);
}

void draw_grill (Window win, int x)
{
	int y = 0;
	while (y < WINHEIGHT - 4)
	{
		y += 3;
		draw_dot (win, x + 3, y);
		draw_dot (win, x, y);
	}
}

int load_applet_so(void **handle, char *soFile)
{
   int _retVal = 1;
   char *error;

   // (*handle) = dlopen(soFile, RTLD_LAZY);
   (*handle) = dlopen(soFile, RTLD_GLOBAL | RTLD_LAZY);

   if (!(*handle))
   {
       error = dlerror();
       printf("%s\n", error );
      _retVal = 0;
   }

   return _retVal ;
}

int unload_applet_so( void *handle )
{
   return dlclose(handle);
}

void gui_draw_taskbar (taskbar * tb)
{
    int i, index;
    applet_info_t current_info, l_info, r_info;
    int right_x, left_x;
    int l_index, r_index;

	set_foreground (5);	/* black */

    left_x = 10;
    right_x = WINWIDTH - 10;

    // Let's fugger out where to place all the applets ...
    for( i = 0; i < active_applet_count_g; i++ )
    {
       index = get_applet_info_index( applet_order[i] );
       current_info = (*applet_info[index].get_info_hook)();

        switch( current_info.alignment )
        {
            case applet_left_align_c:
                applet_info[index].x = left_x;
                applet_info[index].x2 = left_x + current_info.width;
                left_x = left_x + current_info.width;
                break;
            case applet_centre_align_c:
                // TBD
                break;
            case applet_fill_align_c:
                applet_info[index].x = -1; // place holder
                applet_info[index].x2 = -1; // place holder
                break;
        }
    }

    for( i = active_applet_count_g - 1; i > -1; i-- )
    {
        index = get_applet_info_index( applet_order[i] );

        current_info = (*applet_info[index].get_info_hook)();

        switch( current_info.alignment )
        {
            case applet_right_align_c:
                applet_info[index].x = right_x - current_info.width;
                applet_info[index].x2 = right_x;
                right_x = right_x - current_info.width;
                break;
        }
    }

    // Now go back through and look for any "fill" applets
    for( i = 0; i < active_applet_count_g; i++ )
    {
        index = get_applet_info_index( applet_order[i] );
        current_info = (*applet_info[index].get_info_hook)();

        if( current_info.alignment == applet_fill_align_c )
        {
            // Cases for expand
            if( i - 1 > -1 )
            {
                l_index = get_applet_info_index( applet_order[i - 1] );
                l_info = (*applet_info[l_index].get_info_hook)();
            }
            else
            {
                l_info.alignment = -1;
            }

            if( i + 1 < active_applet_count_g )
            {
                r_index = get_applet_info_index( applet_order[i+1] );
                r_info = (*applet_info[r_index].get_info_hook)();
            }
            else
            {
                r_info.alignment = -1;
            }

            // a) Fill between Left and Right aligned
            if( l_info.alignment == applet_left_align_c &&
                r_info.alignment == applet_right_align_c )
            {
                applet_info[index].x  = applet_info[l_index].x2;
                applet_info[index].x2 = applet_info[r_index].x;

                current_info.width = applet_info[index].x2 - applet_info[index].x;
                (*applet_info[index].set_info_hook)( current_info );
            }

            // b) Fill between Left Edge and Right aligned
            if( l_info.alignment == -1 &&
                r_info.alignment == applet_right_align_c )
            {
                printf("left edge and right aligned case\n");

                applet_info[index].x  = left_x;
                applet_info[index].x2 = applet_info[r_index].x;
                current_info.width = applet_info[index].x2 - applet_info[index].x;

                printf("left ends at: %d\n", applet_info[index].x );
                printf("right starts at: %d\n", applet_info[index].x2);

                (*applet_info[index].set_info_hook)( current_info );

            }

            // c) Fill between Left aligned and Right Edge
            if( l_info.alignment == applet_left_align_c &&
                r_info.alignment == -1 )
            {

            }

        }
    }

    printf("drawing tasks for desktop: %d\n", tb->my_desktop );

    for( i = 0; i < active_applet_count_g; i++ )
    {
        index = get_applet_info_index( applet_order[i] );
        (*applet_info[index].draw_hook)( applet_info[index].x,
                                         //(void *) tb_g->task_list[tb->my_desktop] );
                                         (void *) tb_g->task_list[0] );
    }


    draw_grill (tb->win, 2);
	draw_grill (tb->win, WINWIDTH - 6);
}

task * find_task (taskbar * tb, Window win)
{
//	task *list = tb->task_list[tb->my_desktop];
    task *list = tb->task_list[0];
	while (list)
	{
		if (list->win == win)
			return list;
		list = list->next;
	}
	return 0;
}

void del_task (taskbar * tb, Window win)
{
//	task *next, *prev = 0, *list = tb->task_list[tb->my_desktop];
    task *next, *prev = 0, *list = tb->task_list[0];

	while (list)
	{
		next = list->next;
		if (list->win == win)
		{
			/* unlink and free this task */
			tb->num_tasks--;
			if (list->icon_copied)
			{
				XFreePixmap (display, list->icon);
				if (list->mask != None)
					XFreePixmap (display, list->mask);
			}
			if (list->name)
				XFree (list->name);
			free (list);
			if (prev == 0)
				//tb->task_list[tb->my_desktop] = next;
                tb->task_list[0] = next;
			else
				prev->next = next;
			return;
		}
		prev = list;
		list = next;
	}
}

void taskbar_read_clientlist (taskbar * tb)
{
	Window *win, focus_win;
	int num, i, rev, desk, new_desk = 0;
	task *list, *next;

	desk = find_desktop( root_win );

    printf("bva ------------ current desktop is: %d --------------------\n", desk );

	if (desk != tb->my_desktop)
	{
		new_desk = 1;
        // bva - jan 09. the KWM desktop atoms are 1 based, but the
        //                _WIN ones are 0 based. So when KWM says we are
        //               on desktop 2 that is fine, but if we switch to
        //               that desktop + 1 (3), we'll actually end up at
        //               desktop 4, since the _WIN ones are used for switching.

		tb->my_desktop = desk;
	}

	XGetInputFocus (display, &focus_win, &rev);

	/* try unified window spec first */
	win = get_prop_data (root_win, atom__NET_CLIENT_LIST, XA_CARDINAL, &num);
	if (!win)
	{
		/* failed, let's try gnome */
       win = get_prop_data (root_win, atom__WIN_CLIENT_LIST, XA_CARDINAL, &num);

		if (!win)
        {
			return;
        }
	}

	/* remove windows that aren't in the _WIN_CLIENT_LIST anymore */
	//list = tb->task_list[tb->my_desktop];
    list = tb->task_list[0];
	while( list )
	{
		list->focused = (focus_win == list->win);
		next = list->next;

		if (!new_desk)
			for (i = num - 1; i >= 0; i--)
				if (list->win == win[i])
					goto dontdel;
        del_task (tb, list->win);
dontdel:

		list = next;
	}

	/* add any new windows */
	for (i = 0; i < num; i++)
	{
		if ( !find_task (tb, win[i]) )
        {
            printf("adding a task in read_client_list\n");
			add_task (tb, win[i], (win[i] == focus_win) );
        }
	}

	XFree (win);
}

void move_taskbar (taskbar * tb)
{
	int x, y;

	x = y = 0;

	if (tb->hidden)
    {
		x = WINWIDTH - TEXTPAD;
    }

	if (!tb->at_top)
    {
		y = scr_height - WINHEIGHT;
    }

	XMoveWindow (display, tb->win, x, y);
}

void wm_switch_desktop ( int target )
{
	XClientMessageEvent xev;
	unsigned long *data;

	// bva:
    //data = get_prop_data (root_win, atom__WIN_WORKSPACE_COUNT, XA_CARDINAL, 0);
    data = get_prop_data (root_win, atom_KWM_NUMBER_OF_DESKTOPS, atom_KWM_NUMBER_OF_DESKTOPS, 0);

	if (data)
	{
		register unsigned long max_desks = *data;

		XFree (data);
        if ( target < 0 || target > max_desks )
        {
            return;
        }
	}

	xev.type = ClientMessage;
	xev.window = root_win;
	xev.message_type = atom__WIN_WORKSPACE;
	xev.format = 32;
	xev.data.l[0] = target;
	XSendEvent (display, root_win, False, SubstructureNotifyMask, (XEvent *) &xev);
}

void handle_release( int button, int x, int y)
{
    int i, index;

    for( i = 0; i < active_applet_count_g; i++ )
    {
        index = get_applet_info_index( applet_order[i] );

        if( x >= applet_info[index].x && x <= applet_info[index].x2 )
        {
            (*applet_info[index].release_hook)(x, y, button, 0);
        }
    }

}

void handle_press( int button, int abs_x, int abs_y, int x, int y )
{
    int i, index;


    printf("press -> abs(%d,%d), relative (%d,%d)\n", abs_x, abs_y, x ,y );

    if( (tb_g->at_top == 0 && abs_y < scr_height - WINHEIGHT) ||
        (tb_g->at_top == 1 && abs_y > WINHEIGHT) )
    {
        printf("=========== press for some application not in the panel ==============\n");
    }
    else
    {
        // for one of our in-panel applets.

        for( i = 0; i < active_applet_count_g; i++ )
        {
            index = get_applet_info_index( applet_order[i] );

            if( x >= applet_info[index].x && x <= applet_info[index].x2 )
            {
                // we have a press for a particular applet ...
                (*applet_info[index].press_hook)( x - applet_info[index].x,
                                                  y,
                                                  button,
                                                  //(void*) tb_g->task_list[tb_g->my_desktop] );
                                                  (void*) tb_g->task_list[0] );
            }
        }

        /* clicked left grill */
        if (x < 6)
        {
            if (tb_g->hidden)
            {
                tb_g->hidden = 0;
            }
            else
            {
                tb_g->at_top = !tb_g->at_top;
            }
            move_taskbar (tb_g);
            return;
        }

        /* clicked right grill */
        if (x + TEXTPAD > WINWIDTH)
        {
            tb_g->hidden = !tb_g->hidden;
            move_taskbar (tb_g);
            return;
        }


    }

}

void handle_focusin (taskbar * tb, Window win)
{
	task *tk;
    int i, index;

//	tk = tb->task_list[tb->my_desktop];
    tk = tb->task_list[0];

	while (tk)
	{
		if ( tk->focused )
		{
			if (tk->win != win)
			{
				tk->focused = 0;
				//gui_draw_task( tk );
                for( i = 0; i < active_applet_count_g; i++ )
                {
                    index = get_applet_info_index( applet_order[i] );
                    (*applet_info[index].notify_hook)( tk );
                }

			}
		}
        else
		{
			if ( tk->win == win )
			{
				tk->focused = 1;
                for( i = 0; i < active_applet_count_g; i++ )
                {
                    index = get_applet_info_index( applet_order[i] );
                    (*applet_info[index].notify_hook)( tk );
                }
				//gui_draw_task( tk );
			}
		}
		tk = tk->next;
	}
}

void handle_propertynotify (taskbar * tb, Window win, Atom at)
{
	task *tk;
    int i, index;
    unsigned long *data;

    // printf("bva - handle_propertynotify\n" );

	if( win == root_win )
	{
        //printf("bva - handle_propertynotify atom: hex: %x dec: %d\n", at, at );
        printf("bva - handle_propertynotify -> atom name is: %s\n", XGetAtomName( display, at ) );

        // added by bva ...
        if ( at == atom_KWM_ACTIVE_WINDOW  )
        {
            data = get_prop_data ( win, atom_KWM_ACTIVE_WINDOW, atom_KWM_ACTIVE_WINDOW, 0);

            if( data && *data == tb->win )
            {
                printf("\t*** the taskbar thinks it is active\n");
            }
            if( data && *data ==  root_win )
            {
                printf("\t*** the root window thinks it is active\n");
            }

            // If we found an active window and it isn't our taskbar, then we have
            // some possible work.
            if ( data && *data != tb->win )
            {
                tk = find_task (tb, *data);
                if( tk == 0 )
                {
                    // no task ... let's assume it was hidden and add the task !
                    // **** Problem .. un-iconfying a task makes this fire ... it shouldn't *****/

                    printf("\tbva - a task is being unhidden\n");
                    taskbar_read_clientlist (tb);
                    gui_draw_taskbar (tb);
                }
            }
        }


		if ( at == atom__NET_CLIENT_LIST ||
             at == atom__WIN_CLIENT_LIST ||
             at == atom__WIN_WORKSPACE)
		{
			taskbar_read_clientlist (tb);
			gui_draw_taskbar (tb);
		}

		return;
	}

	tk = find_task (tb, win);
	if (!tk)
    {
		return;
    }

	if (at == XA_WM_NAME)
	{
        printf("bva - handle_propertynotify ... at == XA_WM_NAME\n");
		/* window's title changed */
		if (tk->name)
			XFree (tk->name);
		tk->name = get_prop_data (tk->win, XA_WM_NAME, XA_STRING, 0);
        for( i = 0; i < active_applet_count_g; i++ )
        {
            index = get_applet_info_index( applet_order[i] );
            (*applet_info[index].notify_hook)( tk );
        }
		//gui_draw_task ( tk );
	}
    else if (at == atom_WM_STATE)
	{
        printf("bva: handle_propertynotify -> at == atom_WM_STATE\n");

		/* iconified state changed? */
		if (is_iconified( tk->win ) != tk->iconified)
		{
            printf("bva --- tasks icon status changed....\n");
            if( is_hidden( tk->win ) )
            {
                // this case is added by bva ..
                del_task( tb, tk->win );
                //taskbar_read_clientlist (tb);
                gui_draw_taskbar (tb);
            }
            else
            {

                tk->iconified = !tk->iconified;
                for( i = 0; i < active_applet_count_g; i++ )
                {
                    index = get_applet_info_index( applet_order[i] );
                    (*applet_info[index].notify_hook)( tk );
                }
                //gui_draw_task ( tk );
            }
		}
        else if( is_shaded( tk->win ) )
        {
            printf("that is a shaded window\n");
        }
	}
    else if (at == XA_WM_HINTS)
	{
        printf("bva - handle_propertynotify ... at == XA_WM_HINTS\n" );
		/* some windows set their WM_HINTS icon after mapping */
		if (tk->icon == generic_icon)
		{
			get_task_hinticon (tk);
            for( i = 0; i < active_applet_count_g; i++ )
            {
                index = get_applet_info_index( applet_order[i] );
                (*applet_info[index].notify_hook)( tk );
            }
			//gui_draw_task ( tk );
		}
	}
}


void handle_error (Display * d, XErrorEvent * ev)
{
}


void init_applets()
{
    int i, index;
    void *soHandle;
    applet_info_t info;

    for( i = 0; i < active_applet_count_g; i++ )
    {
        // get the first!
        index = get_applet_info_index( applet_order[i] );

        if( index != -1 )
        {
            applet_info[index].x = 0;
            applet_info[index].x2 = 0;

            printf("trying to load: %s\n", applet_info[index].soName );
            if( load_applet_so( &soHandle, applet_info[index].soName ) )
            {
                // TODO: If we can't find a hook, set it to NULL and just skip it
                //       when time to call it comes ... that way I won't have to implment
                //       as many empty functions.
                applet_info[index].draw_hook = dlsym( soHandle, "gui_draw" );
                applet_info[index].press_hook = dlsym( soHandle, "gui_button_press" );
                applet_info[index].get_info_hook = dlsym( soHandle, "get_applet_info" );
                applet_info[index].set_info_hook = dlsym( soHandle, "set_applet_info" );
                applet_info[index].notify_hook = dlsym( soHandle, "event_notification" );
                applet_info[index].release_hook = dlsym( soHandle, "gui_button_release" );

                // setup it's alignment
                info = (*applet_info[index].get_info_hook)();
                info.alignment = applet_info[index].justification;

                // -1 signalls the applet to set it's own width ..
                info.width = applet_info[index].width;
                printf("blah\n");
                // the call right here is messing up other golem plugins ... hmmm
                (*applet_info[index].set_info_hook)( info );
                printf("unblah\n");
            }
            else
            {
               printf("Error. Couldn't load the applet\n");
            }
        }
        else
        {
            printf("ERROR: Couldn't find applet: %s\n", applet_order[i] );
            exit(1);
        }
    }

    printf("applets initialized ....\n");
}

int get_applet_info_index( char *name )
{
    int retVal;
    int done;
    int search;

    // NOTE: This could be a function
    done = 0;
    search = 0;
    while( !done && search < MAX_APPLET )
    {
        if( strcmp( applet_info[search].name, name ) == 0 )
        {
            done = 1;
            retVal = search;
        }
        search++;
    }

    if( done )
    {
        return retVal;
    }
    else
    {
        return -1;
    }
}


/* External References into Lex */
extern FILE *yyin;
char   yytext[100];
extern int yylen;
int yylex();

void readRcFile( char *filename )
{
   int done = 0;
   int tokenVal;
   int index = 0;
   int count = 0;
   char temp[100];
   int found;
   int width;

   printf("===== readRcFile: %s\n", filename );

   /* Open The Configuration File and assign it to yyin
      so that lex will parse		     */
   yyin = fopen( filename, "r" );
   if( yyin == NULL )
   {
       fprintf( stderr, "Error: Cannot Find %s\n", filename );
       exit( 1 );
   }

   /* Now read in the configuration file and fill the data structure */
   /* First we must search for the named configuration */
   count = 0;
   while( !done )
   {
       tokenVal = yylex();

       printf("read: %d\n", tokenVal );

       if( tokenVal == ID )
       {
          //printf("ID --> %s\n", yytext );
          if( strcmp( yytext, "panel" ) == 0 )
          {
             printf("\tprocessing an panel directive\n" );
             if( yylex() == LBLOCK )
             {
                // path
                if( yylex() == ID )
                {
                   //  printf("applets_path .. %s\n", yytext );
                   //strcpy( applets_path, yytext );
                }

                if( yylex() != RBLOCK )
                {
                   printf( "Syntax error 1\n" );
                   exit(1);
                }
             }
             else
             {
                printf("Syntax error 2\n" );
                exit(1);
             }
          }

           if( strcmp( yytext, "applet" ) == 0 )
           {
               // applet config section ...
               if( yylex() == LBLOCK )
               {
                   // Applet Name
                   if( yylex() == ID )
                   {
                       strcpy( applet_info[count].name, yytext );
                   }
                   if( yylex() == COMMA )
                   {
                       if( yylex() == ID )
                       {
                           //strcpy( applet_info[count].soName, yytext );
                          sprintf( applet_info[count].soName, "%s/%s",
                                   applets_path, yytext );
                       }
                       else
                       {
                           printf("Syntax error\n");
                           exit(1);
                       }
                   }
                   else
                   {
                       printf("Syntax error\n");
                       exit(1);
                   }
                   if( yylex() != RBLOCK )
                   {
                       printf("Syntax error\n");
                       exit(1);
                   }

                   count++;
               }
               else
               {
                   printf("Syntax error\n");
                   exit(1);
               }
           }

           // ------------------------------------------------------------
           // count is now equal to the number of applet's we've found ...
           // ------------------------------------------------------------


           if( strcmp( yytext, "applet_order" ) == 0 )
           {
               // Brace
               if( yylex() == LBLOCK )
               {
                   // series of numbers separated by commas
                   while( yylex() == ID )
                   {
                       // this is the applet name.
                       strcpy( temp, yytext );

                       if( yylex() == LBRACK )
                       {
                           if( yylex() == ID )
                           {
                               // justification
                               // We now have an order and justification

                               // NOTE: This could be a function
                               found = get_applet_info_index( temp );

                               strcpy( applet_order[index], temp );
                               active_applet_count_g = index + 1;
                               index++;

                               printf("adding: %s .. justification: %s\n", temp, yytext );

                               if( found != -1 )
                               {
                                   if( strcmp( "left", yytext ) == 0 )
                                   {
                                       applet_info[found].justification = applet_left_align_c;
                                   }
                                   if( strcmp( "right", yytext ) == 0 )
                                   {
                                       applet_info[found].justification = applet_right_align_c;
                                   }
                                   if( strcmp( "fill", yytext ) == 0 )
                                   {
                                       applet_info[found].justification = applet_fill_align_c;
                                   }
                               }

                               // In case the optional width isn't present, set the width to -1
                               applet_info[found].width = -1;

                               tokenVal = yylex();
                               if( tokenVal == COMMA )
                               {
                                   if( yylex() == NUMBER )
                                   {
                                       width = atoi( yytext );

                                       applet_info[found].width = width;

                                       tokenVal = yylex();
                                   }
                               }
                           }

                           if( tokenVal != RBRACK )
                           {
                               printf("Syntax error\n");
                               exit(1);
                           }
                       }

                       tokenVal = yylex();
                       if( tokenVal == COMMA || tokenVal == RBLOCK )
                       {
                           // keep going!
                       }
                       else
                       {
                           exit(1);
                       }
                   }
               }
               else
               {
                   exit(1);
               }
           }

       }

       if( tokenVal == EOF_FLEX )
       {
           done = 1;
       }
   }

   printf("done parsing rcfile\n" );
}

void parse_params()
{
   param_t *param;
   param_t *itemparam;
   int i, t;
   int count;
   int width;

   count = 0;

   /* loop through all subparams and find the "pier" params */
   SUBPARAMS_FOREACH(i, param, &plugin_this->params)
   {
      printf(".\n");
      if (strcmp(param->name, "applet") != 0)
      {
         continue;
      }

      if (strcmp(param->value, "load" ) == 0)
      {
         SUBPARAMS_FOREACH(t, itemparam, &param->subparams)
         {
			if (strcmp(itemparam->name, "path") == 0)
            {
               sprintf( applet_info[count].soName, "%s/%s", applets_path, itemparam->value );
               printf("read path: %s\n", itemparam->value );
            }
            if (strcmp(itemparam->name, "name") == 0)
            {
               printf("read name: %s\n", itemparam->value );
               strcpy( applet_info[count].name, itemparam->value );

               // right now the order they appear in the load, is their running order
               strcpy( applet_order[count], itemparam->value );
               active_applet_count_g = count + 1;
            }
            if( strcmp(itemparam->name, "align") == 0)
            {
               if( strcmp( "left", itemparam->value ) == 0 )
               {
                  applet_info[count].justification = applet_left_align_c;
               }
               if( strcmp( "right", itemparam->value ) == 0 )
               {
                  applet_info[count].justification = applet_right_align_c;
               }
               if( strcmp( "fill", itemparam->value ) == 0 )
               {
                  applet_info[count].justification = applet_fill_align_c;
               }
            }
         }
         OPTIONAL_PARAM(&param->subparams, "width", int, width, 1);
         printf("Wdith is: %d\n", width );
         count++;
      }
   }

}

/* get parameters, init taskbar system */
int init()
{
   int icons;
   int at_top;

   /* get our parameters */
   REQUIRED_PARAM( &plugin_this->params, "applets_path", string, applets_path );

   OPTIONAL_PARAM(&plugin_this->params, "at_top", bool, at_top, 0);
   OPTIONAL_PARAM(&plugin_this->params, "icons",  bool, icons,  1);

   parse_params();

   /* set up the taskbar system */
   // taskbar_initialize( at_top, icons );
   // panel_init();

   taskbar_dgroup = &dgroup_empty;


   printf("-- fspanel init done ---\n");

   return PLUGIN_OK;
}

/* handle plugin shutdowns */
void shutdown() {

	/* clear up the stuff */
	// taskbar_delete( tb_global->tb );
//	XFreeGC( display, tb_global->drawgc );
//	free( tb_global );

	/* grid colors aren't yet free */
//fixme - what about tb_global->*_color

}

/*
 * x event handler for windows we deal with; just
 * call out to the appropriate handler.
 */
int xevent_handler( XEvent *ev )
{
   printf("-------xevent_handler-------------\n");
   switch (ev->type)
   {
      case ButtonRelease:
         handle_release ( ev->xbutton.button, ev->xbutton.x, ev->xbutton.y);
         break;
      case ButtonPress:
//                    if (ev->xbutton.button == 1)
//                    {
         handle_press( ev->xbutton.button,
                       ev->xbutton.x_root,
                       ev->xbutton.y_root,
                       ev->xbutton.x,
                       ev->xbutton.y );
//                    }
         break;
      case DestroyNotify:
         del_task (tb_g, ev->xdestroywindow.window);
         /* fall through */
      case Expose:
         gui_draw_taskbar (tb_g);
         break;
      case PropertyNotify:
         printf("xevent_handler::PropertyNotify\n");
         handle_propertynotify ( tb_g,
                                 ev->xproperty.window,
                                 ev->xproperty.atom );
         break;
      case FocusIn:
         handle_focusin ( tb_g, ev->xfocus.window );
         break;
         /*default:
           printf ("unknown evt type: %d\n", ev.type);*/
   }

   return PLUGIN_OK;
}

static int property_notify( int pcall, client_t *client)
{
   printf("-------------------- property_notify --------------------\n");
 //   if (client->workspace && !client->flags.internal)
//    {
//       taskbar_t *tb = tb_global->tb;
//       taskbar_client_propertynotify( tb, NULL, client );
//       taskbar_expose( tb, tb_global->drawgc, NULL);
//    }

   taskbar_read_clientlist( tb_g );

   return PLUGIN_OK;
}

int start()
{
	int xfd;
	char *home;
	char rcPath[200];

    home = getenv( "HOME" );
    if( home != NULL )
    {
         sprintf( rcPath, "%s/.fspanelrc", home );
    }
    else
    {
         sprintf( rcPath, ".fspanelrc" );
    }
    // golem
//    readRcFile( rcPath );
    // end golem

    // golem
   //  plugin_callback_add(plugin_this, PCALL_WINDOW_BIRTH, window_birth);
//	plugin_callback_add(plugin_this, PCALL_WINDOW_DEATH, window_death);
//	plugin_callback_add(plugin_this, PCALL_ICONIFY_NOTIFY, iconify_notify );
//	plugin_callback_add(plugin_this, PCALL_RESTORE_NOTIFY, restore_notify );
// //	plugin_callback_add(plugin_this, PCALL_FOCUS_CHANGE, focus_change);
//	plugin_callback_add(plugin_this, PCALL_DESKTOP_CHANGE, desktop_change);
//	plugin_callback_add(plugin_this, PCALL_WORKSPACE_CHANGE, workspace_change);
    plugin_callback_add(plugin_this, PCALL_PROPERTY_NOTIFY, property_notify );
    // end golem

    // golem
	// display = XOpenDisplay (NULL);
//	if (!display)
//     {
//		return 0;
//     }
    //end golem


	scr_screen = DefaultScreen (display);

    golem_screen = TAILQ_FIRST(&screen_list);

	scr_depth = DefaultDepth (display, scr_screen);


	scr_height = DisplayHeight (display, scr_screen);
	scr_width = DisplayWidth (display, scr_screen);
	root_win = RootWindow (display, scr_screen);

	/* helps us catch windows closing/opening */
    // golem

    //	XSelectInput (display, root_win, PropertyChangeMask);
    //	XSetErrorHandler ((XErrorHandler) handle_error);
    // end golem

	XInternAtoms( display, panel_atom_names, ATOM_COUNT, False, panel_atoms );


    // don't move this ...
    // init applets is hosing the other golem plugins ...
    init_applets();

	gui_init ();
	tb_g = gui_create_taskbar ();
	xfd = ConnectionNumber (display);

	gui_sync ();

    gui_draw_taskbar (tb_g);


    // golem

    // ok ... how do I get golem to call my property notify callback if
    // one of the root window properties changes ?

    plugin_setcontext( plugin_this, tb_g->win );

    return PLUGIN_OK;
    // end golem
}
