/******************************************************************************
 *                                                                            *
 * Copyright (C) 2006-2009 by Tor Andersson.                                  *
 * Copyright (C) 2010 by Ben Cressey.                                         *
 *                                                                            *
 * This file is part of Gargoyle.                                             *
 *                                                                            *
 * Gargoyle is free software; you can redistribute it and/or modify           *
 * it under the terms of the GNU General Public License as published by       *
 * the Free Software Foundation; either version 2 of the License, or          *
 * (at your option) any later version.                                        *
 *                                                                            *
 * Gargoyle is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU General Public License for more details.                               *
 *                                                                            *
 * You should have received a copy of the GNU General Public License          *
 * along with Gargoyle; if not, write to the Free Software                    *
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA *
 *                                                                            *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "glk.h"
#include "garglk.h"

#define mul255(a,b) (((a) * ((b) + 1)) >> 8)

static void
drawpicture(picture_t *src, window_graphics_t *dst, 
		int x0, int y0, int width, int height, glui32 linkval);

void win_graphics_touch(window_graphics_t *dest)
{
	dest->dirty = 1;
	winrepaint(
			dest->owner->bbox.x0,
			dest->owner->bbox.y0,
			dest->owner->bbox.x1,
			dest->owner->bbox.y1);
}

window_graphics_t *win_graphics_create(window_t *win)
{
	window_graphics_t *res;

	if (!gli_conf_graphics)
		return NULL;

	res = malloc(sizeof(window_graphics_t));
	if (!res)
		return NULL;

	res->owner = win;
	res->bgnd[0] = win->bgcolor[0];
	res->bgnd[1] = win->bgcolor[1];
	res->bgnd[2] = win->bgcolor[2];

	res->w = 0;
	res->h = 0;
	res->rgb = NULL;

	res->dirty = 0;

	return res;
}

void win_graphics_destroy(window_graphics_t *dwin)
{
	dwin->owner = NULL;
	if (dwin->rgb)
		free(dwin->rgb);
	free(dwin);
}

void win_graphics_rearrange(window_t *win, rect_t *box)
{
	window_graphics_t *dwin = win->data;
	int newwid, newhgt;
	int bothwid, bothhgt;
	int oldw, oldh;
	unsigned char *newrgb;
	int y;

	win->bbox = *box;

	newwid = box->x1 - box->x0;
	newhgt = box->y1 - box->y0;
	oldw = dwin->w;
	oldh = dwin->h;

	if (newwid <= 0 || newhgt <= 0)
	{
		dwin->w = 0;
		dwin->h = 0;
		if (dwin->rgb)
			free(dwin->rgb);
		dwin->rgb = NULL;
		return;
	}

	bothwid = dwin->w;
	if (newwid < bothwid)
		bothwid = newwid;
	bothhgt = dwin->h;
	if (newhgt < bothhgt)
		bothhgt = newhgt;

	newrgb = malloc(newwid * newhgt * 3);

	if (dwin->rgb && bothwid && bothhgt)
	{
		for (y = 0; y < bothhgt; y++)
			memcpy(newrgb + y * newwid * 3,
					dwin->rgb + y * oldw * 3,
					bothwid * 3);
	}

	if (dwin->rgb)
	{
		free(dwin->rgb);
		dwin->rgb = 0;
	}

	dwin->rgb = newrgb;
	dwin->w = newwid;
	dwin->h = newhgt;

	if (newwid > oldw)
		win_graphics_erase_rect(dwin, FALSE, oldw, 0, newwid-oldw, newhgt);
	if (newhgt > oldh)
		win_graphics_erase_rect(dwin, FALSE, 0, oldh, newwid, newhgt-oldh);

	win_graphics_touch(dwin);
}

void win_graphics_get_size(window_t *win, glui32 *width, glui32 *height)
{
	window_graphics_t *dwin = win->data;
	*width = dwin->w;
	*height = dwin->h;
}

void win_graphics_redraw(window_t *win)
{
	window_graphics_t *dwin = win->data;
	int x, y;

	int x0 = win->bbox.x0;
	int y0 = win->bbox.y0;

	if (dwin->dirty || gli_force_redraw)
	{
		dwin->dirty = 0;

		if (!dwin->rgb)
			return;

		for (y = 0; y < dwin->h; y++)
			for (x = 0; x < dwin->w; x++)
			{
				gli_draw_pixel(x + x0, y + y0, 0xff,
						dwin->rgb + (y * dwin->w + x) * 3);
			}
	}
}

void win_graphics_click(window_graphics_t *dwin, int sx, int sy)
{
	window_t *win = dwin->owner;
	int x = sx - win->bbox.x0;
	int y = sy - win->bbox.y0;

	if (win->mouse_request) {
		gli_event_store(evtype_MouseInput, win, x, y);
		win->mouse_request = FALSE;
		if (gli_conf_safeclicks)
			gli_forceclick = 1;
	}

	if (win->hyper_request) {
		glui32 linkval = gli_get_hyperlink(sx, sy);
		if (linkval) {
			gli_event_store(evtype_Hyperlink, win, linkval, 0);
			win->hyper_request = FALSE;
			if (gli_conf_safeclicks)
				gli_forceclick = 1;
		}
	}
}

glui32 win_graphics_draw_picture(window_graphics_t *dwin,
		glui32 image, 
		glsi32 xpos, glsi32 ypos,
		int scale, glui32 imagewidth, glui32 imageheight)
{
	picture_t *pic = gli_picture_load(image);
	glui32 hyperlink = dwin->owner->attr.hyper;

	if (!pic) {
		return FALSE;
	}

	if (!dwin->owner->image_loaded)
	{
		gli_piclist_increment();
		dwin->owner->image_loaded = TRUE;
	}

	if (!scale) {
		imagewidth = pic->w;
		imageheight = pic->h;
	}

	drawpicture(pic, dwin, xpos, ypos, imagewidth, imageheight, hyperlink);

	win_graphics_touch(dwin);

	return TRUE;
}

void win_graphics_erase_rect(window_graphics_t *dwin, int whole,
		glsi32 x0, glsi32 y0, glui32 width, glui32 height)
{
	int x1 = x0 + width;
	int y1 = y0 + height;
	int x, y;
	int hx0, hx1, hy0, hy1;

	if (whole)
	{
		x0 = 0;
		y0 = 0;
		x1 = dwin->w;
		y1 = dwin->h;
	}

	if (x0 < 0) x0 = 0;
	if (y0 < 0) y0 = 0;
	if (x1 < 0) x1 = 0;
	if (y1 < 0) y1 = 0;
	if (x0 >= dwin->w) x0 = dwin->w;
	if (y0 >= dwin->h) y0 = dwin->h;
	if (x1 >= dwin->w) x1 = dwin->w;
	if (y1 >= dwin->h) y1 = dwin->h;

	hx0 = dwin->owner->bbox.x0 + x0;
	hx1 = dwin->owner->bbox.x0 + x1;
	hy0 = dwin->owner->bbox.y0 + y0;
	hy1 = dwin->owner->bbox.y0 + y1;

	/* zero out hyperlinks for these coordinates */
	gli_put_hyperlink(0, hx0, hy0, hx1, hy1);

	for (y = y0; y < y1; y++)
	{
		unsigned char *p = dwin->rgb + (y * dwin->w + x0) * 3;
		for (x = x0; x < x1; x++)
		{
			*p++ = dwin->bgnd[0];
			*p++ = dwin->bgnd[1];
			*p++ = dwin->bgnd[2];
		}
	}

	win_graphics_touch(dwin);
}

void win_graphics_fill_rect(window_graphics_t *dwin, glui32 color,
		glsi32 x0, glsi32 y0, glui32 width, glui32 height)
{
	unsigned char col[3];
	int x1 = x0 + width;
	int y1 = y0 + height;
	int x, y;
	int hx0, hx1, hy0, hy1;

	col[0] = (color >> 16) & 0xff;
	col[1] = (color >> 8) & 0xff;
	col[2] = (color >> 0) & 0xff;

	if (x0 < 0) x0 = 0;
	if (y0 < 0) y0 = 0;
	if (x1 < 0) x1 = 0;
	if (y1 < 0) y1 = 0;
	if (x0 > dwin->w) x0 = dwin->w;
	if (y0 > dwin->h) y0 = dwin->h;
	if (x1 > dwin->w) x1 = dwin->w;
	if (y1 > dwin->h) y1 = dwin->h;

	hx0 = dwin->owner->bbox.x0 + x0;
	hx1 = dwin->owner->bbox.x0 + x1;
	hy0 = dwin->owner->bbox.y0 + y0;
	hy1 = dwin->owner->bbox.y0 + y1;

	/* zero out hyperlinks for these coordinates */
	gli_put_hyperlink(0, hx0, hy0, hx1, hy1);

	for (y = y0; y < y1; y++)
	{
		unsigned char *p = dwin->rgb + (y * dwin->w + x0) * 3;
		for (x = x0; x < x1; x++)
		{
			*p++ = col[0];
			*p++ = col[1];
			*p++ = col[2];
		}
	}

	win_graphics_touch(dwin);
}

void win_graphics_set_background_color(window_graphics_t *dwin, glui32 color)
{
	dwin->bgnd[0] = (color >> 16) & 0xff;
	dwin->bgnd[1] = (color >> 8) & 0xff;
	dwin->bgnd[2] = (color >> 0) & 0xff;
}

static void drawpicture(picture_t *src, window_graphics_t *dst, 
		int x0, int y0, int width, int height, glui32 linkval)
{
	unsigned char *sp, *dp;
	int dx1, dy1, x1, y1, sx0, sy0, sx1, sy1;
	int x, y, w, h;
	int hx0, hx1, hy0, hy1;

	if (width != src->w || height != src->h)
	{
		src = gli_picture_scale(src, width, height);
		if (!src)
			return;
	}

	sx0 = 0;
	sy0 = 0;
	sx1 = src->w;
	sy1 = src->h;
	dx1 = dst->w;
	dy1 = dst->h;

	x1 = x0 + src->w;
	y1 = y0 + src->h;

	if (x1 <= 0 || x0 >= dx1) return;
	if (y1 <= 0 || y0 >= dy1) return;
	if (x0 < 0) { sx0 -= x0; x0 = 0; }
	if (y0 < 0) { sy0 -= y0; y0 = 0; }
	if (x1 > dx1) { sx1 += dx1 - x1; x1 = dx1; }
	if (y1 > dy1) { sy1 += dy1 - y1; y1 = dy1; }

	hx0 = dst->owner->bbox.x0 + x0;
	hx1 = dst->owner->bbox.x0 + x1;
	hy0 = dst->owner->bbox.y0 + y0;
	hy1 = dst->owner->bbox.y0 + y1;

	/* zero out or set hyperlink for these coordinates */
	gli_put_hyperlink(linkval, hx0, hy0, hx1, hy1);

	sp = src->rgba + (sy0 * src->w + sx0) * 4;
	dp = dst->rgb + (y0 * dst->w + x0) * 3;

	w = sx1 - sx0;
	h = sy1 - sy0;

	for (y = 0; y < h; y++)
	{
		for (x = 0; x < w; x++)
		{
			unsigned char sa = sp[x*4+3];
			unsigned char na = 255 - sa;
			unsigned char sr = mul255(sp[x*4+0], sa);
			unsigned char sg = mul255(sp[x*4+1], sa);
			unsigned char sb = mul255(sp[x*4+2], sa);
			dp[x*3+0] = sr + mul255(dp[x*3+0], na);
			dp[x*3+1] = sg + mul255(dp[x*3+1], na);
			dp[x*3+2] = sb + mul255(dp[x*3+2], na);
		}
		sp += src->w * 4;
		dp += dst->w * 3;
	}

}

