/*
 * This file is subject to the terms of the GFX License. If a copy of
 * the license was not distributed with this file, you can obtain one at:
 *
 *              http://ugfx.org/license.html
 */

/**
 * @file	src/gwin/gimage.c
 * @brief	GWIN sub-system image code.
 */

#include "gfx.h"

#if GFX_USE_GWIN && GWIN_NEED_IMAGE

#include "gwin/class_gwin.h"

#define widget(gh)	((GImageObject *)gh)

static void _destroy(GWindowObject *gh) {
	if (gdispImageIsOpen(&widget(gh)->image))
		gdispImageClose(&widget(gh)->image);
}

#if GWIN_NEED_IMAGE_ANIMATION
	static void _redraw(GHandle gh);

	static void _timer(void *gh) {
		// We need to re-test the visibility in case it has been made invisible since the last frame.
		if ((((GHandle)gh)->flags & GWIN_FLG_VISIBLE))
			_redraw((GHandle)gh);
	}
#endif

static void _redraw(GHandle gh) {
	coord_t		x, y, w, h, dx, dy;
	color_t		bg;
	#if GWIN_NEED_IMAGE_ANIMATION
		delaytime_t	delay;
	#endif

	// The default display area
	x = gh->x;
	y = gh->y;
	w = gh->width;
	h = gh->height;
	bg = gwinGetDefaultBgColor();

	// If the image isn't open just clear the area
	if (!gdispImageIsOpen(&widget(gh)->image)) {
		gdispFillArea(x, y, w, h, bg);
		return;
	}

	// Center horizontally if the area is larger than the image
	if (widget(gh)->image.width < w) {
		w = widget(gh)->image.width;
		dx = (gh->width-w)/2;
		x += dx;
		if (dx)
			gdispFillArea(gh->x, y, dx, h, bg);
		gdispFillArea(x+w, y, gh->width-dx-w, h, bg);
		dx = 0;
	}

	// Center image horizontally if the area is smaller than the image
	else if (widget(gh)->image.width > w) {
		dx = (widget(gh)->image.width - w)/2;
	}

	// Center vertically if the area is larger than the image
	if (widget(gh)->image.height < h) {
		h = widget(gh)->image.height;
		dy = (gh->height-h)/2;
		y += dy;
		if (dy)
			gdispFillArea(x, gh->y, w, dy, bg);
		gdispFillArea(x, y+h, w, gh->height-dy-h, bg);
		dy = 0;
	}

	// Center image vertically if the area is smaller than the image
	else if (widget(gh)->image.height > h) {
		dy = (widget(gh)->image.height - h)/2;
	}

	// Reset the background color in case it has changed
	gdispImageSetBgColor(&widget(gh)->image, bg);

	// Display the image
	gdispImageDraw(&widget(gh)->image, x, y, w, h, dx, dy);

	#if GWIN_NEED_IMAGE_ANIMATION
		// read the delay for the next frame
		delay = gdispImageNext(&widget((GHandle)gh)->image);

		// Wait for that delay if required
		switch(delay) {
		case TIME_INFINITE:
			// Everything is done
			break;
		case TIME_IMMEDIATE:
			// We can't allow a continuous loop here as it would lock the system up so we delay for the minimum period
			delay = 1;
			// Fall through
		default:
			// Start the timer to draw the next frame of the animation
			gtimerStart(&widget((GHandle)gh)->timer, _timer, (void*)gh, FALSE, delay);
			break;
		}
	#endif
}

static const gwinVMT imageVMT = {
	"Image",					// The class name
	sizeof(GImageObject),		// The object size
	_destroy,					// The destroy routine
	_redraw,					// The redraw routine
	0,							// The after-clear routine
};

GHandle gwinImageCreate(GImageObject *gobj, GWindowInit *pInit) {
	if (!(gobj = (GImageObject *)_gwindowCreate(&gobj->g, pInit, &imageVMT, 0)))
		return 0;

	// Ensure the gdispImageIsOpen() gives valid results
	gobj->image.type = 0;

	// Initialise the timer
	#if GWIN_NEED_IMAGE_ANIMATION
		gtimerInit(&gobj->timer);
	#endif
	
	gwinSetVisible((GHandle)gobj, pInit->show);

	return (GHandle)gobj;
}

bool_t gwinImageOpenMemory(GHandle gh, const void* memory) {
	if (gdispImageIsOpen(&widget(gh)->image))
		gdispImageClose(&widget(gh)->image);

	if (!gdispImageSetMemoryReader(&widget(gh)->image, memory))
		return FALSE;

	if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK)
		return FALSE;

	if ((gh->flags & GWIN_FLG_VISIBLE)) {
		// Setting the clip here shouldn't be necessary if the redraw doesn't overdraw
		//	but we put it in for safety anyway
		#if GDISP_NEED_CLIP
			gdispSetClip(gh->x, gh->y, gh->width, gh->height);
		#endif
		_redraw(gh);
	}

	return TRUE;
}

#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || defined(__DOXYGEN__)
bool_t gwinImageOpenFile(GHandle gh, const char* filename) {
	if (gdispImageIsOpen(&widget(gh)->image))
		gdispImageClose(&widget(gh)->image);

	if (!gdispImageSetFileReader(&widget(gh)->image, filename))
		return FALSE;

	if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK)
		return FALSE;

	if ((gh->flags & GWIN_FLG_VISIBLE)) {
		// Setting the clip here shouldn't be necessary if the redraw doesn't overdraw
		//	but we put it in for safety anyway
		#if GDISP_NEED_CLIP
			gdispSetClip(gh->x, gh->y, gh->width, gh->height);
		#endif
		_redraw(gh);
	}

	return TRUE;
}
#endif 

#if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__)
bool_t gwinImageOpenStream(GHandle gh, void *streamPtr) {
	if (gdispImageIsOpen(&widget(gh)->image))
		gdispImageClose(&widget(gh)->image);

	if (!gdispImageSetBaseFileStreamReader(&widget(gh)->image, streamPtr))
		return FALSE;

	if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK)
		return FALSE;

	if ((gh->flags & GWIN_FLG_VISIBLE)) {
		// Setting the clip here shouldn't be necessary if the redraw doesn't overdraw
		//	but we put it in for safety anyway
		#if GDISP_NEED_CLIP
			gdispSetClip(gh->x, gh->y, gh->width, gh->height);
		#endif
		_redraw(gh);
	}

	return TRUE;
}
#endif

gdispImageError gwinImageCache(GHandle gh) {
	return gdispImageCache(&widget(gh)->image);
}

#endif // GFX_USE_GWIN && GWIN_NEED_IMAGE
/** @} */
