/*
 * Original idea and implementation by Frederik Schueler <fr.schueler@netsurf.de>
 * Rewritten by Pascal Hofstee <daeron@windowmaker.org>
 * - Added options to set min/max values
 * - centralized drawing into one pain function
 */

#include "WINGsP.h"

typedef struct W_ProgressIndicator {
    W_Class	widgetClass;
    W_View	*view;
    
    int	value;
    int	minValue;
    int	maxValue;
    
    void	*clientData;
} ProgressIndicator;


#define DEFAULT_PROGRESS_INDICATOR_WIDTH	276
#define DEFAULT_PROGRESS_INDICATOR_HEIGHT	16

/* define if only the ticks within the progress region should be displayed */
#define SHOW_PROGRESS_TICKS_ONLY


static void didResizeProgressIndicator();


W_ViewDelegate _ProgressIndicatorDelegate = {
    NULL,
	NULL,
	didResizeProgressIndicator,
	NULL,
	NULL
};


static void destroyProgressIndicator(ProgressIndicator *pPtr);
static void paintProgressIndicator(ProgressIndicator *pPtr);
static void realizeProgressIndicator(ProgressIndicator *pPtr);
static void handleEvents(XEvent *event, void *data);

static void realizeObserver(void *self, WMNotification *not) {
    
    realizeProgressIndicator(self);
}



WMProgressIndicator*
    WMCreateProgressIndicator(WMWidget *parent)
{
    ProgressIndicator *pPtr;
    
    pPtr = wmalloc(sizeof(ProgressIndicator));
    memset(pPtr, 0, sizeof(ProgressIndicator));
    
    pPtr->widgetClass = WC_ProgressIndicator;
    
    pPtr->view = W_CreateView(W_VIEW(parent));
    if (!pPtr->view) {
	wfree(pPtr);
	return NULL;
    }
    
    pPtr->view->self = pPtr;
    
    pPtr->view->delegate = &_ProgressIndicatorDelegate;
    
    WMCreateEventHandler(pPtr->view, ExposureMask|StructureNotifyMask,
			 handleEvents, pPtr);
    
    
    W_ResizeView(pPtr->view, DEFAULT_PROGRESS_INDICATOR_WIDTH,
		 DEFAULT_PROGRESS_INDICATOR_HEIGHT);
    
    /* Initialize ProgressIndicator Values */
    pPtr->value = 0;
    pPtr->minValue = 0;
    pPtr->maxValue = 100;
    
    WMAddNotificationObserver(realizeObserver, pPtr,
			      WMViewRealizedNotification, pPtr->view);
    
    return pPtr;
}


void
WMSetProgressIndicatorMinValue(WMProgressIndicator *progressindicator, int value)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    progressindicator->minValue = value;
    if (progressindicator->value < value) {
	progressindicator->value = value;
	if (progressindicator->view->flags.mapped) {
	    paintProgressIndicator(progressindicator);
	}
    }
}


void
WMSetProgressIndicatorMaxValue(WMProgressIndicator *progressindicator, int value)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    progressindicator->maxValue = value;
    if (progressindicator->value > value) {
	progressindicator->value = value;
	if (progressindicator->view->flags.mapped) {
	    paintProgressIndicator(progressindicator);
	}
    }
}


void
WMSetProgressIndicatorValue(WMProgressIndicator *progressindicator, int value)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    progressindicator->value = value;
    
    /* Check if value is within min/max-range */
    if (progressindicator->minValue > value)
	progressindicator->value = progressindicator->minValue;
    
    if (progressindicator->maxValue < value)
	progressindicator->value = progressindicator->maxValue;
    
    
    if (progressindicator->view->flags.mapped) {
	paintProgressIndicator(progressindicator);
    }
}


int
WMGetProgressIndicatorMinValue(WMProgressIndicator *progressindicator)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    return progressindicator->minValue;
}


int
WMGetProgressIndicatorMaxValue(WMProgressIndicator *progressindicator)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    return progressindicator->maxValue;
}


int
WMGetProgressIndicatorValue(WMProgressIndicator *progressindicator)
{
    CHECK_CLASS(progressindicator, WC_ProgressIndicator);
    
    return progressindicator->value;
}


static void
realizeProgressIndicator(ProgressIndicator *pPtr)
{
    W_RealizeView(pPtr->view);
}


static void
didResizeProgressIndicator(W_ViewDelegate *self, WMView *view)
{
    WMProgressIndicator *pPtr = (WMProgressIndicator*)view->self;
    int width = pPtr->view->size.width;
    int height = pPtr->view->size.height;
    
    assert(width > 0);
    assert(height > 0);
}


static void
paintProgressIndicator(ProgressIndicator *pPtr)
{
    W_Screen *scr = pPtr->view->screen;
    GC bgc;
    GC wgc;
    GC lgc;
    GC dgc;
    WMSize size = pPtr->view->size;
    int perc, w, h;
    double unit, i;
    Pixmap buffer;
    
    bgc = WMColorGC(scr->black);
    wgc = WMColorGC(scr->white);
    lgc = WMColorGC(scr->gray);
    dgc = WMColorGC(scr->darkGray);
    
    unit = (double)(size.width - 3.0) / 100;
    
    buffer = XCreatePixmap(scr->display, pPtr->view->window, 
			   size.width, size.height, scr->depth);
    
    XFillRectangle(scr->display, buffer, lgc, 0, 0, size.width, size.height);
    
    /* Calculate size of Progress to draw and paint ticks*/
    perc = (pPtr->value - pPtr->minValue) * 100 / (pPtr->maxValue - pPtr->minValue);
    
    w = (int)((double)(perc * unit));
    h = size.height - 2;
    
    if (w > (size.width - 3))
	w = size.width - 3;
    
    if (w > 0) {
	XFillRectangle(scr->display, buffer, lgc, 2, 1, w, h);
	XFillRectangle(scr->display, buffer, scr->stippleGC, 2, 1, w, h);
	W_DrawRelief(scr, buffer, 2, 1, w, h, WRFlat);
	
	/* Draw Progress Marks */
	i=(5.0*unit);
	
#ifdef SHOW_PROGRESS_TICKS_ONLY
	while((int)i<w+5) {
#else
	while ((int)i < (size.width - 3)) {
#endif
	    XDrawLine(scr->display, buffer, dgc, (int)i+2, h-1, i+2, h-3);
	    
	    i+=(5.0*unit);
	    
#ifdef SHOW_PROGRESS_TICKS_ONLY
	    if((int)i>=w)
		break;
#endif
	    
	    XDrawLine(scr->display, buffer, dgc, (int)i+2, h-1, i+2, h-6);
	    
	    i+=(5.0*unit);
	}
    }
    
    XDrawLine(scr->display, buffer, bgc, w+2, 1, w+2, h+1);
    XDrawLine(scr->display, buffer, lgc, 2, h, w+2, h);
    
    
    XDrawLine(scr->display, buffer, dgc, 0, 0, 0, size.height-1);
    XDrawLine(scr->display, buffer, dgc, 0, 0, size.width, 0);
    XDrawLine(scr->display, buffer, bgc, 1, 1, 1, size.height-1);
    XDrawLine(scr->display, buffer, bgc, 1, 1, size.width-1, 1);
    
    XDrawLine(scr->display, buffer, wgc, size.width-1, 0,
	      size.width-1, size.height-1);
    XDrawLine(scr->display, buffer, wgc, 0, size.height-1,
	      size.width-1, size.height-1);
    
    XCopyArea(scr->display, buffer, pPtr->view->window, scr->copyGC, 0, 0,
	      size.width, size.height, 0, 0);
    
    XFreePixmap(scr->display, buffer);
}

static void
handleEvents(XEvent *event, void *data)
{
    ProgressIndicator *pPtr = (ProgressIndicator*)data;
    
    CHECK_CLASS(data, WC_ProgressIndicator);
    
    switch (event->type) {
     case Expose:
	if (event->xexpose.count!=0)
	    break;
	paintProgressIndicator(pPtr);
	break;
     case DestroyNotify:
	destroyProgressIndicator(pPtr);
	break;
    }
}


static void
destroyProgressIndicator(ProgressIndicator *pPtr)
{
    WMRemoveNotificationObserver(pPtr);
    
    wfree(pPtr);
}

